From f07e24e11cd38da3839e756612479fe332e50931 Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Thu, 1 Sep 2022 12:44:09 -0500 Subject: [PATCH 01/70] partitionByColumn at the expression level --- .../expression_functions/pie_vis_function.ts | 80 +++++++++++++++++-- .../common/types/expression_renderers.ts | 1 + 2 files changed, 74 insertions(+), 7 deletions(-) diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts index 5b69fbc6194fd..1e39abb3f765b 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts @@ -9,6 +9,7 @@ import { Position } from '@elastic/charts'; import { prepareLogTable, validateAccessor } from '@kbn/visualizations-plugin/common/utils'; import { DEFAULT_LEGEND_SIZE, LegendSize } from '@kbn/visualizations-plugin/common/constants'; +import { Datatable, DatatableColumn, DatatableRow } from '@kbn/expressions-plugin/common'; import { EmptySizeRatios, LegendDisplay, PartitionVisParams } from '../types/expression_renderers'; import { ChartTypes, PieVisExpressionFunctionDefinition } from '../types'; import { @@ -19,6 +20,51 @@ import { } from '../constants'; import { errors, strings } from './i18n'; +const transposeTable = ( + table: Datatable +): { + table: Datatable; + metricAccessor: string; + bucketAccessor: string; +} => { + const oldRow = table.rows[0]; // only supports single row tables for now + + const nameColumnId = 'name'; + const valueColumnId = 'value'; + + const transposedRows: DatatableRow[] = table.columns.map((column) => ({ + [nameColumnId]: column.name, + [valueColumnId]: oldRow[column.id], + })); + + const transposedColumns: DatatableColumn[] = [ + { + id: nameColumnId, + name: nameColumnId, + meta: { + type: 'string', + }, + }, + { + id: valueColumnId, + name: valueColumnId, + meta: { + type: 'number', + }, + }, + ]; + + return { + metricAccessor: valueColumnId, + bucketAccessor: nameColumnId, + table: { + type: 'datatable', + columns: transposedColumns, + rows: transposedRows, + }, + }; +}; + export const pieVisFunction = (): PieVisExpressionFunctionDefinition => ({ name: PIE_VIS_EXPRESSION_NAME, type: 'render', @@ -28,13 +74,17 @@ export const pieVisFunction = (): PieVisExpressionFunctionDefinition => ({ metric: { types: ['vis_dimension', 'string'], help: strings.getMetricArgHelp(), - required: true, }, buckets: { types: ['vis_dimension', 'string'], help: strings.getBucketsArgHelp(), multi: true, }, + // TODO - revisit arg name (columnsAsSlices?) + partitionByColumn: { + types: ['boolean'], + help: 'whether or not to transpose the datatable before rendering the slices', + }, splitColumn: { types: ['vis_dimension', 'string'], help: strings.getSplitColumnArgHelp(), @@ -137,7 +187,9 @@ export const pieVisFunction = (): PieVisExpressionFunctionDefinition => ({ throw new Error(errors.splitRowAndSplitColumnAreSpecifiedError()); } - validateAccessor(args.metric, context.columns); + if (args.metric) { + validateAccessor(args.metric, context.columns); + } if (args.buckets) { args.buckets.forEach((bucket) => validateAccessor(bucket, context.columns)); } @@ -148,6 +200,16 @@ export const pieVisFunction = (): PieVisExpressionFunctionDefinition => ({ args.splitRow.forEach((splitRow) => validateAccessor(splitRow, context.columns)); } + let transposedTable; + let transposedMetricAccessor; + let transposedBucketAccessor; + if (args.partitionByColumn) { + const result = transposeTable(context); + transposedTable = result.table; + transposedMetricAccessor = result.metricAccessor; + transposedBucketAccessor = result.bucketAccessor; + } + const visConfig: PartitionVisParams = { ...args, ariaLabel: @@ -156,13 +218,14 @@ export const pieVisFunction = (): PieVisExpressionFunctionDefinition => ({ handlers.getExecutionContext?.()?.description, palette: args.palette, dimensions: { - metric: args.metric, - buckets: args.buckets, + metric: transposedMetricAccessor ?? args.metric, + buckets: transposedBucketAccessor ? [transposedBucketAccessor] : args.buckets, splitColumn: args.splitColumn, splitRow: args.splitRow, }, }; + // TODO fix inspector if (handlers?.inspectorAdapters?.tables) { handlers.inspectorAdapters.tables.reset(); handlers.inspectorAdapters.tables.allowCsvExport = true; @@ -170,8 +233,11 @@ export const pieVisFunction = (): PieVisExpressionFunctionDefinition => ({ const logTable = prepareLogTable( context, [ - [[args.metric], strings.getSliceSizeHelp()], - [args.buckets, strings.getSliceHelp()], + [[transposedMetricAccessor ?? args.metric], strings.getSliceSizeHelp()], + [ + transposedBucketAccessor ? [transposedBucketAccessor] : args.buckets, + strings.getSliceHelp(), + ], [args.splitColumn, strings.getColumnSplitHelp()], [args.splitRow, strings.getRowSplitHelp()], ], @@ -184,7 +250,7 @@ export const pieVisFunction = (): PieVisExpressionFunctionDefinition => ({ type: 'render', as: PARTITION_VIS_RENDERER_NAME, value: { - visData: context, + visData: transposedTable ?? context, visConfig, syncColors: handlers?.isSyncColorsEnabled?.() ?? false, visType: args.isDonut ? ChartTypes.DONUT : ChartTypes.PIE, diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/types/expression_renderers.ts b/src/plugins/chart_expressions/expression_partition_vis/common/types/expression_renderers.ts index 2f436de90e138..444d5c5ee3f98 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/types/expression_renderers.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/types/expression_renderers.ts @@ -80,6 +80,7 @@ export interface PartitionVisParams extends VisCommonParams { export interface PieVisConfig extends VisCommonConfig { buckets?: Array; + partitionByColumn?: boolean; isDonut: boolean; emptySizeRatio?: EmptySizeRatios; respectSourceOrder?: boolean; From 53bbcb4b95b419856666445e5eb593cae33b90e7 Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Tue, 6 Sep 2022 15:50:20 -0500 Subject: [PATCH 02/70] pie expression supports multiple metrics --- .../expression_functions/pie_vis_function.ts | 136 +++++++++++------- .../common/types/expression_renderers.ts | 3 +- 2 files changed, 85 insertions(+), 54 deletions(-) diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts index 1e39abb3f765b..b6d9cebef8f46 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts @@ -7,9 +7,14 @@ */ import { Position } from '@elastic/charts'; -import { prepareLogTable, validateAccessor } from '@kbn/visualizations-plugin/common/utils'; +import { + getColumnByAccessor, + prepareLogTable, + validateAccessor, +} from '@kbn/visualizations-plugin/common/utils'; import { DEFAULT_LEGEND_SIZE, LegendSize } from '@kbn/visualizations-plugin/common/constants'; import { Datatable, DatatableColumn, DatatableRow } from '@kbn/expressions-plugin/common'; +import { ExpressionValueVisDimension } from '@kbn/visualizations-plugin/common'; import { EmptySizeRatios, LegendDisplay, PartitionVisParams } from '../types/expression_renderers'; import { ChartTypes, PieVisExpressionFunctionDefinition } from '../types'; import { @@ -20,24 +25,58 @@ import { } from '../constants'; import { errors, strings } from './i18n'; -const transposeTable = ( - table: Datatable +export const collapseMetrics = ( + table: Datatable, + bucketAccessors: Array, + metricAccessors: Array ): { table: Datatable; - metricAccessor: string; - bucketAccessor: string; + metricAccessor: string | ExpressionValueVisDimension; + bucketAccessors: Array; } => { - const oldRow = table.rows[0]; // only supports single row tables for now + if (metricAccessors.length < 2) { + return { + table, + metricAccessor: metricAccessors[0], + bucketAccessors, + }; + } + + const bucketColumns = bucketAccessors + ?.map((accessor) => getColumnByAccessor(accessor, table.columns)) + .filter(Boolean) as DatatableColumn[]; + + const metricColumns = metricAccessors + ?.map((accessor) => getColumnByAccessor(accessor, table.columns)) + .filter(Boolean) as DatatableColumn[]; - const nameColumnId = 'name'; + const transposedRows: DatatableRow[] = []; + + const [priorBucketColumns, finalBucketColumn] = [ + bucketColumns.slice(0, bucketColumns.length - 1), + bucketColumns[bucketColumns.length - 1], + ]; + + const nameColumnId = 'category-name'; const valueColumnId = 'value'; - const transposedRows: DatatableRow[] = table.columns.map((column) => ({ - [nameColumnId]: column.name, - [valueColumnId]: oldRow[column.id], - })); + table.rows.forEach((row) => { + metricColumns.forEach((metricCol) => { + const newRow: DatatableRow = {}; + + priorBucketColumns.forEach(({ id }) => { + newRow[id] = row[id]; + }); + + newRow[nameColumnId] = `${row[finalBucketColumn.id]} - ${metricCol.name}`; + newRow[valueColumnId] = row[metricCol.id]; + + transposedRows.push(newRow); + }); + }); const transposedColumns: DatatableColumn[] = [ + ...priorBucketColumns, { id: nameColumnId, name: nameColumnId, @@ -56,7 +95,7 @@ const transposeTable = ( return { metricAccessor: valueColumnId, - bucketAccessor: nameColumnId, + bucketAccessors: [...priorBucketColumns.map(({ id }) => id), nameColumnId], table: { type: 'datatable', columns: transposedColumns, @@ -71,20 +110,16 @@ export const pieVisFunction = (): PieVisExpressionFunctionDefinition => ({ inputTypes: ['datatable'], help: strings.getPieVisFunctionName(), args: { - metric: { + metrics: { types: ['vis_dimension', 'string'], help: strings.getMetricArgHelp(), + multi: true, }, buckets: { types: ['vis_dimension', 'string'], help: strings.getBucketsArgHelp(), multi: true, }, - // TODO - revisit arg name (columnsAsSlices?) - partitionByColumn: { - types: ['boolean'], - help: 'whether or not to transpose the datatable before rendering the slices', - }, splitColumn: { types: ['vis_dimension', 'string'], help: strings.getSplitColumnArgHelp(), @@ -187,11 +222,10 @@ export const pieVisFunction = (): PieVisExpressionFunctionDefinition => ({ throw new Error(errors.splitRowAndSplitColumnAreSpecifiedError()); } - if (args.metric) { - validateAccessor(args.metric, context.columns); - } + args.metrics.forEach((accessor) => validateAccessor(accessor, context.columns)); + if (args.buckets) { - args.buckets.forEach((bucket) => validateAccessor(bucket, context.columns)); + args.buckets.forEach((accessor) => validateAccessor(accessor, context.columns)); } if (args.splitColumn) { args.splitColumn.forEach((splitColumn) => validateAccessor(splitColumn, context.columns)); @@ -200,15 +234,11 @@ export const pieVisFunction = (): PieVisExpressionFunctionDefinition => ({ args.splitRow.forEach((splitRow) => validateAccessor(splitRow, context.columns)); } - let transposedTable; - let transposedMetricAccessor; - let transposedBucketAccessor; - if (args.partitionByColumn) { - const result = transposeTable(context); - transposedTable = result.table; - transposedMetricAccessor = result.metricAccessor; - transposedBucketAccessor = result.bucketAccessor; - } + const { table, metricAccessor, bucketAccessors } = collapseMetrics( + context, + args.buckets ?? [], + args.metrics + ); const visConfig: PartitionVisParams = { ...args, @@ -218,39 +248,39 @@ export const pieVisFunction = (): PieVisExpressionFunctionDefinition => ({ handlers.getExecutionContext?.()?.description, palette: args.palette, dimensions: { - metric: transposedMetricAccessor ?? args.metric, - buckets: transposedBucketAccessor ? [transposedBucketAccessor] : args.buckets, + metric: metricAccessor, + buckets: bucketAccessors, splitColumn: args.splitColumn, splitRow: args.splitRow, }, }; - // TODO fix inspector - if (handlers?.inspectorAdapters?.tables) { - handlers.inspectorAdapters.tables.reset(); - handlers.inspectorAdapters.tables.allowCsvExport = true; + // // TODO fix inspector + // if (handlers?.inspectorAdapters?.tables) { + // handlers.inspectorAdapters.tables.reset(); + // handlers.inspectorAdapters.tables.allowCsvExport = true; - const logTable = prepareLogTable( - context, - [ - [[transposedMetricAccessor ?? args.metric], strings.getSliceSizeHelp()], - [ - transposedBucketAccessor ? [transposedBucketAccessor] : args.buckets, - strings.getSliceHelp(), - ], - [args.splitColumn, strings.getColumnSplitHelp()], - [args.splitRow, strings.getRowSplitHelp()], - ], - true - ); - handlers.inspectorAdapters.tables.logDatatable('default', logTable); - } + // const logTable = prepareLogTable( + // context, + // [ + // [[transposedMetricAccessor ?? args.metric], strings.getSliceSizeHelp()], + // [ + // transposedBucketAccessor ? [transposedBucketAccessor] : args.buckets, + // strings.getSliceHelp(), + // ], + // [args.splitColumn, strings.getColumnSplitHelp()], + // [args.splitRow, strings.getRowSplitHelp()], + // ], + // true + // ); + // handlers.inspectorAdapters.tables.logDatatable('default', logTable); + // } return { type: 'render', as: PARTITION_VIS_RENDERER_NAME, value: { - visData: transposedTable ?? context, + visData: table, visConfig, syncColors: handlers?.isSyncColorsEnabled?.() ?? false, visType: args.isDonut ? ChartTypes.DONUT : ChartTypes.PIE, diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/types/expression_renderers.ts b/src/plugins/chart_expressions/expression_partition_vis/common/types/expression_renderers.ts index 444d5c5ee3f98..26d0cc3e5152a 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/types/expression_renderers.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/types/expression_renderers.ts @@ -78,7 +78,8 @@ export interface PartitionVisParams extends VisCommonParams { nestedLegend?: boolean; } -export interface PieVisConfig extends VisCommonConfig { +export interface PieVisConfig extends Omit { + metrics: Array; buckets?: Array; partitionByColumn?: boolean; isDonut: boolean; From 4920044f07f7951800ff08f15223ccdc23c961e7 Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Wed, 7 Sep 2022 11:26:46 -0500 Subject: [PATCH 03/70] test metric collapsing --- .../pie_vis_function.test.ts.snap | 32 ++-- .../pie_vis_function.test.ts | 181 +++++++++++++++++- .../expression_functions/pie_vis_function.ts | 2 +- 3 files changed, 191 insertions(+), 24 deletions(-) diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/pie_vis_function.test.ts.snap b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/pie_vis_function.test.ts.snap index 0f64f4c0a4779..182bb98f98091 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/pie_vis_function.test.ts.snap +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/pie_vis_function.test.ts.snap @@ -114,14 +114,16 @@ Object { "legendPosition": "right", "legendSize": "small", "maxLegendLines": 2, - "metric": Object { - "accessor": 0, - "format": Object { - "id": "number", - "params": Object {}, + "metrics": Array [ + Object { + "accessor": 0, + "format": Object { + "id": "number", + "params": Object {}, + }, + "type": "vis_dimension", }, - "type": "vis_dimension", - }, + ], "nestedLegend": true, "palette": Object { "name": "kibana_palette", @@ -248,14 +250,16 @@ Object { "legendPosition": "right", "legendSize": "small", "maxLegendLines": 2, - "metric": Object { - "accessor": 0, - "format": Object { - "id": "number", - "params": Object {}, + "metrics": Array [ + Object { + "accessor": 0, + "format": Object { + "id": "number", + "params": Object {}, + }, + "type": "vis_dimension", }, - "type": "vis_dimension", - }, + ], "nestedLegend": true, "palette": Object { "name": "kibana_palette", diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.test.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.test.ts index 9a18a348be16f..fba48cb609d35 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.test.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.test.ts @@ -16,7 +16,7 @@ import { } from '../types/expression_renderers'; import { ExpressionValueVisDimension, LegendSize } from '@kbn/visualizations-plugin/common'; import { Datatable } from '@kbn/expressions-plugin/common/expression_types/specs'; -import { pieVisFunction } from './pie_vis_function'; +import { collapseMetrics, pieVisFunction } from './pie_vis_function'; import { PARTITION_LABELS_VALUE } from '../constants'; import { ExecutionContext } from '@kbn/expressions-plugin/common'; @@ -53,14 +53,16 @@ describe('interpreter/functions#pieVis', () => { truncate: 100, last_level: false, }, - metric: { - type: 'vis_dimension', - accessor: 0, - format: { - id: 'number', - params: {}, + metrics: [ + { + type: 'vis_dimension', + accessor: 0, + format: { + id: 'number', + params: {}, + }, }, - }, + ], buckets: [ { type: 'vis_dimension', @@ -122,7 +124,7 @@ describe('interpreter/functions#pieVis', () => { ).toThrowErrorMatchingSnapshot(); }); - it('logs correct datatable to inspector', async () => { + it.skip('logs correct datatable to inspector', async () => { let loggedTable: Datatable; const handlers = { inspectorAdapters: { @@ -141,3 +143,164 @@ describe('interpreter/functions#pieVis', () => { expect(loggedTable!).toMatchSnapshot(); }); }); + +describe('collapseMetrics', () => { + it('collapses multiple metrics into a single metric column', () => { + const table: Datatable = { + type: 'datatable', + columns: [ + { + id: '1', + name: 'bucket1', + meta: { + type: 'string', + }, + }, + { + id: '2', + name: 'bucket2', + meta: { + type: 'string', + }, + }, + { + id: '3', + name: 'metric1', + meta: { + type: 'number', + }, + }, + { + id: '4', + name: 'metric2', + meta: { + type: 'number', + }, + }, + ], + rows: [ + { '1': 'square', '2': 'red', '3': 1, '4': 2 }, + { '1': 'square', '2': 'blue', '3': 3, '4': 4 }, + { '1': 'circle', '2': 'red', '3': 5, '4': 6 }, + { '1': 'circle', '2': 'blue', '3': 7, '4': 8 }, + ], + }; + + const result = collapseMetrics(table, ['1', '2'], ['3', '4']); + expect(result.bucketAccessors).toEqual(['1', 'category-metric']); + expect(result.metricAccessor).toEqual('value'); + expect(result.table).toMatchInlineSnapshot(` + Object { + "columns": Array [ + Object { + "id": "1", + "meta": Object { + "type": "string", + }, + "name": "bucket1", + }, + Object { + "id": "category-metric", + "meta": Object { + "type": "string", + }, + "name": "category-metric", + }, + Object { + "id": "value", + "meta": Object { + "type": "number", + }, + "name": "value", + }, + ], + "rows": Array [ + Object { + "1": "square", + "category-metric": "red - metric1", + "value": 1, + }, + Object { + "1": "square", + "category-metric": "red - metric2", + "value": 2, + }, + Object { + "1": "square", + "category-metric": "blue - metric1", + "value": 3, + }, + Object { + "1": "square", + "category-metric": "blue - metric2", + "value": 4, + }, + Object { + "1": "circle", + "category-metric": "red - metric1", + "value": 5, + }, + Object { + "1": "circle", + "category-metric": "red - metric2", + "value": 6, + }, + Object { + "1": "circle", + "category-metric": "blue - metric1", + "value": 7, + }, + Object { + "1": "circle", + "category-metric": "blue - metric2", + "value": 8, + }, + ], + "type": "datatable", + } + `); + }); + + it('leaves single metric tables alone', () => { + const table: Datatable = { + type: 'datatable', + columns: [ + { + id: '1', + name: 'bucket1', + meta: { + type: 'string', + }, + }, + { + id: '2', + name: 'bucket2', + meta: { + type: 'string', + }, + }, + { + id: '3', + name: 'metric1', + meta: { + type: 'number', + }, + }, + ], + rows: [ + { '1': 'square', '2': 'red', '3': 1 }, + { '1': 'square', '2': 'blue', '3': 3 }, + { '1': 'circle', '2': 'red', '3': 5 }, + { '1': 'circle', '2': 'blue', '3': 7 }, + ], + }; + + const bucketAccessors = ['1', '2']; + const metricAccessors = ['3']; + const result = collapseMetrics(table, bucketAccessors, metricAccessors); + + expect(result.table).toEqual(table); + expect(result.bucketAccessors).toEqual(bucketAccessors); + expect(result.metricAccessor).toEqual(metricAccessors[0]); + }); +}); diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts index b6d9cebef8f46..c1a8a0394363b 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts @@ -57,7 +57,7 @@ export const collapseMetrics = ( bucketColumns[bucketColumns.length - 1], ]; - const nameColumnId = 'category-name'; + const nameColumnId = 'category-metric'; const valueColumnId = 'value'; table.rows.forEach((row) => { From ced6a239497f87c870f02d5332d6d5cfa559aa36 Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Wed, 7 Sep 2022 11:28:07 -0500 Subject: [PATCH 04/70] add defaulting to function signature --- .../common/expression_functions/pie_vis_function.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts index c1a8a0394363b..ea7039f9d44a1 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts @@ -27,7 +27,7 @@ import { errors, strings } from './i18n'; export const collapseMetrics = ( table: Datatable, - bucketAccessors: Array, + bucketAccessors: Array = [], metricAccessors: Array ): { table: Datatable; @@ -236,7 +236,7 @@ export const pieVisFunction = (): PieVisExpressionFunctionDefinition => ({ const { table, metricAccessor, bucketAccessors } = collapseMetrics( context, - args.buckets ?? [], + args.buckets, args.metrics ); From b4dab19842c113651ef7c2681cd29c273b87baa1 Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Wed, 7 Sep 2022 13:15:21 -0500 Subject: [PATCH 05/70] handle no bucket columns case --- .../pie_vis_function.test.ts | 109 ++++++++++++++++-- .../expression_functions/pie_vis_function.ts | 6 +- 2 files changed, 102 insertions(+), 13 deletions(-) diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.test.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.test.ts index fba48cb609d35..3f56e8c92452c 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.test.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.test.ts @@ -187,7 +187,7 @@ describe('collapseMetrics', () => { }; const result = collapseMetrics(table, ['1', '2'], ['3', '4']); - expect(result.bucketAccessors).toEqual(['1', 'category-metric']); + expect(result.bucketAccessors).toEqual(['1', 'metric-name']); expect(result.metricAccessor).toEqual('value'); expect(result.table).toMatchInlineSnapshot(` Object { @@ -200,11 +200,11 @@ describe('collapseMetrics', () => { "name": "bucket1", }, Object { - "id": "category-metric", + "id": "metric-name", "meta": Object { "type": "string", }, - "name": "category-metric", + "name": "metric-name", }, Object { "id": "value", @@ -217,42 +217,42 @@ describe('collapseMetrics', () => { "rows": Array [ Object { "1": "square", - "category-metric": "red - metric1", + "metric-name": "red - metric1", "value": 1, }, Object { "1": "square", - "category-metric": "red - metric2", + "metric-name": "red - metric2", "value": 2, }, Object { "1": "square", - "category-metric": "blue - metric1", + "metric-name": "blue - metric1", "value": 3, }, Object { "1": "square", - "category-metric": "blue - metric2", + "metric-name": "blue - metric2", "value": 4, }, Object { "1": "circle", - "category-metric": "red - metric1", + "metric-name": "red - metric1", "value": 5, }, Object { "1": "circle", - "category-metric": "red - metric2", + "metric-name": "red - metric2", "value": 6, }, Object { "1": "circle", - "category-metric": "blue - metric1", + "metric-name": "blue - metric1", "value": 7, }, Object { "1": "circle", - "category-metric": "blue - metric2", + "metric-name": "blue - metric2", "value": 8, }, ], @@ -303,4 +303,91 @@ describe('collapseMetrics', () => { expect(result.bucketAccessors).toEqual(bucketAccessors); expect(result.metricAccessor).toEqual(metricAccessors[0]); }); + + it('does not blow up when there are no bucket accessors', () => { + const table: Datatable = { + type: 'datatable', + columns: [ + { + id: '3', + name: 'metric1', + meta: { + type: 'number', + }, + }, + { + id: '4', + name: 'metric2', + meta: { + type: 'number', + }, + }, + ], + rows: [ + { '3': 1, '4': 2 }, + { '3': 3, '4': 4 }, + { '3': 5, '4': 6 }, + { '3': 7, '4': 8 }, + ], + }; + + const result = collapseMetrics(table, undefined, ['3', '4']); + expect(result.bucketAccessors).toEqual(['metric-name']); + expect(result.metricAccessor).toEqual('value'); + expect(result.table).toMatchInlineSnapshot(` + Object { + "columns": Array [ + Object { + "id": "metric-name", + "meta": Object { + "type": "string", + }, + "name": "metric-name", + }, + Object { + "id": "value", + "meta": Object { + "type": "number", + }, + "name": "value", + }, + ], + "rows": Array [ + Object { + "metric-name": "metric1", + "value": 1, + }, + Object { + "metric-name": "metric2", + "value": 2, + }, + Object { + "metric-name": "metric1", + "value": 3, + }, + Object { + "metric-name": "metric2", + "value": 4, + }, + Object { + "metric-name": "metric1", + "value": 5, + }, + Object { + "metric-name": "metric2", + "value": 6, + }, + Object { + "metric-name": "metric1", + "value": 7, + }, + Object { + "metric-name": "metric2", + "value": 8, + }, + ], + "type": "datatable", + } + `); + }); }); diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts index ea7039f9d44a1..76692ea3e89a4 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts @@ -57,7 +57,7 @@ export const collapseMetrics = ( bucketColumns[bucketColumns.length - 1], ]; - const nameColumnId = 'category-metric'; + const nameColumnId = 'metric-name'; const valueColumnId = 'value'; table.rows.forEach((row) => { @@ -68,7 +68,9 @@ export const collapseMetrics = ( newRow[id] = row[id]; }); - newRow[nameColumnId] = `${row[finalBucketColumn.id]} - ${metricCol.name}`; + newRow[nameColumnId] = finalBucketColumn + ? `${row[finalBucketColumn.id]} - ${metricCol.name}` + : metricCol.name; newRow[valueColumnId] = row[metricCol.id]; transposedRows.push(newRow); From 3ec7089603502158dbf2f0d2eed8ad75986f7344 Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Wed, 7 Sep 2022 14:41:45 -0500 Subject: [PATCH 06/70] integrate multiple metrics at the lens vis level --- x-pack/plugins/lens/common/types.ts | 2 +- .../partition/suggestions.test.ts | 24 +++++----- .../visualizations/partition/suggestions.ts | 16 +++---- .../visualizations/partition/to_expression.ts | 4 +- .../partition/visualization.test.ts | 4 +- .../partition/visualization.tsx | 44 ++++++++++++------- 6 files changed, 52 insertions(+), 42 deletions(-) diff --git a/x-pack/plugins/lens/common/types.ts b/x-pack/plugins/lens/common/types.ts index f187ff02d5db9..f51a3e80810ad 100644 --- a/x-pack/plugins/lens/common/types.ts +++ b/x-pack/plugins/lens/common/types.ts @@ -64,7 +64,7 @@ export enum EmptySizeRatios { export interface SharedPieLayerState { groups: string[]; - metric?: string; + metrics: string[]; numberDisplay: NumberDisplayType; categoryDisplay: CategoryDisplayType; legendDisplay: LegendDisplayType; diff --git a/x-pack/plugins/lens/public/visualizations/partition/suggestions.test.ts b/x-pack/plugins/lens/public/visualizations/partition/suggestions.test.ts index fc449113adfa2..153f6212e9c42 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/suggestions.test.ts +++ b/x-pack/plugins/lens/public/visualizations/partition/suggestions.test.ts @@ -66,7 +66,7 @@ describe('suggestions', () => { layerId: 'first', layerType: layerTypes.DATA, groups: [], - metric: 'a', + metrics: ['a'], numberDisplay: NumberDisplay.HIDDEN, categoryDisplay: CategoryDisplay.DEFAULT, legendDisplay: LegendDisplay.DEFAULT, @@ -556,7 +556,7 @@ describe('suggestions', () => { layerId: 'first', layerType: layerTypes.DATA, groups: ['a'], - metric: 'b', + metrics: ['b'], numberDisplay: NumberDisplay.HIDDEN, categoryDisplay: CategoryDisplay.INSIDE, @@ -580,7 +580,7 @@ describe('suggestions', () => { layerId: 'first', layerType: layerTypes.DATA, groups: ['a'], - metric: 'b', + metrics: ['b'], numberDisplay: NumberDisplay.HIDDEN, categoryDisplay: CategoryDisplay.INSIDE, @@ -614,7 +614,7 @@ describe('suggestions', () => { layerId: 'first', layerType: layerTypes.DATA, groups: [], - metric: 'a', + metrics: ['a'], numberDisplay: NumberDisplay.HIDDEN, categoryDisplay: CategoryDisplay.DEFAULT, @@ -664,7 +664,7 @@ describe('suggestions', () => { layerId: 'first', layerType: layerTypes.DATA, groups: ['a', 'b'], - metric: 'e', + metrics: ['e'], numberDisplay: NumberDisplay.VALUE, categoryDisplay: CategoryDisplay.DEFAULT, legendDisplay: LegendDisplay.DEFAULT, @@ -713,7 +713,7 @@ describe('suggestions', () => { layerId: 'first', layerType: layerTypes.DATA, groups: ['a', 'b'], - metric: 'e', + metrics: ['e'], numberDisplay: NumberDisplay.PERCENT, categoryDisplay: CategoryDisplay.DEFAULT, legendDisplay: LegendDisplay.DEFAULT, @@ -750,7 +750,7 @@ describe('suggestions', () => { layerId: 'first', layerType: layerTypes.DATA, groups: ['a'], - metric: 'b', + metrics: ['b'], numberDisplay: NumberDisplay.HIDDEN, categoryDisplay: CategoryDisplay.INSIDE, @@ -773,7 +773,7 @@ describe('suggestions', () => { layerId: 'first', layerType: layerTypes.DATA, groups: ['a'], - metric: 'b', + metrics: ['b'], numberDisplay: NumberDisplay.HIDDEN, categoryDisplay: CategoryDisplay.DEFAULT, // This is changed @@ -807,7 +807,7 @@ describe('suggestions', () => { layerId: 'first', layerType: layerTypes.DATA, groups: [], - metric: 'a', + metrics: ['a'], numberDisplay: NumberDisplay.HIDDEN, categoryDisplay: CategoryDisplay.DEFAULT, @@ -849,7 +849,7 @@ describe('suggestions', () => { layerId: 'first', layerType: layerTypes.DATA, groups: ['a', 'b'], - metric: 'c', + metrics: ['c'], numberDisplay: NumberDisplay.HIDDEN, categoryDisplay: CategoryDisplay.INSIDE, @@ -884,7 +884,7 @@ describe('suggestions', () => { layerId: 'first', layerType: layerTypes.DATA, groups: [], - metric: 'a', + metrics: ['a'], numberDisplay: NumberDisplay.HIDDEN, categoryDisplay: CategoryDisplay.DEFAULT, @@ -922,7 +922,7 @@ describe('suggestions', () => { layerId: 'first', layerType: layerTypes.DATA, groups: ['a', 'b'], - metric: 'c', + metrics: ['c'], numberDisplay: NumberDisplay.HIDDEN, categoryDisplay: CategoryDisplay.INSIDE, legendDisplay: LegendDisplay.SHOW, diff --git a/x-pack/plugins/lens/public/visualizations/partition/suggestions.ts b/x-pack/plugins/lens/public/visualizations/partition/suggestions.ts index f2541493be339..b479e9a276460 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/suggestions.ts +++ b/x-pack/plugins/lens/public/visualizations/partition/suggestions.ts @@ -132,13 +132,13 @@ export function suggestions({ ...state.layers[0], layerId: table.layerId, groups: groups.map((col) => col.columnId), - metric: metricColumnId, + metrics: metricColumnId ? [metricColumnId] : [], layerType: layerTypes.DATA, } : { layerId: table.layerId, groups: groups.map((col) => col.columnId), - metric: metricColumnId, + metrics: metricColumnId ? [metricColumnId] : [], numberDisplay: NumberDisplay.PERCENT, categoryDisplay: CategoryDisplay.DEFAULT, legendDisplay: LegendDisplay.DEFAULT, @@ -197,7 +197,7 @@ export function suggestions({ ...state.layers[0], layerId: table.layerId, groups: groups.map((col) => col.columnId), - metric: metricColumnId, + metrics: metricColumnId ? [metricColumnId] : [], categoryDisplay: state.layers[0].categoryDisplay === CategoryDisplay.INSIDE ? CategoryDisplay.DEFAULT @@ -207,7 +207,7 @@ export function suggestions({ : { layerId: table.layerId, groups: groups.map((col) => col.columnId), - metric: metricColumnId, + metrics: metricColumnId ? [metricColumnId] : [], numberDisplay: NumberDisplay.PERCENT, categoryDisplay: CategoryDisplay.DEFAULT, legendDisplay: LegendDisplay.DEFAULT, @@ -244,14 +244,14 @@ export function suggestions({ ...state.layers[0], layerId: table.layerId, groups: groups.map((col) => col.columnId), - metric: metricColumnId, + metrics: metricColumnId ? [metricColumnId] : [], categoryDisplay: CategoryDisplay.DEFAULT, layerType: layerTypes.DATA, } : { layerId: table.layerId, groups: groups.map((col) => col.columnId), - metric: metricColumnId, + metrics: metricColumnId ? [metricColumnId] : [], numberDisplay: NumberDisplay.PERCENT, categoryDisplay: CategoryDisplay.DEFAULT, legendDisplay: LegendDisplay.DEFAULT, @@ -283,14 +283,14 @@ export function suggestions({ ...state.layers[0], layerId: table.layerId, groups: groups.map((col) => col.columnId), - metric: metricColumnId, + metrics: metricColumnId ? [metricColumnId] : [], categoryDisplay: CategoryDisplay.DEFAULT, layerType: layerTypes.DATA, } : { layerId: table.layerId, groups: groups.map((col) => col.columnId), - metric: metricColumnId, + metrics: metricColumnId ? [metricColumnId] : [], numberDisplay: NumberDisplay.PERCENT, categoryDisplay: CategoryDisplay.DEFAULT, legendDisplay: LegendDisplay.DEFAULT, diff --git a/x-pack/plugins/lens/public/visualizations/partition/to_expression.ts b/x-pack/plugins/lens/public/visualizations/partition/to_expression.ts index 5439f267c43ba..f8d9db782a715 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/to_expression.ts +++ b/x-pack/plugins/lens/public/visualizations/partition/to_expression.ts @@ -141,7 +141,7 @@ const generateCommonArguments: GenerateExpressionAstArguments = ( return { labels: generateCommonLabelsAstArgs(state, attributes, layer), buckets: operations.map((o) => o.columnId).map(prepareDimension), - metric: layer.metric ? [prepareDimension(layer.metric)] : [], + metrics: layer.metrics.map(prepareDimension), legendDisplay: [attributes.isPreview ? LegendDisplay.HIDE : layer.legendDisplay], legendPosition: [layer.legendPosition || Position.Right], maxLegendLines: [layer.legendMaxLines ?? 1], @@ -271,7 +271,7 @@ function expressionHelper( })) .filter((o): o is { columnId: string; operation: Operation } => !!o.operation); - if (!layer.metric || !operations.length) { + if (!layer.metrics.length) { return null; } const visualizationAst = generateExprAst( diff --git a/x-pack/plugins/lens/public/visualizations/partition/visualization.test.ts b/x-pack/plugins/lens/public/visualizations/partition/visualization.test.ts index 1b46c6eefea58..6e003842a0b9e 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/visualization.test.ts +++ b/x-pack/plugins/lens/public/visualizations/partition/visualization.test.ts @@ -36,7 +36,7 @@ function getExampleState(): PieVisualizationState { layerId: LAYER_ID, layerType: layerTypes.DATA, groups: [], - metric: undefined, + metrics: [], numberDisplay: NumberDisplay.PERCENT, categoryDisplay: CategoryDisplay.DEFAULT, legendDisplay: LegendDisplay.DEFAULT, @@ -91,7 +91,7 @@ describe('pie_visualization', () => { categoryDisplay: CategoryDisplay.DEFAULT, legendDisplay: LegendDisplay.DEFAULT, nestedLegend: false, - metric: undefined, + metrics: [], }, ], shape: PieChartTypes.DONUT, diff --git a/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx b/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx index 460cddf90c034..e40ba0cb70ab8 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx @@ -31,7 +31,7 @@ function newLayerState(layerId: string): PieLayerState { return { layerId, groups: [], - metric: undefined, + metrics: [], numberDisplay: NumberDisplay.PERCENT, categoryDisplay: CategoryDisplay.DEFAULT, legendDisplay: LegendDisplay.DEFAULT, @@ -134,7 +134,7 @@ export const getPieVisualization = ({ const getSliceByGroup = (): VisualizationDimensionGroupConfig => { const baseProps = { - required: true, + required: false, groupId: 'groups', accessors: sortedColumns, enableDimensionEditor: true, @@ -184,8 +184,8 @@ export const getPieVisualization = ({ defaultMessage: 'Value', }), }, - accessors: layer.metric ? [{ columnId: layer.metric }] : [], - supportsMoreColumns: !layer.metric, + accessors: layer.metrics.map((columnId) => ({ columnId })), + supportsMoreColumns: true, filterOperations: numberMetricOperations, required: true, dataTestSubj: 'lnsPie_sizeByDimensionPanel', @@ -206,7 +206,7 @@ export const getPieVisualization = ({ if (groupId === 'groups') { return { ...l, groups: [...l.groups.filter((group) => group !== columnId), columnId] }; } - return { ...l, metric: columnId }; + return { ...l, metrics: [...l.metrics.filter((metric) => metric !== columnId), columnId] }; }), }; }, @@ -218,10 +218,11 @@ export const getPieVisualization = ({ return l; } - if (l.metric === columnId) { - return { ...l, metric: undefined }; - } - return { ...l, groups: l.groups.filter((c) => c !== columnId) }; + return { + ...l, + groups: l.groups.filter((c) => c !== columnId), + metrics: l.metrics.filter((c) => c !== columnId), + }; }), }; }, @@ -275,13 +276,13 @@ export const getPieVisualization = ({ const warningMessages = []; for (const layer of state.layers) { - const { layerId, metric } = layer; + const { layerId, metrics } = layer; const rows = frame.activeData[layerId]?.rows; const numericColumn = frame.activeData[layerId]?.columns.find( ({ meta }) => meta?.type === 'number' ); - if (!rows || !metric) { + if (!rows || !metrics.length) { break; } @@ -299,17 +300,26 @@ export const getPieVisualization = ({ ); } - const columnToLabel = frame.datasourceLayers[layerId]?.getOperationForColumnId(metric)?.label; - const hasArrayValues = rows.some((row) => Array.isArray(row[metric])); - if (hasArrayValues) { + const metricsWithArrayValues = metrics + .map((metricColId) => { + if (rows.some((row) => Array.isArray(row[metricColId]))) { + return metricColId; + } + }) + .filter(Boolean) as string[]; + + if (metricsWithArrayValues.length) { + const labels = metricsWithArrayValues.map( + (colId) => frame.datasourceLayers[layerId]?.getOperationForColumnId(colId)?.label || colId + ); warningMessages.push( {columnToLabel || metric}, + label: {labels.join(', ')}, }} /> ); From b9e2a431f6c3f1e1a3fe2083f4ee1062189c739c Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Wed, 7 Sep 2022 14:50:09 -0500 Subject: [PATCH 07/70] collapseMetricColumns gets own module --- .../pie_vis_function.test.ts | 250 +---------------- .../expression_functions/pie_vis_function.ts | 92 +------ .../utils/collapse_metric_columns.test.ts | 258 ++++++++++++++++++ .../common/utils/collapse_metric_columns.ts | 94 +++++++ .../common/utils/index.ts | 9 + 5 files changed, 365 insertions(+), 338 deletions(-) create mode 100644 src/plugins/chart_expressions/expression_partition_vis/common/utils/collapse_metric_columns.test.ts create mode 100644 src/plugins/chart_expressions/expression_partition_vis/common/utils/collapse_metric_columns.ts create mode 100644 src/plugins/chart_expressions/expression_partition_vis/common/utils/index.ts diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.test.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.test.ts index 3f56e8c92452c..9f7e1c41b27b5 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.test.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.test.ts @@ -16,7 +16,7 @@ import { } from '../types/expression_renderers'; import { ExpressionValueVisDimension, LegendSize } from '@kbn/visualizations-plugin/common'; import { Datatable } from '@kbn/expressions-plugin/common/expression_types/specs'; -import { collapseMetrics, pieVisFunction } from './pie_vis_function'; +import { pieVisFunction } from './pie_vis_function'; import { PARTITION_LABELS_VALUE } from '../constants'; import { ExecutionContext } from '@kbn/expressions-plugin/common'; @@ -143,251 +143,3 @@ describe('interpreter/functions#pieVis', () => { expect(loggedTable!).toMatchSnapshot(); }); }); - -describe('collapseMetrics', () => { - it('collapses multiple metrics into a single metric column', () => { - const table: Datatable = { - type: 'datatable', - columns: [ - { - id: '1', - name: 'bucket1', - meta: { - type: 'string', - }, - }, - { - id: '2', - name: 'bucket2', - meta: { - type: 'string', - }, - }, - { - id: '3', - name: 'metric1', - meta: { - type: 'number', - }, - }, - { - id: '4', - name: 'metric2', - meta: { - type: 'number', - }, - }, - ], - rows: [ - { '1': 'square', '2': 'red', '3': 1, '4': 2 }, - { '1': 'square', '2': 'blue', '3': 3, '4': 4 }, - { '1': 'circle', '2': 'red', '3': 5, '4': 6 }, - { '1': 'circle', '2': 'blue', '3': 7, '4': 8 }, - ], - }; - - const result = collapseMetrics(table, ['1', '2'], ['3', '4']); - expect(result.bucketAccessors).toEqual(['1', 'metric-name']); - expect(result.metricAccessor).toEqual('value'); - expect(result.table).toMatchInlineSnapshot(` - Object { - "columns": Array [ - Object { - "id": "1", - "meta": Object { - "type": "string", - }, - "name": "bucket1", - }, - Object { - "id": "metric-name", - "meta": Object { - "type": "string", - }, - "name": "metric-name", - }, - Object { - "id": "value", - "meta": Object { - "type": "number", - }, - "name": "value", - }, - ], - "rows": Array [ - Object { - "1": "square", - "metric-name": "red - metric1", - "value": 1, - }, - Object { - "1": "square", - "metric-name": "red - metric2", - "value": 2, - }, - Object { - "1": "square", - "metric-name": "blue - metric1", - "value": 3, - }, - Object { - "1": "square", - "metric-name": "blue - metric2", - "value": 4, - }, - Object { - "1": "circle", - "metric-name": "red - metric1", - "value": 5, - }, - Object { - "1": "circle", - "metric-name": "red - metric2", - "value": 6, - }, - Object { - "1": "circle", - "metric-name": "blue - metric1", - "value": 7, - }, - Object { - "1": "circle", - "metric-name": "blue - metric2", - "value": 8, - }, - ], - "type": "datatable", - } - `); - }); - - it('leaves single metric tables alone', () => { - const table: Datatable = { - type: 'datatable', - columns: [ - { - id: '1', - name: 'bucket1', - meta: { - type: 'string', - }, - }, - { - id: '2', - name: 'bucket2', - meta: { - type: 'string', - }, - }, - { - id: '3', - name: 'metric1', - meta: { - type: 'number', - }, - }, - ], - rows: [ - { '1': 'square', '2': 'red', '3': 1 }, - { '1': 'square', '2': 'blue', '3': 3 }, - { '1': 'circle', '2': 'red', '3': 5 }, - { '1': 'circle', '2': 'blue', '3': 7 }, - ], - }; - - const bucketAccessors = ['1', '2']; - const metricAccessors = ['3']; - const result = collapseMetrics(table, bucketAccessors, metricAccessors); - - expect(result.table).toEqual(table); - expect(result.bucketAccessors).toEqual(bucketAccessors); - expect(result.metricAccessor).toEqual(metricAccessors[0]); - }); - - it('does not blow up when there are no bucket accessors', () => { - const table: Datatable = { - type: 'datatable', - columns: [ - { - id: '3', - name: 'metric1', - meta: { - type: 'number', - }, - }, - { - id: '4', - name: 'metric2', - meta: { - type: 'number', - }, - }, - ], - rows: [ - { '3': 1, '4': 2 }, - { '3': 3, '4': 4 }, - { '3': 5, '4': 6 }, - { '3': 7, '4': 8 }, - ], - }; - - const result = collapseMetrics(table, undefined, ['3', '4']); - expect(result.bucketAccessors).toEqual(['metric-name']); - expect(result.metricAccessor).toEqual('value'); - expect(result.table).toMatchInlineSnapshot(` - Object { - "columns": Array [ - Object { - "id": "metric-name", - "meta": Object { - "type": "string", - }, - "name": "metric-name", - }, - Object { - "id": "value", - "meta": Object { - "type": "number", - }, - "name": "value", - }, - ], - "rows": Array [ - Object { - "metric-name": "metric1", - "value": 1, - }, - Object { - "metric-name": "metric2", - "value": 2, - }, - Object { - "metric-name": "metric1", - "value": 3, - }, - Object { - "metric-name": "metric2", - "value": 4, - }, - Object { - "metric-name": "metric1", - "value": 5, - }, - Object { - "metric-name": "metric2", - "value": 6, - }, - Object { - "metric-name": "metric1", - "value": 7, - }, - Object { - "metric-name": "metric2", - "value": 8, - }, - ], - "type": "datatable", - } - `); - }); -}); diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts index 76692ea3e89a4..446eac621a3f5 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts @@ -7,14 +7,8 @@ */ import { Position } from '@elastic/charts'; -import { - getColumnByAccessor, - prepareLogTable, - validateAccessor, -} from '@kbn/visualizations-plugin/common/utils'; +import { prepareLogTable, validateAccessor } from '@kbn/visualizations-plugin/common/utils'; import { DEFAULT_LEGEND_SIZE, LegendSize } from '@kbn/visualizations-plugin/common/constants'; -import { Datatable, DatatableColumn, DatatableRow } from '@kbn/expressions-plugin/common'; -import { ExpressionValueVisDimension } from '@kbn/visualizations-plugin/common'; import { EmptySizeRatios, LegendDisplay, PartitionVisParams } from '../types/expression_renderers'; import { ChartTypes, PieVisExpressionFunctionDefinition } from '../types'; import { @@ -24,87 +18,7 @@ import { PARTITION_VIS_RENDERER_NAME, } from '../constants'; import { errors, strings } from './i18n'; - -export const collapseMetrics = ( - table: Datatable, - bucketAccessors: Array = [], - metricAccessors: Array -): { - table: Datatable; - metricAccessor: string | ExpressionValueVisDimension; - bucketAccessors: Array; -} => { - if (metricAccessors.length < 2) { - return { - table, - metricAccessor: metricAccessors[0], - bucketAccessors, - }; - } - - const bucketColumns = bucketAccessors - ?.map((accessor) => getColumnByAccessor(accessor, table.columns)) - .filter(Boolean) as DatatableColumn[]; - - const metricColumns = metricAccessors - ?.map((accessor) => getColumnByAccessor(accessor, table.columns)) - .filter(Boolean) as DatatableColumn[]; - - const transposedRows: DatatableRow[] = []; - - const [priorBucketColumns, finalBucketColumn] = [ - bucketColumns.slice(0, bucketColumns.length - 1), - bucketColumns[bucketColumns.length - 1], - ]; - - const nameColumnId = 'metric-name'; - const valueColumnId = 'value'; - - table.rows.forEach((row) => { - metricColumns.forEach((metricCol) => { - const newRow: DatatableRow = {}; - - priorBucketColumns.forEach(({ id }) => { - newRow[id] = row[id]; - }); - - newRow[nameColumnId] = finalBucketColumn - ? `${row[finalBucketColumn.id]} - ${metricCol.name}` - : metricCol.name; - newRow[valueColumnId] = row[metricCol.id]; - - transposedRows.push(newRow); - }); - }); - - const transposedColumns: DatatableColumn[] = [ - ...priorBucketColumns, - { - id: nameColumnId, - name: nameColumnId, - meta: { - type: 'string', - }, - }, - { - id: valueColumnId, - name: valueColumnId, - meta: { - type: 'number', - }, - }, - ]; - - return { - metricAccessor: valueColumnId, - bucketAccessors: [...priorBucketColumns.map(({ id }) => id), nameColumnId], - table: { - type: 'datatable', - columns: transposedColumns, - rows: transposedRows, - }, - }; -}; +import { collapseMetricColumns } from '../utils'; export const pieVisFunction = (): PieVisExpressionFunctionDefinition => ({ name: PIE_VIS_EXPRESSION_NAME, @@ -236,7 +150,7 @@ export const pieVisFunction = (): PieVisExpressionFunctionDefinition => ({ args.splitRow.forEach((splitRow) => validateAccessor(splitRow, context.columns)); } - const { table, metricAccessor, bucketAccessors } = collapseMetrics( + const { table, metricAccessor, bucketAccessors } = collapseMetricColumns( context, args.buckets, args.metrics diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/utils/collapse_metric_columns.test.ts b/src/plugins/chart_expressions/expression_partition_vis/common/utils/collapse_metric_columns.test.ts new file mode 100644 index 0000000000000..f95089b32052a --- /dev/null +++ b/src/plugins/chart_expressions/expression_partition_vis/common/utils/collapse_metric_columns.test.ts @@ -0,0 +1,258 @@ +/* + * 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 { Datatable } from '@kbn/expressions-plugin/common'; +import { collapseMetricColumns } from './collapse_metric_columns'; + +describe('collapseMetricColumns', () => { + it('collapses multiple metrics into a single metric column', () => { + const table: Datatable = { + type: 'datatable', + columns: [ + { + id: '1', + name: 'bucket1', + meta: { + type: 'string', + }, + }, + { + id: '2', + name: 'bucket2', + meta: { + type: 'string', + }, + }, + { + id: '3', + name: 'metric1', + meta: { + type: 'number', + }, + }, + { + id: '4', + name: 'metric2', + meta: { + type: 'number', + }, + }, + ], + rows: [ + { '1': 'square', '2': 'red', '3': 1, '4': 2 }, + { '1': 'square', '2': 'blue', '3': 3, '4': 4 }, + { '1': 'circle', '2': 'red', '3': 5, '4': 6 }, + { '1': 'circle', '2': 'blue', '3': 7, '4': 8 }, + ], + }; + + const result = collapseMetricColumns(table, ['1', '2'], ['3', '4']); + expect(result.bucketAccessors).toEqual(['1', 'metric-name']); + expect(result.metricAccessor).toEqual('value'); + expect(result.table).toMatchInlineSnapshot(` + Object { + "columns": Array [ + Object { + "id": "1", + "meta": Object { + "type": "string", + }, + "name": "bucket1", + }, + Object { + "id": "metric-name", + "meta": Object { + "type": "string", + }, + "name": "metric-name", + }, + Object { + "id": "value", + "meta": Object { + "type": "number", + }, + "name": "value", + }, + ], + "rows": Array [ + Object { + "1": "square", + "metric-name": "red - metric1", + "value": 1, + }, + Object { + "1": "square", + "metric-name": "red - metric2", + "value": 2, + }, + Object { + "1": "square", + "metric-name": "blue - metric1", + "value": 3, + }, + Object { + "1": "square", + "metric-name": "blue - metric2", + "value": 4, + }, + Object { + "1": "circle", + "metric-name": "red - metric1", + "value": 5, + }, + Object { + "1": "circle", + "metric-name": "red - metric2", + "value": 6, + }, + Object { + "1": "circle", + "metric-name": "blue - metric1", + "value": 7, + }, + Object { + "1": "circle", + "metric-name": "blue - metric2", + "value": 8, + }, + ], + "type": "datatable", + } + `); + }); + + it('leaves single metric tables alone', () => { + const table: Datatable = { + type: 'datatable', + columns: [ + { + id: '1', + name: 'bucket1', + meta: { + type: 'string', + }, + }, + { + id: '2', + name: 'bucket2', + meta: { + type: 'string', + }, + }, + { + id: '3', + name: 'metric1', + meta: { + type: 'number', + }, + }, + ], + rows: [ + { '1': 'square', '2': 'red', '3': 1 }, + { '1': 'square', '2': 'blue', '3': 3 }, + { '1': 'circle', '2': 'red', '3': 5 }, + { '1': 'circle', '2': 'blue', '3': 7 }, + ], + }; + + const bucketAccessors = ['1', '2']; + const metricAccessors = ['3']; + const result = collapseMetricColumns(table, bucketAccessors, metricAccessors); + + expect(result.table).toEqual(table); + expect(result.bucketAccessors).toEqual(bucketAccessors); + expect(result.metricAccessor).toEqual(metricAccessors[0]); + }); + + it('does not blow up when there are no bucket accessors', () => { + const table: Datatable = { + type: 'datatable', + columns: [ + { + id: '3', + name: 'metric1', + meta: { + type: 'number', + }, + }, + { + id: '4', + name: 'metric2', + meta: { + type: 'number', + }, + }, + ], + rows: [ + { '3': 1, '4': 2 }, + { '3': 3, '4': 4 }, + { '3': 5, '4': 6 }, + { '3': 7, '4': 8 }, + ], + }; + + const result = collapseMetricColumns(table, undefined, ['3', '4']); + expect(result.bucketAccessors).toEqual(['metric-name']); + expect(result.metricAccessor).toEqual('value'); + expect(result.table).toMatchInlineSnapshot(` + Object { + "columns": Array [ + Object { + "id": "metric-name", + "meta": Object { + "type": "string", + }, + "name": "metric-name", + }, + Object { + "id": "value", + "meta": Object { + "type": "number", + }, + "name": "value", + }, + ], + "rows": Array [ + Object { + "metric-name": "metric1", + "value": 1, + }, + Object { + "metric-name": "metric2", + "value": 2, + }, + Object { + "metric-name": "metric1", + "value": 3, + }, + Object { + "metric-name": "metric2", + "value": 4, + }, + Object { + "metric-name": "metric1", + "value": 5, + }, + Object { + "metric-name": "metric2", + "value": 6, + }, + Object { + "metric-name": "metric1", + "value": 7, + }, + Object { + "metric-name": "metric2", + "value": 8, + }, + ], + "type": "datatable", + } + `); + }); +}); diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/utils/collapse_metric_columns.ts b/src/plugins/chart_expressions/expression_partition_vis/common/utils/collapse_metric_columns.ts new file mode 100644 index 0000000000000..7cb0cbd181433 --- /dev/null +++ b/src/plugins/chart_expressions/expression_partition_vis/common/utils/collapse_metric_columns.ts @@ -0,0 +1,94 @@ +/* + * 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 { Datatable, DatatableColumn, DatatableRow } from '@kbn/expressions-plugin/common'; +import { + ExpressionValueVisDimension, + getColumnByAccessor, +} from '@kbn/visualizations-plugin/common'; + +export const collapseMetricColumns = ( + table: Datatable, + bucketAccessors: Array = [], + metricAccessors: Array +): { + table: Datatable; + metricAccessor: string | ExpressionValueVisDimension; + bucketAccessors: Array; +} => { + if (metricAccessors.length < 2) { + return { + table, + metricAccessor: metricAccessors[0], + bucketAccessors, + }; + } + + const bucketColumns = bucketAccessors + ?.map((accessor) => getColumnByAccessor(accessor, table.columns)) + .filter(Boolean) as DatatableColumn[]; + + const metricColumns = metricAccessors + ?.map((accessor) => getColumnByAccessor(accessor, table.columns)) + .filter(Boolean) as DatatableColumn[]; + + const transposedRows: DatatableRow[] = []; + + const [priorBucketColumns, finalBucketColumn] = [ + bucketColumns.slice(0, bucketColumns.length - 1), + bucketColumns[bucketColumns.length - 1], + ]; + + const nameColumnId = 'metric-name'; + const valueColumnId = 'value'; + + table.rows.forEach((row) => { + metricColumns.forEach((metricCol) => { + const newRow: DatatableRow = {}; + + priorBucketColumns.forEach(({ id }) => { + newRow[id] = row[id]; + }); + + newRow[nameColumnId] = finalBucketColumn + ? `${row[finalBucketColumn.id]} - ${metricCol.name}` + : metricCol.name; + newRow[valueColumnId] = row[metricCol.id]; + + transposedRows.push(newRow); + }); + }); + + const transposedColumns: DatatableColumn[] = [ + ...priorBucketColumns, + { + id: nameColumnId, + name: nameColumnId, + meta: { + type: 'string', + }, + }, + { + id: valueColumnId, + name: valueColumnId, + meta: { + type: 'number', + }, + }, + ]; + + return { + metricAccessor: valueColumnId, + bucketAccessors: [...priorBucketColumns.map(({ id }) => id), nameColumnId], + table: { + type: 'datatable', + columns: transposedColumns, + rows: transposedRows, + }, + }; +}; diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/utils/index.ts b/src/plugins/chart_expressions/expression_partition_vis/common/utils/index.ts new file mode 100644 index 0000000000000..c47f8fa385566 --- /dev/null +++ b/src/plugins/chart_expressions/expression_partition_vis/common/utils/index.ts @@ -0,0 +1,9 @@ +/* + * 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. + */ + +export * from './collapse_metric_columns'; From 6c65fd280fdf401e489a4f7d2867590f66fd7421 Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Wed, 7 Sep 2022 15:01:18 -0500 Subject: [PATCH 08/70] restore inspector --- .../expression_functions/pie_vis_function.ts | 34 ++++++++----------- .../common/utils/collapse_metric_columns.ts | 6 ++-- 2 files changed, 17 insertions(+), 23 deletions(-) diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts index 446eac621a3f5..30a58643c0481 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts @@ -171,26 +171,22 @@ export const pieVisFunction = (): PieVisExpressionFunctionDefinition => ({ }, }; - // // TODO fix inspector - // if (handlers?.inspectorAdapters?.tables) { - // handlers.inspectorAdapters.tables.reset(); - // handlers.inspectorAdapters.tables.allowCsvExport = true; + if (handlers?.inspectorAdapters?.tables) { + handlers.inspectorAdapters.tables.reset(); + handlers.inspectorAdapters.tables.allowCsvExport = true; - // const logTable = prepareLogTable( - // context, - // [ - // [[transposedMetricAccessor ?? args.metric], strings.getSliceSizeHelp()], - // [ - // transposedBucketAccessor ? [transposedBucketAccessor] : args.buckets, - // strings.getSliceHelp(), - // ], - // [args.splitColumn, strings.getColumnSplitHelp()], - // [args.splitRow, strings.getRowSplitHelp()], - // ], - // true - // ); - // handlers.inspectorAdapters.tables.logDatatable('default', logTable); - // } + const logTable = prepareLogTable( + table, + [ + [[metricAccessor], strings.getSliceSizeHelp()], + [bucketAccessors, strings.getSliceHelp()], + [args.splitColumn, strings.getColumnSplitHelp()], + [args.splitRow, strings.getRowSplitHelp()], + ], + true + ); + handlers.inspectorAdapters.tables.logDatatable('default', logTable); + } return { type: 'render', diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/utils/collapse_metric_columns.ts b/src/plugins/chart_expressions/expression_partition_vis/common/utils/collapse_metric_columns.ts index 7cb0cbd181433..0b20715794c2c 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/utils/collapse_metric_columns.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/utils/collapse_metric_columns.ts @@ -7,10 +7,8 @@ */ import { Datatable, DatatableColumn, DatatableRow } from '@kbn/expressions-plugin/common'; -import { - ExpressionValueVisDimension, - getColumnByAccessor, -} from '@kbn/visualizations-plugin/common'; +import { getColumnByAccessor } from '@kbn/visualizations-plugin/common/utils'; +import type { ExpressionValueVisDimension } from '@kbn/visualizations-plugin/common'; export const collapseMetricColumns = ( table: Datatable, From 90a7706b95704b4d49c40ad3b3f2fd3272b5e621 Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Wed, 7 Sep 2022 15:22:43 -0500 Subject: [PATCH 09/70] add metric collapse to other partition types --- .../mosaic_vis_function.test.ts.snap | 16 +++++++----- .../treemap_vis_function.test.ts.snap | 16 +++++++----- .../waffle_vis_function.test.ts.snap | 16 +++++++----- .../mosaic_vis_function.test.ts | 16 +++++++----- .../mosaic_vis_function.ts | 25 ++++++++++++------ .../treemap_vis_function.test.ts | 16 +++++++----- .../treemap_vis_function.ts | 25 ++++++++++++------ .../waffle_vis_function.test.ts | 16 +++++++----- .../waffle_vis_function.ts | 26 ++++++++++++------- .../common/types/expression_renderers.ts | 5 ++-- 10 files changed, 107 insertions(+), 70 deletions(-) diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/mosaic_vis_function.test.ts.snap b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/mosaic_vis_function.test.ts.snap index 859f644454169..acee7e4fa9262 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/mosaic_vis_function.test.ts.snap +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/mosaic_vis_function.test.ts.snap @@ -114,14 +114,16 @@ Object { "legendPosition": "right", "legendSize": "medium", "maxLegendLines": 2, - "metric": Object { - "accessor": 0, - "format": Object { - "id": "number", - "params": Object {}, + "metrics": Array [ + Object { + "accessor": 0, + "format": Object { + "id": "number", + "params": Object {}, + }, + "type": "vis_dimension", }, - "type": "vis_dimension", - }, + ], "nestedLegend": true, "palette": Object { "name": "kibana_palette", diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/treemap_vis_function.test.ts.snap b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/treemap_vis_function.test.ts.snap index ef1c7be526670..0cdca2f770dff 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/treemap_vis_function.test.ts.snap +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/treemap_vis_function.test.ts.snap @@ -114,14 +114,16 @@ Object { "legendPosition": "right", "legendSize": "medium", "maxLegendLines": 2, - "metric": Object { - "accessor": 0, - "format": Object { - "id": "number", - "params": Object {}, + "metrics": Array [ + Object { + "accessor": 0, + "format": Object { + "id": "number", + "params": Object {}, + }, + "type": "vis_dimension", }, - "type": "vis_dimension", - }, + ], "nestedLegend": true, "palette": Object { "name": "kibana_palette", diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/waffle_vis_function.test.ts.snap b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/waffle_vis_function.test.ts.snap index 9cdc69904460a..a2cfe26ae8a54 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/waffle_vis_function.test.ts.snap +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/waffle_vis_function.test.ts.snap @@ -88,14 +88,16 @@ Object { "legendPosition": "right", "legendSize": "medium", "maxLegendLines": 2, - "metric": Object { - "accessor": 0, - "format": Object { - "id": "number", - "params": Object {}, + "metrics": Array [ + Object { + "accessor": 0, + "format": Object { + "id": "number", + "params": Object {}, + }, + "type": "vis_dimension", }, - "type": "vis_dimension", - }, + ], "palette": Object { "name": "kibana_palette", "type": "system_palette", diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/mosaic_vis_function.test.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/mosaic_vis_function.test.ts index 46816ee1f34b8..5b56a8cfdafea 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/mosaic_vis_function.test.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/mosaic_vis_function.test.ts @@ -53,14 +53,16 @@ describe('interpreter/functions#mosaicVis', () => { truncate: 100, last_level: false, }, - metric: { - type: 'vis_dimension', - accessor: 0, - format: { - id: 'number', - params: {}, + metrics: [ + { + type: 'vis_dimension', + accessor: 0, + format: { + id: 'number', + params: {}, + }, }, - }, + ], buckets: [ { type: 'vis_dimension', diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/mosaic_vis_function.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/mosaic_vis_function.ts index ae3f17ff8df3a..c1f9f9c1a7641 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/mosaic_vis_function.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/mosaic_vis_function.ts @@ -18,6 +18,7 @@ import { MOSAIC_VIS_EXPRESSION_NAME, } from '../constants'; import { errors, strings } from './i18n'; +import { collapseMetricColumns } from '../utils'; export const mosaicVisFunction = (): MosaicVisExpressionFunctionDefinition => ({ name: MOSAIC_VIS_EXPRESSION_NAME, @@ -25,10 +26,11 @@ export const mosaicVisFunction = (): MosaicVisExpressionFunctionDefinition => ({ inputTypes: ['datatable'], help: strings.getPieVisFunctionName(), args: { - metric: { + metrics: { types: ['string', 'vis_dimension'], help: strings.getMetricArgHelp(), required: true, + multi: true, }, buckets: { types: ['string', 'vis_dimension'], @@ -117,7 +119,8 @@ export const mosaicVisFunction = (): MosaicVisExpressionFunctionDefinition => ({ throw new Error(errors.splitRowAndSplitColumnAreSpecifiedError()); } - validateAccessor(args.metric, context.columns); + args.metrics.forEach((accessor) => validateAccessor(accessor, context.columns)); + if (args.buckets) { args.buckets.forEach((bucket) => validateAccessor(bucket, context.columns)); } @@ -128,6 +131,12 @@ export const mosaicVisFunction = (): MosaicVisExpressionFunctionDefinition => ({ args.splitRow.forEach((splitRow) => validateAccessor(splitRow, context.columns)); } + const { table, metricAccessor, bucketAccessors } = collapseMetricColumns( + context, + args.buckets, + args.metrics + ); + const visConfig: PartitionVisParams = { ...args, ariaLabel: @@ -136,8 +145,8 @@ export const mosaicVisFunction = (): MosaicVisExpressionFunctionDefinition => ({ handlers.getExecutionContext?.()?.description, palette: args.palette, dimensions: { - metric: args.metric, - buckets: args.buckets, + metric: metricAccessor, + buckets: bucketAccessors, splitColumn: args.splitColumn, splitRow: args.splitRow, }, @@ -148,10 +157,10 @@ export const mosaicVisFunction = (): MosaicVisExpressionFunctionDefinition => ({ handlers.inspectorAdapters.tables.allowCsvExport = true; const logTable = prepareLogTable( - context, + table, [ - [[args.metric], strings.getSliceSizeHelp()], - [args.buckets, strings.getSliceHelp()], + [[metricAccessor], strings.getSliceSizeHelp()], + [bucketAccessors, strings.getSliceHelp()], [args.splitColumn, strings.getColumnSplitHelp()], [args.splitRow, strings.getRowSplitHelp()], ], @@ -164,7 +173,7 @@ export const mosaicVisFunction = (): MosaicVisExpressionFunctionDefinition => ({ type: 'render', as: PARTITION_VIS_RENDERER_NAME, value: { - visData: context, + visData: table, visConfig, syncColors: handlers?.isSyncColorsEnabled?.() ?? false, visType: ChartTypes.MOSAIC, diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/treemap_vis_function.test.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/treemap_vis_function.test.ts index 14aaea3a0cf5e..09b813ec805dd 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/treemap_vis_function.test.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/treemap_vis_function.test.ts @@ -53,14 +53,16 @@ describe('interpreter/functions#treemapVis', () => { truncate: 100, last_level: false, }, - metric: { - type: 'vis_dimension', - accessor: 0, - format: { - id: 'number', - params: {}, + metrics: [ + { + type: 'vis_dimension', + accessor: 0, + format: { + id: 'number', + params: {}, + }, }, - }, + ], buckets: [ { type: 'vis_dimension', diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/treemap_vis_function.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/treemap_vis_function.ts index 427179ca5a25a..38ca5d3ea96ea 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/treemap_vis_function.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/treemap_vis_function.ts @@ -18,6 +18,7 @@ import { TREEMAP_VIS_EXPRESSION_NAME, } from '../constants'; import { errors, strings } from './i18n'; +import { collapseMetricColumns } from '../utils'; export const treemapVisFunction = (): TreemapVisExpressionFunctionDefinition => ({ name: TREEMAP_VIS_EXPRESSION_NAME, @@ -25,10 +26,11 @@ export const treemapVisFunction = (): TreemapVisExpressionFunctionDefinition => inputTypes: ['datatable'], help: strings.getPieVisFunctionName(), args: { - metric: { + metrics: { types: ['vis_dimension'], help: strings.getMetricArgHelp(), required: true, + multi: true, }, buckets: { types: ['vis_dimension'], @@ -117,7 +119,8 @@ export const treemapVisFunction = (): TreemapVisExpressionFunctionDefinition => throw new Error(errors.splitRowAndSplitColumnAreSpecifiedError()); } - validateAccessor(args.metric, context.columns); + args.metrics.forEach((accessor) => validateAccessor(accessor, context.columns)); + if (args.buckets) { args.buckets.forEach((bucket) => validateAccessor(bucket, context.columns)); } @@ -128,6 +131,12 @@ export const treemapVisFunction = (): TreemapVisExpressionFunctionDefinition => args.splitRow.forEach((splitRow) => validateAccessor(splitRow, context.columns)); } + const { table, metricAccessor, bucketAccessors } = collapseMetricColumns( + context, + args.buckets, + args.metrics + ); + const visConfig: PartitionVisParams = { ...args, ariaLabel: @@ -136,8 +145,8 @@ export const treemapVisFunction = (): TreemapVisExpressionFunctionDefinition => handlers.getExecutionContext?.()?.description, palette: args.palette, dimensions: { - metric: args.metric, - buckets: args.buckets, + metric: metricAccessor, + buckets: bucketAccessors, splitColumn: args.splitColumn, splitRow: args.splitRow, }, @@ -148,10 +157,10 @@ export const treemapVisFunction = (): TreemapVisExpressionFunctionDefinition => handlers.inspectorAdapters.tables.allowCsvExport = true; const logTable = prepareLogTable( - context, + table, [ - [[args.metric], strings.getSliceSizeHelp()], - [args.buckets, strings.getSliceHelp()], + [[metricAccessor], strings.getSliceSizeHelp()], + [bucketAccessors, strings.getSliceHelp()], [args.splitColumn, strings.getColumnSplitHelp()], [args.splitRow, strings.getRowSplitHelp()], ], @@ -164,7 +173,7 @@ export const treemapVisFunction = (): TreemapVisExpressionFunctionDefinition => type: 'render', as: PARTITION_VIS_RENDERER_NAME, value: { - visData: context, + visData: table, visConfig, syncColors: handlers?.isSyncColorsEnabled?.() ?? false, visType: ChartTypes.TREEMAP, diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/waffle_vis_function.test.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/waffle_vis_function.test.ts index 608c40b501066..f36b492495a0c 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/waffle_vis_function.test.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/waffle_vis_function.test.ts @@ -53,14 +53,16 @@ describe('interpreter/functions#waffleVis', () => { truncate: 100, last_level: false, }, - metric: { - type: 'vis_dimension', - accessor: 0, - format: { - id: 'number', - params: {}, + metrics: [ + { + type: 'vis_dimension', + accessor: 0, + format: { + id: 'number', + params: {}, + }, }, - }, + ], bucket: { type: 'vis_dimension', accessor: 1, diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/waffle_vis_function.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/waffle_vis_function.ts index 0867e6cb9bd76..2c5fa5dbec4f7 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/waffle_vis_function.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/waffle_vis_function.ts @@ -18,6 +18,7 @@ import { WAFFLE_VIS_EXPRESSION_NAME, } from '../constants'; import { errors, strings } from './i18n'; +import { collapseMetricColumns } from '../utils'; export const waffleVisFunction = (): WaffleVisExpressionFunctionDefinition => ({ name: WAFFLE_VIS_EXPRESSION_NAME, @@ -25,10 +26,11 @@ export const waffleVisFunction = (): WaffleVisExpressionFunctionDefinition => ({ inputTypes: ['datatable'], help: strings.getPieVisFunctionName(), args: { - metric: { + metrics: { types: ['vis_dimension'], help: strings.getMetricArgHelp(), required: true, + multi: true, }, bucket: { types: ['vis_dimension'], @@ -111,7 +113,8 @@ export const waffleVisFunction = (): WaffleVisExpressionFunctionDefinition => ({ throw new Error(errors.splitRowAndSplitColumnAreSpecifiedError()); } - validateAccessor(args.metric, context.columns); + args.metrics.forEach((accessor) => validateAccessor(accessor, context.columns)); + if (args.bucket) { validateAccessor(args.bucket, context.columns); } @@ -122,7 +125,12 @@ export const waffleVisFunction = (): WaffleVisExpressionFunctionDefinition => ({ args.splitRow.forEach((splitRow) => validateAccessor(splitRow, context.columns)); } - const buckets = args.bucket ? [args.bucket] : []; + const { table, metricAccessor, bucketAccessors } = collapseMetricColumns( + context, + args.bucket ? [args.bucket] : [], + args.metrics + ); + const visConfig: PartitionVisParams = { ...args, ariaLabel: @@ -131,8 +139,8 @@ export const waffleVisFunction = (): WaffleVisExpressionFunctionDefinition => ({ handlers.getExecutionContext?.()?.description, palette: args.palette, dimensions: { - metric: args.metric, - buckets, + metric: metricAccessor, + buckets: bucketAccessors, splitColumn: args.splitColumn, splitRow: args.splitRow, }, @@ -143,10 +151,10 @@ export const waffleVisFunction = (): WaffleVisExpressionFunctionDefinition => ({ handlers.inspectorAdapters.tables.allowCsvExport = true; const logTable = prepareLogTable( - context, + table, [ - [[args.metric], strings.getSliceSizeHelp()], - [buckets, strings.getSliceHelp()], + [[metricAccessor], strings.getSliceSizeHelp()], + [bucketAccessors, strings.getSliceHelp()], [args.splitColumn, strings.getColumnSplitHelp()], [args.splitRow, strings.getRowSplitHelp()], ], @@ -159,7 +167,7 @@ export const waffleVisFunction = (): WaffleVisExpressionFunctionDefinition => ({ type: 'render', as: PARTITION_VIS_RENDERER_NAME, value: { - visData: context, + visData: table, visConfig, syncColors: handlers?.isSyncColorsEnabled?.() ?? false, visType: ChartTypes.WAFFLE, diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/types/expression_renderers.ts b/src/plugins/chart_expressions/expression_partition_vis/common/types/expression_renderers.ts index 26d0cc3e5152a..327e13278bd55 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/types/expression_renderers.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/types/expression_renderers.ts @@ -58,7 +58,7 @@ interface VisCommonParams { } interface VisCommonConfig extends VisCommonParams { - metric: ExpressionValueVisDimension | string; + metrics: Array; splitColumn?: Array; splitRow?: Array; labels: ExpressionValuePartitionLabels; @@ -78,8 +78,7 @@ export interface PartitionVisParams extends VisCommonParams { nestedLegend?: boolean; } -export interface PieVisConfig extends Omit { - metrics: Array; +export interface PieVisConfig extends VisCommonConfig { buckets?: Array; partitionByColumn?: boolean; isDonut: boolean; From 5a5b039cf7f79ed39d2b486fba079e97d1668fc4 Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Fri, 9 Sep 2022 10:35:36 -0500 Subject: [PATCH 10/70] apply field formatting to bucket values during collapse --- .../mosaic_vis_function.ts | 19 +++------ .../expression_functions/pie_vis_function.ts | 19 +++------ .../treemap_vis_function.ts | 19 +++------ .../waffle_vis_function.ts | 20 ++++------ .../common/types/expression_renderers.ts | 2 +- .../utils/collapse_metric_columns.test.ts | 34 ++++++++++------ .../common/utils/collapse_metric_columns.ts | 14 +++++-- .../components/partition_vis_component.tsx | 39 +++++++++++++++---- .../expression_partition_vis/public/plugin.ts | 5 ++- .../public/services/format_service.ts | 13 +++++++ .../public/utils/get_columns.ts | 9 +++-- 11 files changed, 115 insertions(+), 78 deletions(-) create mode 100644 src/plugins/chart_expressions/expression_partition_vis/public/services/format_service.ts diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/mosaic_vis_function.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/mosaic_vis_function.ts index c1f9f9c1a7641..96d9da2c1a785 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/mosaic_vis_function.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/mosaic_vis_function.ts @@ -18,7 +18,6 @@ import { MOSAIC_VIS_EXPRESSION_NAME, } from '../constants'; import { errors, strings } from './i18n'; -import { collapseMetricColumns } from '../utils'; export const mosaicVisFunction = (): MosaicVisExpressionFunctionDefinition => ({ name: MOSAIC_VIS_EXPRESSION_NAME, @@ -131,12 +130,6 @@ export const mosaicVisFunction = (): MosaicVisExpressionFunctionDefinition => ({ args.splitRow.forEach((splitRow) => validateAccessor(splitRow, context.columns)); } - const { table, metricAccessor, bucketAccessors } = collapseMetricColumns( - context, - args.buckets, - args.metrics - ); - const visConfig: PartitionVisParams = { ...args, ariaLabel: @@ -145,8 +138,8 @@ export const mosaicVisFunction = (): MosaicVisExpressionFunctionDefinition => ({ handlers.getExecutionContext?.()?.description, palette: args.palette, dimensions: { - metric: metricAccessor, - buckets: bucketAccessors, + metrics: args.metrics, + buckets: args.buckets, splitColumn: args.splitColumn, splitRow: args.splitRow, }, @@ -157,10 +150,10 @@ export const mosaicVisFunction = (): MosaicVisExpressionFunctionDefinition => ({ handlers.inspectorAdapters.tables.allowCsvExport = true; const logTable = prepareLogTable( - table, + context, [ - [[metricAccessor], strings.getSliceSizeHelp()], - [bucketAccessors, strings.getSliceHelp()], + [args.metrics, strings.getSliceSizeHelp()], + [args.buckets, strings.getSliceHelp()], [args.splitColumn, strings.getColumnSplitHelp()], [args.splitRow, strings.getRowSplitHelp()], ], @@ -173,7 +166,7 @@ export const mosaicVisFunction = (): MosaicVisExpressionFunctionDefinition => ({ type: 'render', as: PARTITION_VIS_RENDERER_NAME, value: { - visData: table, + visData: context, visConfig, syncColors: handlers?.isSyncColorsEnabled?.() ?? false, visType: ChartTypes.MOSAIC, diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts index 30a58643c0481..c6086b5e72fb2 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts @@ -18,7 +18,6 @@ import { PARTITION_VIS_RENDERER_NAME, } from '../constants'; import { errors, strings } from './i18n'; -import { collapseMetricColumns } from '../utils'; export const pieVisFunction = (): PieVisExpressionFunctionDefinition => ({ name: PIE_VIS_EXPRESSION_NAME, @@ -150,12 +149,6 @@ export const pieVisFunction = (): PieVisExpressionFunctionDefinition => ({ args.splitRow.forEach((splitRow) => validateAccessor(splitRow, context.columns)); } - const { table, metricAccessor, bucketAccessors } = collapseMetricColumns( - context, - args.buckets, - args.metrics - ); - const visConfig: PartitionVisParams = { ...args, ariaLabel: @@ -164,8 +157,8 @@ export const pieVisFunction = (): PieVisExpressionFunctionDefinition => ({ handlers.getExecutionContext?.()?.description, palette: args.palette, dimensions: { - metric: metricAccessor, - buckets: bucketAccessors, + metrics: args.metrics, + buckets: args.buckets, splitColumn: args.splitColumn, splitRow: args.splitRow, }, @@ -176,10 +169,10 @@ export const pieVisFunction = (): PieVisExpressionFunctionDefinition => ({ handlers.inspectorAdapters.tables.allowCsvExport = true; const logTable = prepareLogTable( - table, + context, [ - [[metricAccessor], strings.getSliceSizeHelp()], - [bucketAccessors, strings.getSliceHelp()], + [args.metrics, strings.getSliceSizeHelp()], + [args.buckets, strings.getSliceHelp()], [args.splitColumn, strings.getColumnSplitHelp()], [args.splitRow, strings.getRowSplitHelp()], ], @@ -192,7 +185,7 @@ export const pieVisFunction = (): PieVisExpressionFunctionDefinition => ({ type: 'render', as: PARTITION_VIS_RENDERER_NAME, value: { - visData: table, + visData: context, visConfig, syncColors: handlers?.isSyncColorsEnabled?.() ?? false, visType: args.isDonut ? ChartTypes.DONUT : ChartTypes.PIE, diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/treemap_vis_function.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/treemap_vis_function.ts index 38ca5d3ea96ea..7ba5c842907d6 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/treemap_vis_function.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/treemap_vis_function.ts @@ -18,7 +18,6 @@ import { TREEMAP_VIS_EXPRESSION_NAME, } from '../constants'; import { errors, strings } from './i18n'; -import { collapseMetricColumns } from '../utils'; export const treemapVisFunction = (): TreemapVisExpressionFunctionDefinition => ({ name: TREEMAP_VIS_EXPRESSION_NAME, @@ -131,12 +130,6 @@ export const treemapVisFunction = (): TreemapVisExpressionFunctionDefinition => args.splitRow.forEach((splitRow) => validateAccessor(splitRow, context.columns)); } - const { table, metricAccessor, bucketAccessors } = collapseMetricColumns( - context, - args.buckets, - args.metrics - ); - const visConfig: PartitionVisParams = { ...args, ariaLabel: @@ -145,8 +138,8 @@ export const treemapVisFunction = (): TreemapVisExpressionFunctionDefinition => handlers.getExecutionContext?.()?.description, palette: args.palette, dimensions: { - metric: metricAccessor, - buckets: bucketAccessors, + metrics: args.metrics, + buckets: args.buckets, splitColumn: args.splitColumn, splitRow: args.splitRow, }, @@ -157,10 +150,10 @@ export const treemapVisFunction = (): TreemapVisExpressionFunctionDefinition => handlers.inspectorAdapters.tables.allowCsvExport = true; const logTable = prepareLogTable( - table, + context, [ - [[metricAccessor], strings.getSliceSizeHelp()], - [bucketAccessors, strings.getSliceHelp()], + [args.metrics, strings.getSliceSizeHelp()], + [args.buckets, strings.getSliceHelp()], [args.splitColumn, strings.getColumnSplitHelp()], [args.splitRow, strings.getRowSplitHelp()], ], @@ -173,7 +166,7 @@ export const treemapVisFunction = (): TreemapVisExpressionFunctionDefinition => type: 'render', as: PARTITION_VIS_RENDERER_NAME, value: { - visData: table, + visData: context, visConfig, syncColors: handlers?.isSyncColorsEnabled?.() ?? false, visType: ChartTypes.TREEMAP, diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/waffle_vis_function.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/waffle_vis_function.ts index 2c5fa5dbec4f7..20b72dad3dfaa 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/waffle_vis_function.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/waffle_vis_function.ts @@ -18,7 +18,6 @@ import { WAFFLE_VIS_EXPRESSION_NAME, } from '../constants'; import { errors, strings } from './i18n'; -import { collapseMetricColumns } from '../utils'; export const waffleVisFunction = (): WaffleVisExpressionFunctionDefinition => ({ name: WAFFLE_VIS_EXPRESSION_NAME, @@ -125,12 +124,7 @@ export const waffleVisFunction = (): WaffleVisExpressionFunctionDefinition => ({ args.splitRow.forEach((splitRow) => validateAccessor(splitRow, context.columns)); } - const { table, metricAccessor, bucketAccessors } = collapseMetricColumns( - context, - args.bucket ? [args.bucket] : [], - args.metrics - ); - + const buckets = args.bucket ? [args.bucket] : []; const visConfig: PartitionVisParams = { ...args, ariaLabel: @@ -139,8 +133,8 @@ export const waffleVisFunction = (): WaffleVisExpressionFunctionDefinition => ({ handlers.getExecutionContext?.()?.description, palette: args.palette, dimensions: { - metric: metricAccessor, - buckets: bucketAccessors, + metrics: args.metrics, + buckets, splitColumn: args.splitColumn, splitRow: args.splitRow, }, @@ -151,10 +145,10 @@ export const waffleVisFunction = (): WaffleVisExpressionFunctionDefinition => ({ handlers.inspectorAdapters.tables.allowCsvExport = true; const logTable = prepareLogTable( - table, + context, [ - [[metricAccessor], strings.getSliceSizeHelp()], - [bucketAccessors, strings.getSliceHelp()], + [args.metrics, strings.getSliceSizeHelp()], + [buckets, strings.getSliceHelp()], [args.splitColumn, strings.getColumnSplitHelp()], [args.splitRow, strings.getRowSplitHelp()], ], @@ -167,7 +161,7 @@ export const waffleVisFunction = (): WaffleVisExpressionFunctionDefinition => ({ type: 'render', as: PARTITION_VIS_RENDERER_NAME, value: { - visData: table, + visData: context, visConfig, syncColors: handlers?.isSyncColorsEnabled?.() ?? false, visType: ChartTypes.WAFFLE, diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/types/expression_renderers.ts b/src/plugins/chart_expressions/expression_partition_vis/common/types/expression_renderers.ts index 327e13278bd55..7fdd60534236f 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/types/expression_renderers.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/types/expression_renderers.ts @@ -29,7 +29,7 @@ export interface Dimension { } export interface Dimensions { - metric?: ExpressionValueVisDimension | string; + metrics: Array; buckets?: Array; splitRow?: Array; splitColumn?: Array; diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/utils/collapse_metric_columns.test.ts b/src/plugins/chart_expressions/expression_partition_vis/common/utils/collapse_metric_columns.test.ts index f95089b32052a..49b65fd93d6ce 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/utils/collapse_metric_columns.test.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/utils/collapse_metric_columns.test.ts @@ -7,9 +7,16 @@ */ import { Datatable } from '@kbn/expressions-plugin/common'; +import { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; import { collapseMetricColumns } from './collapse_metric_columns'; describe('collapseMetricColumns', () => { + const formatServiceMock = { + deserialize: () => ({ + getConverterFor: () => (str: string) => `${str}`, + }), + } as FieldFormatsStart; + it('collapses multiple metrics into a single metric column', () => { const table: Datatable = { type: 'datatable', @@ -51,7 +58,7 @@ describe('collapseMetricColumns', () => { ], }; - const result = collapseMetricColumns(table, ['1', '2'], ['3', '4']); + const result = collapseMetricColumns(table, ['1', '2'], ['3', '4'], formatServiceMock); expect(result.bucketAccessors).toEqual(['1', 'metric-name']); expect(result.metricAccessor).toEqual('value'); expect(result.table).toMatchInlineSnapshot(` @@ -82,42 +89,42 @@ describe('collapseMetricColumns', () => { "rows": Array [ Object { "1": "square", - "metric-name": "red - metric1", + "metric-name": "red - metric1", "value": 1, }, Object { "1": "square", - "metric-name": "red - metric2", + "metric-name": "red - metric2", "value": 2, }, Object { "1": "square", - "metric-name": "blue - metric1", + "metric-name": "blue - metric1", "value": 3, }, Object { "1": "square", - "metric-name": "blue - metric2", + "metric-name": "blue - metric2", "value": 4, }, Object { "1": "circle", - "metric-name": "red - metric1", + "metric-name": "red - metric1", "value": 5, }, Object { "1": "circle", - "metric-name": "red - metric2", + "metric-name": "red - metric2", "value": 6, }, Object { "1": "circle", - "metric-name": "blue - metric1", + "metric-name": "blue - metric1", "value": 7, }, Object { "1": "circle", - "metric-name": "blue - metric2", + "metric-name": "blue - metric2", "value": 8, }, ], @@ -162,7 +169,12 @@ describe('collapseMetricColumns', () => { const bucketAccessors = ['1', '2']; const metricAccessors = ['3']; - const result = collapseMetricColumns(table, bucketAccessors, metricAccessors); + const result = collapseMetricColumns( + table, + bucketAccessors, + metricAccessors, + formatServiceMock + ); expect(result.table).toEqual(table); expect(result.bucketAccessors).toEqual(bucketAccessors); @@ -196,7 +208,7 @@ describe('collapseMetricColumns', () => { ], }; - const result = collapseMetricColumns(table, undefined, ['3', '4']); + const result = collapseMetricColumns(table, undefined, ['3', '4'], formatServiceMock); expect(result.bucketAccessors).toEqual(['metric-name']); expect(result.metricAccessor).toEqual('value'); expect(result.table).toMatchInlineSnapshot(` diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/utils/collapse_metric_columns.ts b/src/plugins/chart_expressions/expression_partition_vis/common/utils/collapse_metric_columns.ts index 0b20715794c2c..37d073a801656 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/utils/collapse_metric_columns.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/utils/collapse_metric_columns.ts @@ -7,13 +7,15 @@ */ import { Datatable, DatatableColumn, DatatableRow } from '@kbn/expressions-plugin/common'; -import { getColumnByAccessor } from '@kbn/visualizations-plugin/common/utils'; +import { getColumnByAccessor, getFormatByAccessor } from '@kbn/visualizations-plugin/common/utils'; import type { ExpressionValueVisDimension } from '@kbn/visualizations-plugin/common'; +import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; export const collapseMetricColumns = ( table: Datatable, bucketAccessors: Array = [], - metricAccessors: Array + metricAccessors: Array, + formatService: FieldFormatsStart ): { table: Datatable; metricAccessor: string | ExpressionValueVisDimension; @@ -42,6 +44,12 @@ export const collapseMetricColumns = ( bucketColumns[bucketColumns.length - 1], ]; + const finalBucketFormatter = finalBucketColumn + ? formatService + .deserialize(getFormatByAccessor(finalBucketColumn.id, table.columns)) + .getConverterFor('text') + : undefined; + const nameColumnId = 'metric-name'; const valueColumnId = 'value'; @@ -54,7 +62,7 @@ export const collapseMetricColumns = ( }); newRow[nameColumnId] = finalBucketColumn - ? `${row[finalBucketColumn.id]} - ${metricCol.name}` + ? `${finalBucketFormatter!(row[finalBucketColumn.id])} - ${metricCol.name}` : metricCol.name; newRow[valueColumnId] = row[metricCol.id]; diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.tsx b/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.tsx index 6ef1bb79f0f3f..b1cbbc6c00853 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.tsx +++ b/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.tsx @@ -34,6 +34,7 @@ import { IInterpreterRenderHandlers, } from '@kbn/expressions-plugin/public'; import type { FieldFormat } from '@kbn/field-formats-plugin/common'; +import { collapseMetricColumns } from '../../common/utils'; import { DEFAULT_PERCENT_DECIMALS } from '../../common/constants'; import { PartitionVisParams, @@ -68,6 +69,7 @@ import { import { ChartTypes } from '../../common/types'; import { filterOutConfig } from '../utils/filter_out_config'; import { FilterEvent, StartDeps } from '../types'; +import { getFormatService } from '../services/format_service'; declare global { interface Window { @@ -91,14 +93,35 @@ export interface PartitionVisComponentProps { } const PartitionVisComponent = (props: PartitionVisComponentProps) => { - const { visData, visParams: preVisParams, visType, services, syncColors } = props; + const { + visData: originalVisData, + visParams: preVisParams, + visType, + services, + syncColors, + } = props; const visParams = useMemo(() => filterOutConfig(visType, preVisParams), [preVisParams, visType]); const chartTheme = props.chartsThemeService.useChartsTheme(); const chartBaseTheme = props.chartsThemeService.useChartsBaseTheme(); + const { + table: visData, + metricAccessor, + bucketAccessors, + } = useMemo( + () => + collapseMetricColumns( + originalVisData, + visParams.dimensions.buckets, + visParams.dimensions.metrics, + getFormatService() + ), + [originalVisData, visParams.dimensions.buckets, visParams.dimensions.metrics] + ); + const { bucketColumns, metricColumn } = useMemo( - () => getColumns(props.visParams, props.visData), - [props.visData, props.visParams] + () => getColumns({ metric: metricAccessor, buckets: bucketAccessors }, visData), + [bucketAccessors, metricAccessor, visData] ); const formatters = useMemo( @@ -115,7 +138,9 @@ const PartitionVisComponent = (props: PartitionVisComponentProps) => { const showToggleLegendElement = props.uiState !== undefined; - const [dimensions, setDimensions] = useState(); + const [containerDimensions, setContainerDimensions] = useState< + undefined | PieContainerDimensions + >(); const parentRef = useRef(null); @@ -123,7 +148,7 @@ const PartitionVisComponent = (props: PartitionVisComponentProps) => { if (parentRef && parentRef.current) { const parentHeight = parentRef.current!.getBoundingClientRect().height; const parentWidth = parentRef.current!.getBoundingClientRect().width; - setDimensions({ width: parentWidth, height: parentHeight }); + setContainerDimensions({ width: parentWidth, height: parentHeight }); } }, [parentRef]); @@ -292,8 +317,8 @@ const PartitionVisComponent = (props: PartitionVisComponentProps) => { }, [visData.rows, metricColumn]); const themeOverrides = useMemo( - () => getPartitionTheme(visType, visParams, chartTheme, dimensions, rescaleFactor), - [visType, visParams, chartTheme, dimensions, rescaleFactor] + () => getPartitionTheme(visType, visParams, chartTheme, containerDimensions, rescaleFactor), + [visType, visParams, chartTheme, containerDimensions, rescaleFactor] ); const fixedViewPort = document.getElementById('app-fixed-viewport'); diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/plugin.ts b/src/plugins/chart_expressions/expression_partition_vis/public/plugin.ts index d5a003b3013fa..50272a02bb48c 100755 --- a/src/plugins/chart_expressions/expression_partition_vis/public/plugin.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/public/plugin.ts @@ -16,6 +16,7 @@ import { waffleVisFunction, } from '../common'; import { getPartitionVisRenderer } from './expression_renderers'; +import { setFormatService } from './services/format_service'; import { ExpressionPartitionVisPluginSetup, ExpressionPartitionVisPluginStart, @@ -44,7 +45,9 @@ export class ExpressionPartitionVisPlugin { expressions.registerRenderer(getPartitionVisRenderer({ getStartDeps })); } - public start(core: CoreStart, deps: StartDeps): ExpressionPartitionVisPluginStart {} + public start(core: CoreStart, deps: StartDeps): ExpressionPartitionVisPluginStart { + setFormatService(deps.fieldFormats); + } public stop() {} } diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/services/format_service.ts b/src/plugins/chart_expressions/expression_partition_vis/public/services/format_service.ts new file mode 100644 index 0000000000000..f8e48e3101952 --- /dev/null +++ b/src/plugins/chart_expressions/expression_partition_vis/public/services/format_service.ts @@ -0,0 +1,13 @@ +/* + * 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 { createGetterSetter } from '@kbn/kibana-utils-plugin/public'; +import { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; + +export const [getFormatService, setFormatService] = + createGetterSetter('fieldFormats'); diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/utils/get_columns.ts b/src/plugins/chart_expressions/expression_partition_vis/public/utils/get_columns.ts index 0b5d0d101cf38..56d461515aa46 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/utils/get_columns.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/public/utils/get_columns.ts @@ -9,7 +9,7 @@ import { ExpressionValueVisDimension } from '@kbn/visualizations-plugin/common'; import { getColumnByAccessor, getFormatByAccessor } from '@kbn/visualizations-plugin/common/utils'; import { DatatableColumn, Datatable } from '@kbn/expressions-plugin/public'; -import { BucketColumns, PartitionVisParams } from '../../common/types'; +import { BucketColumns } from '../../common/types'; const getMetricColumn = ( metricAccessor: ExpressionValueVisDimension | string, @@ -19,13 +19,16 @@ const getMetricColumn = ( }; export const getColumns = ( - visParams: PartitionVisParams, + dimensions: { + metric: string | ExpressionValueVisDimension; + buckets: Array; + }, visData: Datatable ): { metricColumn: DatatableColumn; bucketColumns: Array>; } => { - const { metric, buckets } = visParams.dimensions; + const { metric, buckets } = dimensions; if (buckets && buckets.length > 0) { const bucketColumns: Array> = buckets.map((bucket) => { const column = getColumnByAccessor(bucket, visData.columns); From 8b3e0ca7f7aa12d060462dfe15ffab7daed46a69 Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Fri, 9 Sep 2022 13:16:22 -0500 Subject: [PATCH 11/70] use already-provided format service --- .../public/components/partition_vis_component.tsx | 10 +++++++--- .../expression_partition_vis/public/plugin.ts | 5 +---- .../public/services/format_service.ts | 13 ------------- 3 files changed, 8 insertions(+), 20 deletions(-) delete mode 100644 src/plugins/chart_expressions/expression_partition_vis/public/services/format_service.ts diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.tsx b/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.tsx index b1cbbc6c00853..b83a3d45064f7 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.tsx +++ b/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.tsx @@ -69,7 +69,6 @@ import { import { ChartTypes } from '../../common/types'; import { filterOutConfig } from '../utils/filter_out_config'; import { FilterEvent, StartDeps } from '../types'; -import { getFormatService } from '../services/format_service'; declare global { interface Window { @@ -114,9 +113,14 @@ const PartitionVisComponent = (props: PartitionVisComponentProps) => { originalVisData, visParams.dimensions.buckets, visParams.dimensions.metrics, - getFormatService() + services.fieldFormats ), - [originalVisData, visParams.dimensions.buckets, visParams.dimensions.metrics] + [ + originalVisData, + services.fieldFormats, + visParams.dimensions.buckets, + visParams.dimensions.metrics, + ] ); const { bucketColumns, metricColumn } = useMemo( diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/plugin.ts b/src/plugins/chart_expressions/expression_partition_vis/public/plugin.ts index 50272a02bb48c..d5a003b3013fa 100755 --- a/src/plugins/chart_expressions/expression_partition_vis/public/plugin.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/public/plugin.ts @@ -16,7 +16,6 @@ import { waffleVisFunction, } from '../common'; import { getPartitionVisRenderer } from './expression_renderers'; -import { setFormatService } from './services/format_service'; import { ExpressionPartitionVisPluginSetup, ExpressionPartitionVisPluginStart, @@ -45,9 +44,7 @@ export class ExpressionPartitionVisPlugin { expressions.registerRenderer(getPartitionVisRenderer({ getStartDeps })); } - public start(core: CoreStart, deps: StartDeps): ExpressionPartitionVisPluginStart { - setFormatService(deps.fieldFormats); - } + public start(core: CoreStart, deps: StartDeps): ExpressionPartitionVisPluginStart {} public stop() {} } diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/services/format_service.ts b/src/plugins/chart_expressions/expression_partition_vis/public/services/format_service.ts deleted file mode 100644 index f8e48e3101952..0000000000000 --- a/src/plugins/chart_expressions/expression_partition_vis/public/services/format_service.ts +++ /dev/null @@ -1,13 +0,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 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 { createGetterSetter } from '@kbn/kibana-utils-plugin/public'; -import { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; - -export const [getFormatService, setFormatService] = - createGetterSetter('fieldFormats'); From 545d732ee45bcbee9035c53240b7cd8212355cc6 Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Fri, 9 Sep 2022 13:39:23 -0500 Subject: [PATCH 12/70] update tests --- .../mosaic_vis_function.test.ts.snap | 16 +- .../pie_vis_function.test.ts.snap | 32 +- .../treemap_vis_function.test.ts.snap | 16 +- .../waffle_vis_function.test.ts.snap | 16 +- .../partition_vis_component.test.tsx.snap | 576 +++++++++++++----- .../partition_vis_component.test.tsx | 15 + .../expression_partition_vis/public/mocks.ts | 16 +- 7 files changed, 485 insertions(+), 202 deletions(-) diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/mosaic_vis_function.test.ts.snap b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/mosaic_vis_function.test.ts.snap index acee7e4fa9262..59150e008e651 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/mosaic_vis_function.test.ts.snap +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/mosaic_vis_function.test.ts.snap @@ -89,14 +89,16 @@ Object { "type": "vis_dimension", }, ], - "metric": Object { - "accessor": 0, - "format": Object { - "id": "number", - "params": Object {}, + "metrics": Array [ + Object { + "accessor": 0, + "format": Object { + "id": "number", + "params": Object {}, + }, + "type": "vis_dimension", }, - "type": "vis_dimension", - }, + ], "splitColumn": undefined, "splitRow": undefined, }, diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/pie_vis_function.test.ts.snap b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/pie_vis_function.test.ts.snap index 182bb98f98091..2089da9aee3dc 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/pie_vis_function.test.ts.snap +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/pie_vis_function.test.ts.snap @@ -86,14 +86,16 @@ Object { "type": "vis_dimension", }, ], - "metric": Object { - "accessor": 0, - "format": Object { - "id": "number", - "params": Object {}, + "metrics": Array [ + Object { + "accessor": 0, + "format": Object { + "id": "number", + "params": Object {}, + }, + "type": "vis_dimension", }, - "type": "vis_dimension", - }, + ], "splitColumn": undefined, "splitRow": undefined, }, @@ -222,14 +224,16 @@ Object { "type": "vis_dimension", }, ], - "metric": Object { - "accessor": 0, - "format": Object { - "id": "number", - "params": Object {}, + "metrics": Array [ + Object { + "accessor": 0, + "format": Object { + "id": "number", + "params": Object {}, + }, + "type": "vis_dimension", }, - "type": "vis_dimension", - }, + ], "splitColumn": undefined, "splitRow": undefined, }, diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/treemap_vis_function.test.ts.snap b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/treemap_vis_function.test.ts.snap index 0cdca2f770dff..74f89a037df85 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/treemap_vis_function.test.ts.snap +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/treemap_vis_function.test.ts.snap @@ -89,14 +89,16 @@ Object { "type": "vis_dimension", }, ], - "metric": Object { - "accessor": 0, - "format": Object { - "id": "number", - "params": Object {}, + "metrics": Array [ + Object { + "accessor": 0, + "format": Object { + "id": "number", + "params": Object {}, + }, + "type": "vis_dimension", }, - "type": "vis_dimension", - }, + ], "splitColumn": undefined, "splitRow": undefined, }, diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/waffle_vis_function.test.ts.snap b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/waffle_vis_function.test.ts.snap index a2cfe26ae8a54..336979eff662d 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/waffle_vis_function.test.ts.snap +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/waffle_vis_function.test.ts.snap @@ -63,14 +63,16 @@ Object { "type": "vis_dimension", }, ], - "metric": Object { - "accessor": 0, - "format": Object { - "id": "number", - "params": Object {}, + "metrics": Array [ + Object { + "accessor": 0, + "format": Object { + "id": "number", + "params": Object {}, + }, + "type": "vis_dimension", }, - "type": "vis_dimension", - }, + ], "splitColumn": undefined, "splitRow": undefined, }, diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/components/__snapshots__/partition_vis_component.test.tsx.snap b/src/plugins/chart_expressions/expression_partition_vis/public/components/__snapshots__/partition_vis_component.test.tsx.snap index 121c40d50afe2..ec339c872379e 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/components/__snapshots__/partition_vis_component.test.tsx.snap +++ b/src/plugins/chart_expressions/expression_partition_vis/public/components/__snapshots__/partition_vis_component.test.tsx.snap @@ -194,14 +194,6 @@ exports[`PartitionVisComponent should render correct structure for donut 1`] = ` "vis.colors", Object {}, ], - Array [ - "vis.legendOpen", - true, - ], - Array [ - "vis.colors", - Object {}, - ], ], "results": Array [ Object { @@ -212,14 +204,6 @@ exports[`PartitionVisComponent should render correct structure for donut 1`] = ` "type": "return", "value": Object {}, }, - Object { - "type": "return", - "value": true, - }, - Object { - "type": "return", - "value": Object {}, - }, ], }, "set": [MockFunction], @@ -597,30 +581,6 @@ exports[`PartitionVisComponent should render correct structure for mosaic 1`] = "vis.colors", Object {}, ], - Array [ - "vis.legendOpen", - true, - ], - Array [ - "vis.colors", - Object {}, - ], - Array [ - "vis.legendOpen", - true, - ], - Array [ - "vis.colors", - Object {}, - ], - Array [ - "vis.legendOpen", - true, - ], - Array [ - "vis.colors", - Object {}, - ], ], "results": Array [ Object { @@ -631,30 +591,6 @@ exports[`PartitionVisComponent should render correct structure for mosaic 1`] = "type": "return", "value": Object {}, }, - Object { - "type": "return", - "value": true, - }, - Object { - "type": "return", - "value": Object {}, - }, - Object { - "type": "return", - "value": true, - }, - Object { - "type": "return", - "value": Object {}, - }, - Object { - "type": "return", - "value": true, - }, - Object { - "type": "return", - "value": Object {}, - }, ], }, "set": [MockFunction], @@ -840,6 +776,422 @@ exports[`PartitionVisComponent should render correct structure for mosaic 1`] = `; +exports[`PartitionVisComponent should render correct structure for multi-metric pie 1`] = ` +
+
+ + + + + + } + onElementClick={[Function]} + onRenderChange={[Function]} + showLegend={true} + theme={ + Array [ + Object { + "background": Object { + "color": "transparent", + }, + }, + Object { + "chartMargins": Object { + "bottom": 0, + "left": 0, + "right": 0, + "top": 0, + }, + "partition": Object { + "circlePadding": 4, + "emptySizeRatio": 0, + "fontFamily": undefined, + "linkLabel": Object { + "fontSize": 11, + "maxCount": 5, + "maxTextLength": 100, + "textColor": undefined, + }, + "maxFontSize": 16, + "minFontSize": 10, + "outerSizeRatio": undefined, + "sectorLineStroke": undefined, + "sectorLineWidth": 1.5, + }, + }, + Object {}, + Object { + "legend": Object { + "labelOptions": Object { + "maxLines": 1, + }, + }, + }, + ] + } + tooltip={ + Object { + "type": "follow", + } + } + /> + + + +
+
+`; + exports[`PartitionVisComponent should render correct structure for pie 1`] = `
{ const original = jest.requireActual('@elastic/charts'); @@ -83,11 +84,25 @@ describe('PartitionVisComponent', function () { }; }); + afterEach(() => { + mockState.clear(); + jest.clearAllMocks(); + }); + it('should render correct structure for pie', function () { const component = shallow(); expect(component).toMatchSnapshot(); }); + it('should render correct structure for multi-metric pie', function () { + const localParams = cloneDeep(wrapperProps.visParams); + + localParams.dimensions.metrics = [...localParams.dimensions.metrics, 'col-3-1']; + + const component = shallow(); + expect(component).toMatchSnapshot(); + }); + it('should render correct structure for donut', function () { const donutVisParams = createMockDonutParams(); const component = shallow( diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/mocks.ts b/src/plugins/chart_expressions/expression_partition_vis/public/mocks.ts index 455dd111179d2..d9aaf309fc108 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/mocks.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/public/mocks.ts @@ -293,16 +293,18 @@ export const createMockPartitionVisParams = (): PartitionVisParams => { type: 'palette', }, dimensions: { - metric: { - type: 'vis_dimension', - accessor: 1, - format: { - id: 'number', - params: { + metrics: [ + { + type: 'vis_dimension', + accessor: 1, + format: { id: 'number', + params: { + id: 'number', + }, }, }, - }, + ], buckets: [ { type: 'vis_dimension', From 1ea78a68fb4c0bbea5785ea0d0f856cceb8e5ff3 Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Fri, 9 Sep 2022 15:46:57 -0500 Subject: [PATCH 13/70] support consolidated tables for filtering --- .../utils/collapse_metric_columns.test.ts | 8 +++++ .../common/utils/collapse_metric_columns.ts | 4 +++ .../components/partition_vis_component.tsx | 4 ++- .../public/utils/filter_helpers.test.ts | 13 +++++-- .../public/utils/filter_helpers.ts | 36 ++++++++++++++----- 5 files changed, 53 insertions(+), 12 deletions(-) diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/utils/collapse_metric_columns.test.ts b/src/plugins/chart_expressions/expression_partition_vis/common/utils/collapse_metric_columns.test.ts index 49b65fd93d6ce..61f3bb0325f28 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/utils/collapse_metric_columns.test.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/utils/collapse_metric_columns.test.ts @@ -74,6 +74,10 @@ describe('collapseMetricColumns', () => { Object { "id": "metric-name", "meta": Object { + "sourceParams": Object { + "collapsedMetricsColumn": true, + "combinedWithBucketColumn": true, + }, "type": "string", }, "name": "metric-name", @@ -217,6 +221,10 @@ describe('collapseMetricColumns', () => { Object { "id": "metric-name", "meta": Object { + "sourceParams": Object { + "collapsedMetricsColumn": true, + "combinedWithBucketColumn": false, + }, "type": "string", }, "name": "metric-name", diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/utils/collapse_metric_columns.ts b/src/plugins/chart_expressions/expression_partition_vis/common/utils/collapse_metric_columns.ts index 37d073a801656..9f21648e178b5 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/utils/collapse_metric_columns.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/utils/collapse_metric_columns.ts @@ -77,6 +77,10 @@ export const collapseMetricColumns = ( name: nameColumnId, meta: { type: 'string', + sourceParams: { + collapsedMetricsColumn: true, + combinedWithBucketColumn: Boolean(finalBucketColumn), + }, }, }, { diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.tsx b/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.tsx index b83a3d45064f7..cd8b4b37e8244 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.tsx +++ b/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.tsx @@ -183,12 +183,14 @@ const PartitionVisComponent = (props: PartitionVisComponentProps) => { clickedLayers, buckets, vData, + originalVisData, + visParams.dimensions.metrics.length, splitChartDimension, splitChartFormatter ); props.fireEvent({ name: 'filter', data: { data } }); }, - [props] + [originalVisData, props, visParams.dimensions.metrics.length] ); // handles legend action event data diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/utils/filter_helpers.test.ts b/src/plugins/chart_expressions/expression_partition_vis/public/utils/filter_helpers.test.ts index fbd68ad5177f4..f6b62cb7a705a 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/utils/filter_helpers.test.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/public/utils/filter_helpers.test.ts @@ -24,7 +24,7 @@ describe('getFilterClickData', () => { smAccessorValue: '', }, ]; - const data = getFilterClickData(clickedLayers, bucketColumns, visData); + const data = getFilterClickData(clickedLayers, bucketColumns, visData, visData, 1); expect(data.length).toEqual(clickedLayers.length); expect(data[0].value).toEqual('Logstash Airways'); expect(data[0].row).toEqual(0); @@ -42,7 +42,7 @@ describe('getFilterClickData', () => { smAccessorValue: '', }, ]; - const data = getFilterClickData(clickedLayers, bucketColumns, visData); + const data = getFilterClickData(clickedLayers, bucketColumns, visData, visData, 1); expect(data.length).toEqual(clickedLayers.length); expect(data[0].value).toEqual('ES-Air'); expect(data[0].row).toEqual(4); @@ -64,7 +64,14 @@ describe('getFilterClickData', () => { id: 'col-2-3', name: 'Cancelled: Descending', } as DatatableColumn; - const data = getFilterClickData(clickedLayers, bucketColumns, visData, splitDimension); + const data = getFilterClickData( + clickedLayers, + bucketColumns, + visData, + visData, + 1, + splitDimension + ); expect(data.length).toEqual(2); expect(data[0].value).toEqual('ES-Air'); expect(data[0].row).toEqual(5); diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/utils/filter_helpers.ts b/src/plugins/chart_expressions/expression_partition_vis/public/utils/filter_helpers.ts index c36e160c0c36b..480c3340e9caa 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/utils/filter_helpers.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/public/utils/filter_helpers.ts @@ -29,11 +29,13 @@ export const getFilterClickData = ( clickedLayers: LayerValue[], bucketColumns: Array>, visData: Datatable, + originalVisData: Datatable, // before multiple metrics are consolidated with collapseMetricColumns + numOriginalMetrics: number, splitChartDimension?: DatatableColumn, splitChartFormatter?: FieldFormat ): ValueClickContext['data']['data'] => { const data: ValueClickContext['data']['data'] = []; - const matchingIndex = visData.rows.findIndex((row) => + const rowIndex = visData.rows.findIndex((row) => clickedLayers.every((layer, index) => { const columnId = bucketColumns[index].id; if (!columnId) return; @@ -47,20 +49,38 @@ export const getFilterClickData = ( }) ); + const originalRowIndex = Math.floor(rowIndex / numOriginalMetrics); + data.push( - ...clickedLayers.map((clickedLayer, index) => ({ - column: visData.columns.findIndex((col) => col.id === bucketColumns[index].id), - row: matchingIndex, - value: clickedLayer.groupByRollup, - table: visData, - })) + ...clickedLayers + .map((clickedLayer, index) => { + const currentColumn = visData.columns.find((col) => col.id === bucketColumns[index].id); + const currentColumnIndex = visData.columns.findIndex( + (col) => col.id === bucketColumns[index].id + ); + + const originalColumnIndexes = currentColumn!.meta?.sourceParams?.collapsedMetricsColumn + ? // if this is the special combined column, expand it into both original columns + currentColumn!.meta.sourceParams.combinedWithBucketColumn + ? [currentColumnIndex + 1 + (rowIndex % numOriginalMetrics), currentColumnIndex] + : [currentColumnIndex + (rowIndex % numOriginalMetrics)] + : [currentColumnIndex]; + + return originalColumnIndexes.map((colIdx) => ({ + column: colIdx, + row: originalRowIndex, + value: clickedLayer.groupByRollup, + table: originalVisData, + })); + }) + .flat() ); // Allows filtering with the small multiples value if (splitChartDimension) { data.push({ column: visData.columns.findIndex((col) => col.id === splitChartDimension.id), - row: matchingIndex, + row: rowIndex, table: visData, value: clickedLayers[0].smAccessorValue, }); From bbf2a4f03c39201a6c53ae6b7e083e4c95de09ac Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Fri, 9 Sep 2022 16:09:35 -0500 Subject: [PATCH 14/70] update tests and names --- ....ts => consolidate_metric_columns.test.ts} | 14 ++-- ...lumns.ts => consolidate_metric_columns.ts} | 6 +- .../common/utils/index.ts | 2 +- .../partition_vis_component.test.tsx.snap | 4 + .../components/partition_vis_component.tsx | 4 +- .../public/utils/filter_helpers.ts | 2 +- .../public/utils/get_columns.test.ts | 78 ++++--------------- .../public/utils/get_columns.ts | 4 +- 8 files changed, 37 insertions(+), 77 deletions(-) rename src/plugins/chart_expressions/expression_partition_vis/common/utils/{collapse_metric_columns.test.ts => consolidate_metric_columns.test.ts} (93%) rename src/plugins/chart_expressions/expression_partition_vis/common/utils/{collapse_metric_columns.ts => consolidate_metric_columns.ts} (95%) diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/utils/collapse_metric_columns.test.ts b/src/plugins/chart_expressions/expression_partition_vis/common/utils/consolidate_metric_columns.test.ts similarity index 93% rename from src/plugins/chart_expressions/expression_partition_vis/common/utils/collapse_metric_columns.test.ts rename to src/plugins/chart_expressions/expression_partition_vis/common/utils/consolidate_metric_columns.test.ts index 61f3bb0325f28..c2983a5f9e998 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/utils/collapse_metric_columns.test.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/utils/consolidate_metric_columns.test.ts @@ -8,9 +8,9 @@ import { Datatable } from '@kbn/expressions-plugin/common'; import { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; -import { collapseMetricColumns } from './collapse_metric_columns'; +import { consolidateMetricColumns } from './consolidate_metric_columns'; -describe('collapseMetricColumns', () => { +describe('consolidateMetricColumns', () => { const formatServiceMock = { deserialize: () => ({ getConverterFor: () => (str: string) => `${str}`, @@ -58,7 +58,7 @@ describe('collapseMetricColumns', () => { ], }; - const result = collapseMetricColumns(table, ['1', '2'], ['3', '4'], formatServiceMock); + const result = consolidateMetricColumns(table, ['1', '2'], ['3', '4'], formatServiceMock); expect(result.bucketAccessors).toEqual(['1', 'metric-name']); expect(result.metricAccessor).toEqual('value'); expect(result.table).toMatchInlineSnapshot(` @@ -75,8 +75,8 @@ describe('collapseMetricColumns', () => { "id": "metric-name", "meta": Object { "sourceParams": Object { - "collapsedMetricsColumn": true, "combinedWithBucketColumn": true, + "consolidatedMetricsColumn": true, }, "type": "string", }, @@ -173,7 +173,7 @@ describe('collapseMetricColumns', () => { const bucketAccessors = ['1', '2']; const metricAccessors = ['3']; - const result = collapseMetricColumns( + const result = consolidateMetricColumns( table, bucketAccessors, metricAccessors, @@ -212,7 +212,7 @@ describe('collapseMetricColumns', () => { ], }; - const result = collapseMetricColumns(table, undefined, ['3', '4'], formatServiceMock); + const result = consolidateMetricColumns(table, undefined, ['3', '4'], formatServiceMock); expect(result.bucketAccessors).toEqual(['metric-name']); expect(result.metricAccessor).toEqual('value'); expect(result.table).toMatchInlineSnapshot(` @@ -222,8 +222,8 @@ describe('collapseMetricColumns', () => { "id": "metric-name", "meta": Object { "sourceParams": Object { - "collapsedMetricsColumn": true, "combinedWithBucketColumn": false, + "consolidatedMetricsColumn": true, }, "type": "string", }, diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/utils/collapse_metric_columns.ts b/src/plugins/chart_expressions/expression_partition_vis/common/utils/consolidate_metric_columns.ts similarity index 95% rename from src/plugins/chart_expressions/expression_partition_vis/common/utils/collapse_metric_columns.ts rename to src/plugins/chart_expressions/expression_partition_vis/common/utils/consolidate_metric_columns.ts index 9f21648e178b5..5f9bd22a4cc3c 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/utils/collapse_metric_columns.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/utils/consolidate_metric_columns.ts @@ -11,14 +11,14 @@ import { getColumnByAccessor, getFormatByAccessor } from '@kbn/visualizations-pl import type { ExpressionValueVisDimension } from '@kbn/visualizations-plugin/common'; import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; -export const collapseMetricColumns = ( +export const consolidateMetricColumns = ( table: Datatable, bucketAccessors: Array = [], metricAccessors: Array, formatService: FieldFormatsStart ): { table: Datatable; - metricAccessor: string | ExpressionValueVisDimension; + metricAccessor: string | ExpressionValueVisDimension | undefined; bucketAccessors: Array; } => { if (metricAccessors.length < 2) { @@ -78,7 +78,7 @@ export const collapseMetricColumns = ( meta: { type: 'string', sourceParams: { - collapsedMetricsColumn: true, + consolidatedMetricsColumn: true, combinedWithBucketColumn: Boolean(finalBucketColumn), }, }, diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/utils/index.ts b/src/plugins/chart_expressions/expression_partition_vis/common/utils/index.ts index c47f8fa385566..12ea01c9177cb 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/utils/index.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/utils/index.ts @@ -6,4 +6,4 @@ * Side Public License, v 1. */ -export * from './collapse_metric_columns'; +export * from './consolidate_metric_columns'; diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/components/__snapshots__/partition_vis_component.test.tsx.snap b/src/plugins/chart_expressions/expression_partition_vis/public/components/__snapshots__/partition_vis_component.test.tsx.snap index ec339c872379e..7f25a2ad0ba1c 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/components/__snapshots__/partition_vis_component.test.tsx.snap +++ b/src/plugins/chart_expressions/expression_partition_vis/public/components/__snapshots__/partition_vis_component.test.tsx.snap @@ -864,6 +864,10 @@ exports[`PartitionVisComponent should render correct structure for multi-metric "format": undefined, "id": "metric-name", "meta": Object { + "sourceParams": Object { + "combinedWithBucketColumn": true, + "consolidatedMetricsColumn": true, + }, "type": "string", }, "name": "metric-name", diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.tsx b/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.tsx index cd8b4b37e8244..1cf82eac01f0b 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.tsx +++ b/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.tsx @@ -34,7 +34,7 @@ import { IInterpreterRenderHandlers, } from '@kbn/expressions-plugin/public'; import type { FieldFormat } from '@kbn/field-formats-plugin/common'; -import { collapseMetricColumns } from '../../common/utils'; +import { consolidateMetricColumns } from '../../common/utils'; import { DEFAULT_PERCENT_DECIMALS } from '../../common/constants'; import { PartitionVisParams, @@ -109,7 +109,7 @@ const PartitionVisComponent = (props: PartitionVisComponentProps) => { bucketAccessors, } = useMemo( () => - collapseMetricColumns( + consolidateMetricColumns( originalVisData, visParams.dimensions.buckets, visParams.dimensions.metrics, diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/utils/filter_helpers.ts b/src/plugins/chart_expressions/expression_partition_vis/public/utils/filter_helpers.ts index 480c3340e9caa..5130447fcadd9 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/utils/filter_helpers.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/public/utils/filter_helpers.ts @@ -59,7 +59,7 @@ export const getFilterClickData = ( (col) => col.id === bucketColumns[index].id ); - const originalColumnIndexes = currentColumn!.meta?.sourceParams?.collapsedMetricsColumn + const originalColumnIndexes = currentColumn!.meta?.sourceParams?.consolidatedMetricsColumn ? // if this is the special combined column, expand it into both original columns currentColumn!.meta.sourceParams.combinedWithBucketColumn ? [currentColumnIndex + 1 + (rowIndex % numOriginalMetrics), currentColumnIndex] diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/utils/get_columns.test.ts b/src/plugins/chart_expressions/expression_partition_vis/public/utils/get_columns.test.ts index 157336599a26e..a544dfe1f8537 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/utils/get_columns.test.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/public/utils/get_columns.test.ts @@ -7,20 +7,18 @@ */ import { getColumns } from './get_columns'; -import { - LabelPositions, - LegendDisplay, - PartitionVisParams, - ValueFormats, -} from '../../common/types'; import { createMockPieParams, createMockVisData } from '../mocks'; const visParams = createMockPieParams(); +const dimensions = { + metric: visParams.dimensions.metrics[0], + buckets: visParams.dimensions.buckets!, +}; const visData = createMockVisData(); describe('getColumns', () => { it('should return the correct bucket columns if visParams returns dimensions', () => { - const { bucketColumns } = getColumns(visParams, visData); + const { bucketColumns } = getColumns(dimensions, visData); expect(bucketColumns.length).toEqual(visParams.dimensions.buckets?.length); expect(bucketColumns).toEqual([ { @@ -115,11 +113,8 @@ describe('getColumns', () => { it('should return the correct metric column if visParams returns dimensions', () => { const { metricColumn } = getColumns( { - ...visParams, - dimensions: { - ...visParams.dimensions, - metric: undefined, - }, + ...dimensions, + metric: undefined, }, visData ); @@ -144,28 +139,8 @@ describe('getColumns', () => { }); it('should return the first data column if no buckets specified', () => { - const visParamsOnlyMetric: PartitionVisParams = { - legendDisplay: LegendDisplay.SHOW, - addTooltip: true, - labels: { - position: LabelPositions.DEFAULT, - show: true, - truncate: 100, - values: true, - valuesFormat: ValueFormats.PERCENT, - percentDecimals: 2, - last_level: false, - }, - legendPosition: 'right', - nestedLegend: false, - maxLegendLines: 1, - truncateLegend: false, - distinctColors: false, - palette: { - name: 'default', - type: 'palette', - }, - dimensions: { + const { metricColumn } = getColumns( + { metric: { type: 'vis_dimension', accessor: 1, @@ -174,9 +149,10 @@ describe('getColumns', () => { params: {}, }, }, + buckets: [], }, - }; - const { metricColumn } = getColumns(visParamsOnlyMetric, visData); + visData + ); expect(metricColumn).toEqual({ id: 'col-1-1', meta: { @@ -200,29 +176,8 @@ describe('getColumns', () => { }); it('should return an object with the name of the metric if no buckets specified', () => { - const visParamsOnlyMetric: PartitionVisParams = { - legendDisplay: LegendDisplay.SHOW, - addTooltip: true, - isDonut: true, - labels: { - position: LabelPositions.DEFAULT, - show: true, - truncate: 100, - values: true, - valuesFormat: ValueFormats.PERCENT, - percentDecimals: 2, - last_level: false, - }, - truncateLegend: false, - maxLegendLines: 100, - distinctColors: false, - legendPosition: 'right', - nestedLegend: false, - palette: { - name: 'default', - type: 'palette', - }, - dimensions: { + const { bucketColumns, metricColumn } = getColumns( + { metric: { type: 'vis_dimension', accessor: 1, @@ -231,9 +186,10 @@ describe('getColumns', () => { params: {}, }, }, + buckets: [], }, - }; - const { bucketColumns, metricColumn } = getColumns(visParamsOnlyMetric, visData); + visData + ); expect(bucketColumns).toEqual([{ name: metricColumn.name }]); }); }); diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/utils/get_columns.ts b/src/plugins/chart_expressions/expression_partition_vis/public/utils/get_columns.ts index 56d461515aa46..53d039a9e5199 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/utils/get_columns.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/public/utils/get_columns.ts @@ -20,7 +20,7 @@ const getMetricColumn = ( export const getColumns = ( dimensions: { - metric: string | ExpressionValueVisDimension; + metric: string | ExpressionValueVisDimension | undefined; buckets: Array; }, visData: Datatable @@ -29,7 +29,7 @@ export const getColumns = ( bucketColumns: Array>; } => { const { metric, buckets } = dimensions; - if (buckets && buckets.length > 0) { + if (buckets.length > 0) { const bucketColumns: Array> = buckets.map((bucket) => { const column = getColumnByAccessor(bucket, visData.columns); return { From eec55e688c0ad96c0aa63dc15623a83032e15f9c Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Mon, 12 Sep 2022 11:17:21 -0500 Subject: [PATCH 15/70] update state generation code --- .../public/__stories__/shared/config.ts | 26 ++++++++++--------- .../testing_embedded_lens/public/app.tsx | 2 +- .../form/pack_queries_status_table.tsx | 2 +- .../packs/pack_queries_status_table.tsx | 2 +- .../visitor_breakdown_chart.test.tsx.snap | 4 ++- .../charts/visitor_breakdown_chart.tsx | 2 +- 6 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/__stories__/shared/config.ts b/src/plugins/chart_expressions/expression_partition_vis/public/__stories__/shared/config.ts index d16802518cce4..e0424a3249210 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/__stories__/shared/config.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/public/__stories__/shared/config.ts @@ -35,20 +35,22 @@ export const config: RenderValue['visConfig'] = { last_level: false, }, dimensions: { - metric: { - type: 'vis_dimension', - accessor: { - id: 'percent_uptime', - name: 'percent_uptime', - meta: { - type: 'number', + metrics: [ + { + type: 'vis_dimension', + accessor: { + id: 'percent_uptime', + name: 'percent_uptime', + meta: { + type: 'number', + }, + }, + format: { + id: 'string', + params: {}, }, }, - format: { - id: 'string', - params: {}, - }, - }, + ], }, }; diff --git a/x-pack/examples/testing_embedded_lens/public/app.tsx b/x-pack/examples/testing_embedded_lens/public/app.tsx index cd550a9280004..75c05ed817337 100644 --- a/x-pack/examples/testing_embedded_lens/public/app.tsx +++ b/x-pack/examples/testing_embedded_lens/public/app.tsx @@ -312,7 +312,7 @@ function getLensAttributesPartition( layers: [ { primaryGroups: ['col1'], - metric: 'col2', + metrics: ['col2'], layerId: 'layer1', layerType: 'data', numberDisplay: 'percent', diff --git a/x-pack/plugins/osquery/public/live_queries/form/pack_queries_status_table.tsx b/x-pack/plugins/osquery/public/live_queries/form/pack_queries_status_table.tsx index 9ba830e96311a..4b08268288953 100644 --- a/x-pack/plugins/osquery/public/live_queries/form/pack_queries_status_table.tsx +++ b/x-pack/plugins/osquery/public/live_queries/form/pack_queries_status_table.tsx @@ -136,7 +136,7 @@ function getLensAttributes( legendDisplay: 'default', nestedLegend: false, layerId: 'layer1', - metric: 'ed999e9d-204c-465b-897f-fe1a125b39ed', + metrics: ['ed999e9d-204c-465b-897f-fe1a125b39ed'], numberDisplay: 'percent', primaryGroups: ['8690befd-fd69-4246-af4a-dd485d2a3b38'], categoryDisplay: 'default', diff --git a/x-pack/plugins/osquery/public/packs/pack_queries_status_table.tsx b/x-pack/plugins/osquery/public/packs/pack_queries_status_table.tsx index 2ba4a76c48515..c655b0e60b6c3 100644 --- a/x-pack/plugins/osquery/public/packs/pack_queries_status_table.tsx +++ b/x-pack/plugins/osquery/public/packs/pack_queries_status_table.tsx @@ -114,7 +114,7 @@ function getLensAttributes( legendDisplay: 'default', nestedLegend: false, layerId: 'layer1', - metric: 'ed999e9d-204c-465b-897f-fe1a125b39ed', + metrics: ['ed999e9d-204c-465b-897f-fe1a125b39ed'], numberDisplay: 'percent', primaryGroups: ['8690befd-fd69-4246-af4a-dd485d2a3b38'], categoryDisplay: 'default', diff --git a/x-pack/plugins/ux/public/components/app/rum_dashboard/charts/__snapshots__/visitor_breakdown_chart.test.tsx.snap b/x-pack/plugins/ux/public/components/app/rum_dashboard/charts/__snapshots__/visitor_breakdown_chart.test.tsx.snap index c3bc67a02749c..cbffd41772dda 100644 --- a/x-pack/plugins/ux/public/components/app/rum_dashboard/charts/__snapshots__/visitor_breakdown_chart.test.tsx.snap +++ b/x-pack/plugins/ux/public/components/app/rum_dashboard/charts/__snapshots__/visitor_breakdown_chart.test.tsx.snap @@ -106,7 +106,9 @@ Object { "layerId": "layer1", "layerType": "data", "legendDisplay": "hide", - "metric": "col2", + "metrics": Array [ + "col2", + ], "nestedLegend": false, "numberDisplay": "percent", "primaryGroups": Array [ diff --git a/x-pack/plugins/ux/public/components/app/rum_dashboard/charts/visitor_breakdown_chart.tsx b/x-pack/plugins/ux/public/components/app/rum_dashboard/charts/visitor_breakdown_chart.tsx index 89176ea2e21c0..c48dce5d2e319 100644 --- a/x-pack/plugins/ux/public/components/app/rum_dashboard/charts/visitor_breakdown_chart.tsx +++ b/x-pack/plugins/ux/public/components/app/rum_dashboard/charts/visitor_breakdown_chart.tsx @@ -101,7 +101,7 @@ const visConfig: PieVisualizationState = { { layerId: 'layer1', primaryGroups: ['col1'], - metric: 'col2', + metrics: ['col2'], categoryDisplay: 'default', legendDisplay: 'hide', numberDisplay: 'percent', From da38b59aed427ea24bb89c58e78b38afec4eb386 Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Mon, 12 Sep 2022 14:38:37 -0500 Subject: [PATCH 16/70] test multi-metric filtering logic --- .../public/utils/filter_helpers.test.ts | 184 +++++++++++++++++- .../public/utils/filter_helpers.ts | 3 + 2 files changed, 183 insertions(+), 4 deletions(-) diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/utils/filter_helpers.test.ts b/src/plugins/chart_expressions/expression_partition_vis/public/utils/filter_helpers.test.ts index f6b62cb7a705a..7d199de7f886f 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/utils/filter_helpers.test.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/public/utils/filter_helpers.test.ts @@ -5,16 +5,20 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import { DatatableColumn } from '@kbn/expressions-plugin/public'; +import { Datatable, DatatableColumn } from '@kbn/expressions-plugin/public'; import { getFilterClickData, getFilterEventData } from './filter_helpers'; import { createMockBucketColumns, createMockVisData } from '../mocks'; +import { consolidateMetricColumns } from '../../common/utils'; +import { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; +import { LayerValue } from '@elastic/charts'; +import faker from 'faker'; const bucketColumns = createMockBucketColumns(); const visData = createMockVisData(); describe('getFilterClickData', () => { it('returns the correct filter data for the specific layer', () => { - const clickedLayers = [ + const clickedLayers: LayerValue[] = [ { groupByRollup: 'Logstash Airways', value: 729, @@ -32,7 +36,7 @@ describe('getFilterClickData', () => { }); it('changes the filter if the user clicks on another layer', () => { - const clickedLayers = [ + const clickedLayers: LayerValue[] = [ { groupByRollup: 'ES-Air', value: 572, @@ -50,7 +54,7 @@ describe('getFilterClickData', () => { }); it('returns the correct filters for small multiples', () => { - const clickedLayers = [ + const clickedLayers: LayerValue[] = [ { groupByRollup: 'ES-Air', value: 572, @@ -78,6 +82,178 @@ describe('getFilterClickData', () => { expect(data[0].column).toEqual(0); expect(data[1].value).toEqual(1); }); + + describe('multi-metric scenarios', () => { + describe('without any original bucket columns', () => { + const originalTable: Datatable = { + type: 'datatable', + columns: [ + { + name: 'metric1', + id: '1', + meta: { + type: 'number', + }, + }, + { + name: 'metric2', + id: '2', + meta: { + type: 'number', + }, + }, + { + name: 'metric3', + id: '3', + meta: { + type: 'number', + }, + }, + ], + rows: [{ '1': 1, '2': 2, '3': 3 }], + }; + + const { table: consolidatedTable } = consolidateMetricColumns( + originalTable, + [], + ['1', '2', '3'], + { + deserialize: () => ({ + getConverterFor: () => (str: string) => `${str}`, + }), + } as FieldFormatsStart + ); + + it('generates the correct filters', () => { + const clickedLayers: LayerValue[] = [ + { + groupByRollup: 'metric2', + value: faker.random.number(), + depth: faker.random.number(), + path: [], + sortIndex: faker.random.number(), + smAccessorValue: '', + }, + ]; + + const data = getFilterClickData( + clickedLayers, + consolidatedTable.columns.slice(0, 1), + consolidatedTable, + originalTable, + 3 + ); + + expect(data).toHaveLength(1); + + const datum = data[0]; + + expect(datum.column).toBe(1); + expect(datum.row).toBe(0); + expect(datum.value).toBe('metric2'); + expect(datum.table).toBe(originalTable); + }); + }); + + describe('with original bucket columns', () => { + const originalTable: Datatable = { + type: 'datatable', + columns: [ + { name: 'bucket1', id: '0', meta: { type: 'string' } }, + { name: 'bucket2', id: '1', meta: { type: 'string' } }, + { + name: 'metric1', + id: '2', + meta: { + type: 'number', + }, + }, + { + name: 'metric2', + id: '3', + meta: { + type: 'number', + }, + }, + ], + rows: [ + { '0': 'square', '1': 'red', '2': 1, '3': 2 }, + { '0': 'square', '1': 'blue', '2': 3, '3': 4 }, + { '0': 'circle', '1': 'green', '2': 5, '3': 6 }, + { '0': 'circle', '1': 'gray', '2': 7, '3': 8 }, + ], + }; + + const { table: consolidatedTable } = consolidateMetricColumns( + originalTable, + ['0', '1'], + ['2', '3'], + { + deserialize: () => ({ + getConverterFor: () => (str: string) => `${str}`, + }), + } as FieldFormatsStart + ); + + it('generates the correct filters', () => { + const localBucketColumns = consolidatedTable.columns.slice(0, 2); + + const clickedLayers: LayerValue[] = [ + { + groupByRollup: 'circle', + value: faker.random.number(), + depth: faker.random.number(), + path: [], + sortIndex: faker.random.number(), + smAccessorValue: '', + }, + { + groupByRollup: 'green - metric2', + value: faker.random.number(), + depth: faker.random.number(), + path: [], + sortIndex: faker.random.number(), + smAccessorValue: '', + }, + ]; + + const data = getFilterClickData( + clickedLayers, + localBucketColumns, + consolidatedTable, + originalTable, + 2 + ); + + expect(data).toHaveLength(3); + + expect(data.map((datum) => ({ ...datum, table: undefined }))).toMatchInlineSnapshot(` + Array [ + Object { + "column": 0, + "row": 2, + "table": undefined, + "value": "circle", + }, + Object { + "column": 3, + "row": 2, + "table": undefined, + "value": "green - metric2", + }, + Object { + "column": 1, + "row": 2, + "table": undefined, + "value": "green - metric2", + }, + ] + `); + + expect(data.map((datum) => datum.table === originalTable).every(Boolean)).toBe(true); + }); + }); + }); }); describe('getFilterEventData', () => { diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/utils/filter_helpers.ts b/src/plugins/chart_expressions/expression_partition_vis/public/utils/filter_helpers.ts index 5130447fcadd9..baec931646cce 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/utils/filter_helpers.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/public/utils/filter_helpers.ts @@ -59,6 +59,9 @@ export const getFilterClickData = ( (col) => col.id === bucketColumns[index].id ); + // this logic maps the indices of the elements in the + // visualization's table to the indices in the table before + // any multiple metrics were collapsed into one metric column const originalColumnIndexes = currentColumn!.meta?.sourceParams?.consolidatedMetricsColumn ? // if this is the special combined column, expand it into both original columns currentColumn!.meta.sourceParams.combinedWithBucketColumn From b0461d87f6ce9f0b6fe438a12aec431cb69a4d10 Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Mon, 12 Sep 2022 14:41:34 -0500 Subject: [PATCH 17/70] fix aggs-based --- src/plugins/vis_types/pie/public/to_ast.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/vis_types/pie/public/to_ast.ts b/src/plugins/vis_types/pie/public/to_ast.ts index 91ff6b0b6c17d..47669c8a39a25 100644 --- a/src/plugins/vis_types/pie/public/to_ast.ts +++ b/src/plugins/vis_types/pie/public/to_ast.ts @@ -70,7 +70,7 @@ export const toExpressionAst: VisToExpressionAst = async (vi emptySizeRatio: vis.params.emptySizeRatio, palette: preparePalette(vis.params.palette), labels: prepareLabels(vis.params.labels), - metric: schemas.metric.map(prepareDimension), + metrics: schemas.metric.map(prepareDimension), buckets: schemas.segment?.map(prepareDimension), splitColumn: schemas.split_column?.map(prepareDimension), splitRow: schemas.split_row?.map(prepareDimension), From 3818e2191f28e296f758206350d8d851a3460e67 Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Mon, 12 Sep 2022 15:25:27 -0500 Subject: [PATCH 18/70] update pie_vis element expression --- .../plugins/canvas/canvas_plugin_src/elements/pie_vis/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/canvas/canvas_plugin_src/elements/pie_vis/index.ts b/x-pack/plugins/canvas/canvas_plugin_src/elements/pie_vis/index.ts index e7ece1155560a..52368f2718843 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/elements/pie_vis/index.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/elements/pie_vis/index.ts @@ -16,6 +16,6 @@ export const pieVis: ElementFactory = () => ({ | selectFilter | demodata | head 10 -| pieVis metric={visdimension "age"} buckets={visdimension "project"} buckets={visdimension "cost"} legendDisplay="default" +| pieVis metrics={visdimension "age"} buckets={visdimension "project"} buckets={visdimension "cost"} legendDisplay="default" | render`, }); From 0c59453ebd933cbbf4b1797f9b76f9f91967dddd Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Tue, 13 Sep 2022 14:41:10 -0500 Subject: [PATCH 19/70] update snapshot --- .../vis_types/pie/public/__snapshots__/to_ast.test.ts.snap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/vis_types/pie/public/__snapshots__/to_ast.test.ts.snap b/src/plugins/vis_types/pie/public/__snapshots__/to_ast.test.ts.snap index b9dcb3f6bff6a..253057cd8c2f5 100644 --- a/src/plugins/vis_types/pie/public/__snapshots__/to_ast.test.ts.snap +++ b/src/plugins/vis_types/pie/public/__snapshots__/to_ast.test.ts.snap @@ -67,7 +67,7 @@ Object { "legendSize": Array [ "large", ], - "metric": Array [ + "metrics": Array [ Object { "chain": Array [ Object { From a41e035cd3013344a65db318bf87f82c584fc025 Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Tue, 13 Sep 2022 14:47:09 -0500 Subject: [PATCH 20/70] suggestions --- .../partition/suggestions.test.ts | 38 ++++++++++++++++++- .../visualizations/partition/suggestions.ts | 27 +++++++------ 2 files changed, 52 insertions(+), 13 deletions(-) diff --git a/x-pack/plugins/lens/public/visualizations/partition/suggestions.test.ts b/x-pack/plugins/lens/public/visualizations/partition/suggestions.test.ts index dc367ddc362c7..d0afdc5eaf7e2 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/suggestions.test.ts +++ b/x-pack/plugins/lens/public/visualizations/partition/suggestions.test.ts @@ -674,7 +674,7 @@ describe('suggestions', () => { ).toHaveLength(0); }); - it('should reject when there are too many metrics', () => { + it('should accept multiple metrics if active visualization', () => { expect( suggestions({ table: { @@ -720,6 +720,42 @@ describe('suggestions', () => { }, keptLayerIds: ['first'], }) + ).toHaveLength(2); + }); + + it('should reject multiple metrics if not active visualization', () => { + expect( + suggestions({ + table: { + layerId: 'first', + isMultiRow: true, + columns: [ + { + columnId: 'a', + operation: { label: 'Top 5', dataType: 'string' as DataType, isBucketed: true }, + }, + { + columnId: 'b', + operation: { label: 'Top 5', dataType: 'string' as DataType, isBucketed: true }, + }, + { + columnId: 'c', + operation: { label: 'Top 5', dataType: 'string' as DataType, isBucketed: true }, + }, + { + columnId: 'd', + operation: { label: 'Avg', dataType: 'number' as DataType, isBucketed: false }, + }, + { + columnId: 'e', + operation: { label: 'Count', dataType: 'number' as DataType, isBucketed: false }, + }, + ], + changeType: 'initial', + }, + state: undefined, + keptLayerIds: ['first'], + }) ).toHaveLength(0); }); diff --git a/x-pack/plugins/lens/public/visualizations/partition/suggestions.ts b/x-pack/plugins/lens/public/visualizations/partition/suggestions.ts index 1a7501aeb159b..7e5f7b1afee2d 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/suggestions.ts +++ b/x-pack/plugins/lens/public/visualizations/partition/suggestions.ts @@ -85,6 +85,8 @@ export function suggestions({ return []; } + const isActive = Boolean(state); + const [groups, metrics] = partition( // filter out all metrics which are not number based table.columns.filter((col) => col.operation.isBucketed || col.operation.dataType === 'number'), @@ -95,12 +97,11 @@ export function suggestions({ return []; } - if (metrics.length > 1 || groups.length > maximumGroupLength) { + if ((metrics.length > 1 && !isActive) || groups.length > maximumGroupLength) { return []; } const incompleteConfiguration = metrics.length === 0 || groups.length === 0; - const metricColumnId = metrics.length > 0 ? metrics[0].columnId : undefined; if (incompleteConfiguration && state && !subVisualizationId) { // reject incomplete configurations if the sub visualization isn't specifically requested @@ -109,6 +110,8 @@ export function suggestions({ return []; } + const metricColumns = metrics.map(({ columnId }) => columnId); + const results: Array> = []; if ( @@ -132,13 +135,13 @@ export function suggestions({ ...state.layers[0], layerId: table.layerId, primaryGroups: groups.map((col) => col.columnId), - metrics: metricColumnId ? [metricColumnId] : [], + metrics: metricColumns, layerType: layerTypes.DATA, } : { layerId: table.layerId, primaryGroups: groups.map((col) => col.columnId), - metrics: metricColumnId ? [metricColumnId] : [], + metrics: metricColumns, numberDisplay: NumberDisplay.PERCENT, categoryDisplay: CategoryDisplay.DEFAULT, legendDisplay: LegendDisplay.DEFAULT, @@ -179,7 +182,7 @@ export function suggestions({ if ( groups.length <= PartitionChartsMeta.treemap.maxBuckets && - (!subVisualizationId || subVisualizationId === 'treemap') + (!subVisualizationId || subVisualizationId === PieChartTypes.TREEMAP) ) { results.push({ title: i18n.translate('xpack.lens.pie.treemapSuggestionLabel', { @@ -197,7 +200,7 @@ export function suggestions({ ...state.layers[0], layerId: table.layerId, primaryGroups: groups.map((col) => col.columnId), - metrics: metricColumnId ? [metricColumnId] : [], + metrics: metricColumns, categoryDisplay: state.layers[0].categoryDisplay === CategoryDisplay.INSIDE ? CategoryDisplay.DEFAULT @@ -207,7 +210,7 @@ export function suggestions({ : { layerId: table.layerId, primaryGroups: groups.map((col) => col.columnId), - metrics: metricColumnId ? [metricColumnId] : [], + metrics: metricColumns, numberDisplay: NumberDisplay.PERCENT, categoryDisplay: CategoryDisplay.DEFAULT, legendDisplay: LegendDisplay.DEFAULT, @@ -245,7 +248,7 @@ export function suggestions({ layerId: table.layerId, primaryGroups: groups[0] ? [groups[0].columnId] : [], secondaryGroups: groups[1] ? [groups[1].columnId] : [], - metrics: metricColumnId ? [metricColumnId] : [], + metrics: metricColumns, categoryDisplay: CategoryDisplay.DEFAULT, layerType: layerTypes.DATA, } @@ -253,7 +256,7 @@ export function suggestions({ layerId: table.layerId, primaryGroups: groups[0] ? [groups[0].columnId] : [], secondaryGroups: groups[1] ? [groups[1].columnId] : [], - metrics: metricColumnId ? [metricColumnId] : [], + metrics: metricColumns, numberDisplay: NumberDisplay.PERCENT, categoryDisplay: CategoryDisplay.DEFAULT, legendDisplay: LegendDisplay.DEFAULT, @@ -285,14 +288,14 @@ export function suggestions({ ...state.layers[0], layerId: table.layerId, primaryGroups: groups.map((col) => col.columnId), - metrics: metricColumnId ? [metricColumnId] : [], + metrics: metricColumns, categoryDisplay: CategoryDisplay.DEFAULT, layerType: layerTypes.DATA, } : { layerId: table.layerId, primaryGroups: groups.map((col) => col.columnId), - metrics: metricColumnId ? [metricColumnId] : [], + metrics: metricColumns, numberDisplay: NumberDisplay.PERCENT, categoryDisplay: CategoryDisplay.DEFAULT, legendDisplay: LegendDisplay.DEFAULT, @@ -309,7 +312,7 @@ export function suggestions({ return [...results] .map((suggestion) => ({ ...suggestion, - score: suggestion.score + 0.05 * groups.length, + score: suggestion.score + 0.05 * groups.length + 0.05 * metrics.length, })) .sort((a, b) => b.score - a.score) .map((suggestion) => ({ From eb3c77e19a6839077b02f083af569313dde52231 Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Tue, 13 Sep 2022 15:10:12 -0500 Subject: [PATCH 21/70] one more suggestion test --- .../partition/suggestions.test.ts | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/x-pack/plugins/lens/public/visualizations/partition/suggestions.test.ts b/x-pack/plugins/lens/public/visualizations/partition/suggestions.test.ts index d0afdc5eaf7e2..5009de3cd0555 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/suggestions.test.ts +++ b/x-pack/plugins/lens/public/visualizations/partition/suggestions.test.ts @@ -255,6 +255,59 @@ describe('suggestions', () => { ).toHaveLength(0); }); + it('should accept multiple metrics when active', () => { + expect( + suggestions({ + table: { + layerId: 'first', + isMultiRow: true, + columns: [ + { + columnId: 'a', + operation: { label: 'Top 5', dataType: 'string' as DataType, isBucketed: true }, + }, + { + columnId: 'b', + operation: { label: 'Top 5', dataType: 'string' as DataType, isBucketed: true }, + }, + { + columnId: 'c', + operation: { label: 'Top 5', dataType: 'string' as DataType, isBucketed: true }, + }, + { + columnId: 'd', + operation: { label: 'Avg', dataType: 'number' as DataType, isBucketed: false }, + }, + { + columnId: 'e', + operation: { label: 'Count', dataType: 'number' as DataType, isBucketed: false }, + }, + ], + changeType: 'initial', + }, + state: { + shape: PieChartTypes.PIE, + layers: [ + { + layerId: 'first', + layerType: layerTypes.DATA, + primaryGroups: ['a'], + metrics: ['b'], + numberDisplay: NumberDisplay.HIDDEN, + categoryDisplay: CategoryDisplay.INSIDE, + legendDisplay: LegendDisplay.SHOW, + percentDecimals: 0, + legendMaxLines: 1, + truncateLegend: true, + nestedLegend: true, + }, + ], + }, + keptLayerIds: ['first'], + }) + ).toHaveLength(2); + }); + it('should reject if there are no buckets and it is not a specific chart type switch', () => { expect( suggestions({ From 4c3305b5d9b76b5fbdbe4b2dd709a9643d4c1b2c Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Tue, 13 Sep 2022 15:23:27 -0500 Subject: [PATCH 22/70] rename secondary group --- .../public/visualizations/partition/visualization.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx b/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx index 3aafcb6a28bf2..8de7ac85c9adf 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx @@ -250,11 +250,11 @@ export const getPieVisualization = ({ const getMetricGroupConfig = (): VisualizationDimensionGroupConfig => ({ groupId: 'metric', - groupLabel: i18n.translate('xpack.lens.pie.groupsizeLabel', { - defaultMessage: 'Size by', + groupLabel: i18n.translate('xpack.lens.pie.groupMetricLabel', { + defaultMessage: 'Metrics', }), - dimensionEditorGroupLabel: i18n.translate('xpack.lens.pie.groupSizeLabel', { - defaultMessage: 'Size', + dimensionEditorGroupLabel: i18n.translate('xpack.lens.pie.groupMetricLabel', { + defaultMessage: 'Metrics', }), paramEditorCustomProps: { headingLabel: i18n.translate('xpack.lens.pie.headingLabel', { From 1c78548daeb761bd0c699646ad770eba7d7e6be7 Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Tue, 13 Sep 2022 17:05:54 -0500 Subject: [PATCH 23/70] handle case when column is not found --- .../public/utils/filter_helpers.ts | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/utils/filter_helpers.ts b/src/plugins/chart_expressions/expression_partition_vis/public/utils/filter_helpers.ts index baec931646cce..17c76a81344cc 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/utils/filter_helpers.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/public/utils/filter_helpers.ts @@ -52,19 +52,22 @@ export const getFilterClickData = ( const originalRowIndex = Math.floor(rowIndex / numOriginalMetrics); data.push( - ...clickedLayers + ...(clickedLayers .map((clickedLayer, index) => { const currentColumn = visData.columns.find((col) => col.id === bucketColumns[index].id); - const currentColumnIndex = visData.columns.findIndex( - (col) => col.id === bucketColumns[index].id - ); + + if (!currentColumn) { + return undefined; + } + + const currentColumnIndex = visData.columns.findIndex((col) => col === currentColumn); // this logic maps the indices of the elements in the // visualization's table to the indices in the table before // any multiple metrics were collapsed into one metric column - const originalColumnIndexes = currentColumn!.meta?.sourceParams?.consolidatedMetricsColumn + const originalColumnIndexes = currentColumn.meta?.sourceParams?.consolidatedMetricsColumn ? // if this is the special combined column, expand it into both original columns - currentColumn!.meta.sourceParams.combinedWithBucketColumn + currentColumn.meta.sourceParams.combinedWithBucketColumn ? [currentColumnIndex + 1 + (rowIndex % numOriginalMetrics), currentColumnIndex] : [currentColumnIndex + (rowIndex % numOriginalMetrics)] : [currentColumnIndex]; @@ -77,6 +80,7 @@ export const getFilterClickData = ( })); }) .flat() + .filter(Boolean) as ValueClickContext['data']['data']) ); // Allows filtering with the small multiples value From c6002d297c5f6e96ea0a993db38e9335181a69af Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Tue, 13 Sep 2022 17:07:06 -0500 Subject: [PATCH 24/70] remove unused translations --- x-pack/plugins/translations/translations/fr-FR.json | 1 - x-pack/plugins/translations/translations/ja-JP.json | 1 - x-pack/plugins/translations/translations/zh-CN.json | 1 - 3 files changed, 3 deletions(-) diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index a7aadfb61248a..d3999d901d8df 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -17714,7 +17714,6 @@ "xpack.lens.pie.addLayer": "Visualisation", "xpack.lens.pie.donutLabel": "Graphique en anneau", "xpack.lens.pie.groupLabel": "Proportion", - "xpack.lens.pie.groupsizeLabel": "Taille par", "xpack.lens.pie.mosaiclabel": "Mosaïque", "xpack.lens.pie.mosaicSuggestionLabel": "En mosaïque", "xpack.lens.pie.pielabel": "Camembert", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index fd4a320e417e1..0d46ebfad4ed6 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -17699,7 +17699,6 @@ "xpack.lens.pie.addLayer": "ビジュアライゼーション", "xpack.lens.pie.donutLabel": "ドーナッツ", "xpack.lens.pie.groupLabel": "比率", - "xpack.lens.pie.groupsizeLabel": "サイズ単位", "xpack.lens.pie.mosaiclabel": "モザイク", "xpack.lens.pie.mosaicSuggestionLabel": "モザイクとして", "xpack.lens.pie.pielabel": "円", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 8002605d93758..bf56f42c1472c 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -17721,7 +17721,6 @@ "xpack.lens.pie.addLayer": "可视化", "xpack.lens.pie.donutLabel": "圆环图", "xpack.lens.pie.groupLabel": "比例", - "xpack.lens.pie.groupsizeLabel": "大小调整依据", "xpack.lens.pie.mosaiclabel": "马赛克", "xpack.lens.pie.mosaicSuggestionLabel": "为马赛克", "xpack.lens.pie.pielabel": "饼图", From 614779747c3b7b9aacdaceb61965fb13bcf5576e Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Wed, 14 Sep 2022 11:41:46 -0500 Subject: [PATCH 25/70] add migration --- .../make_lens_embeddable_factory.ts | 2 + .../server/migrations/common_migrations.ts | 29 ++++++++++++++ .../saved_object_migrations.test.ts | 40 +++++++++++++++++++ .../migrations/saved_object_migrations.ts | 15 ++++++- 4 files changed, 85 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/lens/server/embeddable/make_lens_embeddable_factory.ts b/x-pack/plugins/lens/server/embeddable/make_lens_embeddable_factory.ts index bbac982150000..1a9e9d76bd722 100644 --- a/x-pack/plugins/lens/server/embeddable/make_lens_embeddable_factory.ts +++ b/x-pack/plugins/lens/server/embeddable/make_lens_embeddable_factory.ts @@ -31,6 +31,7 @@ import { getLensDataViewMigrations, commonMigrateMetricIds, commonMigratePartitionChartGroups, + commonMigratePartitionMetrics, } from '../migrations/common_migrations'; import { CustomVisualizationMigrations, @@ -150,6 +151,7 @@ export const makeLensEmbeddableFactory = layers: Array<{ groups?: string[] }>; }> ); + migratedLensState = commonMigratePartitionMetrics(migratedLensState); return { ...lensState, attributes: migratedLensState, diff --git a/x-pack/plugins/lens/server/migrations/common_migrations.ts b/x-pack/plugins/lens/server/migrations/common_migrations.ts index 89dca4829c583..252c9dd5c85b1 100644 --- a/x-pack/plugins/lens/server/migrations/common_migrations.ts +++ b/x-pack/plugins/lens/server/migrations/common_migrations.ts @@ -521,3 +521,32 @@ export const commonMigratePartitionChartGroups = ( layers: Array<{ primaryGroups?: string[]; secondaryGroups?: string[] }>; }>; }; + +export const commonMigratePartitionMetrics = (attributes: LensDocShape850) => { + if (attributes.visualizationType !== 'lnsPie') { + return attributes as LensDocShape850; + } + + const partitionAttributes = attributes as LensDocShape850<{ + shape: string; + layers: Array<{ metric: string }>; + }>; + + return { + ...attributes, + state: { + ...attributes.state, + visualization: { + ...partitionAttributes.state.visualization, + layers: partitionAttributes.state.visualization.layers.map((layer) => ({ + ...layer, + metrics: [layer.metric], + metric: undefined, + })), + }, + }, + } as LensDocShape850<{ + shape: string; + layers: Array<{ metrics: string[] }>; + }>; +}; diff --git a/x-pack/plugins/lens/server/migrations/saved_object_migrations.test.ts b/x-pack/plugins/lens/server/migrations/saved_object_migrations.test.ts index b07f801e53f11..4ae5668bc3952 100644 --- a/x-pack/plugins/lens/server/migrations/saved_object_migrations.test.ts +++ b/x-pack/plugins/lens/server/migrations/saved_object_migrations.test.ts @@ -2361,4 +2361,44 @@ describe('Lens migrations', () => { expect(result.attributes.visualizationType).toBe('lnsMetric'); }); }); + + describe('8.5.0 migrates partition metrics', () => { + const context = { log: { warn: () => {} } } as unknown as SavedObjectMigrationContext; + const example = { + type: 'lens', + id: 'mocked-saved-object-id', + attributes: { + savedObjectId: '1', + title: 'some title', + description: '', + visualizationType: 'lnsPie', + state: { + visualization: { + layers: [ + { + metric: 'some-metric', + }, + ], + }, + }, + }, + } as unknown as SavedObjectUnsanitizedDoc; + + it('make metric an array', () => { + const result = migrations['8.5.0'](example, context) as ReturnType< + SavedObjectMigrationFn + >; + expect( + (result.attributes.state.visualization as { layers: Array<{ metrics: string[] }> }) + .layers[0] + ).toMatchInlineSnapshot(` + Object { + "metric": undefined, + "metrics": Array [ + "some-metric", + ], + } + `); + }); + }); }); diff --git a/x-pack/plugins/lens/server/migrations/saved_object_migrations.ts b/x-pack/plugins/lens/server/migrations/saved_object_migrations.ts index e48f46ad885c5..0754642102d79 100644 --- a/x-pack/plugins/lens/server/migrations/saved_object_migrations.ts +++ b/x-pack/plugins/lens/server/migrations/saved_object_migrations.ts @@ -59,6 +59,7 @@ import { getLensDataViewMigrations, commonMigrateMetricIds, commonMigratePartitionChartGroups, + commonMigratePartitionMetrics, } from './common_migrations'; interface LensDocShapePre710 { @@ -545,6 +546,13 @@ const migratePartitionChartGroups: SavedObjectMigrationFn = ( + doc +) => ({ + ...doc, + attributes: commonMigratePartitionMetrics(doc.attributes), +}); + const lensMigrations: SavedObjectMigrationMap = { '7.7.0': removeInvalidAccessors, // The order of these migrations matter, since the timefield migration relies on the aggConfigs @@ -565,7 +573,12 @@ const lensMigrations: SavedObjectMigrationMap = { enhanceTableRowHeight ), '8.3.0': flow(lockOldMetricVisSettings, preserveOldLegendSizeDefault, fixValueLabelsInXY), - '8.5.0': flow(migrateMetricIds, addEventAnnotationType, migratePartitionChartGroups), + '8.5.0': flow( + migrateMetricIds, + addEventAnnotationType, + migratePartitionChartGroups, + migratePartitionMetrics + ), }; export const getAllMigrations = ( From aece49889bfaa6467cf5c95624e3ecb87f399cff Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Fri, 23 Sep 2022 14:32:13 -0500 Subject: [PATCH 26/70] add layer actions toggle --- x-pack/plugins/lens/common/types.ts | 1 + .../layer_actions/clone_layer_action.tsx | 1 + .../layer_actions/layer_actions.tsx | 19 ++++++++ .../layer_actions/remove_layer_action.tsx | 1 + .../config_panel/layer_actions/types.ts | 1 + .../editor_frame/config_panel/layer_panel.tsx | 3 ++ x-pack/plugins/lens/public/types.ts | 5 ++ .../partition/visualization.tsx | 48 ++++++++++++++++++- 8 files changed, 78 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/lens/common/types.ts b/x-pack/plugins/lens/common/types.ts index b6457519de9d2..96edb7b1022e9 100644 --- a/x-pack/plugins/lens/common/types.ts +++ b/x-pack/plugins/lens/common/types.ts @@ -66,6 +66,7 @@ export interface SharedPieLayerState { metrics: string[]; primaryGroups: string[]; secondaryGroups?: string[]; + allowMultipleMetrics?: boolean; collapseFns?: Record; numberDisplay: NumberDisplayType; categoryDisplay: CategoryDisplayType; diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/clone_layer_action.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/clone_layer_action.tsx index dbaa4aa0fd069..f25b8bbe044e0 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/clone_layer_action.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/clone_layer_action.tsx @@ -21,6 +21,7 @@ export const getCloneLayerAction = (props: CloneLayerAction): LayerAction => { }); return { + id: 'cloneLayerAction', execute: props.execute, displayName, isCompatible: Boolean(props.activeVisualization.cloneLayer), diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/layer_actions.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/layer_actions.tsx index 46abe207637a4..99d8719e112c3 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/layer_actions.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/layer_actions.tsx @@ -18,6 +18,7 @@ import { EuiText, EuiOutsideClickDetector, } from '@elastic/eui'; +import { StateSetter } from '../../../../types'; import type { LayerType, Visualization } from '../../../..'; import type { LayerAction } from './types'; @@ -28,10 +29,13 @@ export interface LayerActionsProps { onRemoveLayer: () => void; onCloneLayer: () => void; layerIndex: number; + layerId: string; isOnlyLayer: boolean; activeVisualization: Visualization; + visualizationState: unknown; layerType?: LayerType; core: Pick; + updateVisualization: StateSetter; } /** @internal **/ @@ -120,6 +124,21 @@ export const LayerActions = (props: LayerActionsProps) => { isOnlyLayer: props.isOnlyLayer, core: props.core, }), + ...(props.activeVisualization.getLayerActions + ? props.activeVisualization.getLayerActions(props.layerId, props.visualizationState) + : [] + ).map((partialAction) => ({ + ...partialAction, + execute: () => { + props.updateVisualization( + props.activeVisualization.onLayerAction!( + props.layerId, + partialAction.id, + props.visualizationState + ) + ); + }, + })), ].filter((i) => i.isCompatible), [props] ); diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/remove_layer_action.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/remove_layer_action.tsx index 32a18d1535697..1efcf67592a2b 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/remove_layer_action.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/remove_layer_action.tsx @@ -207,6 +207,7 @@ export const getRemoveLayerAction = (props: RemoveLayerAction): LayerAction => { ); return { + id: 'removeLayerAction', execute: async () => { const storage = new Storage(localStorage); const lensLocalStorage = storage.get(LOCAL_STORAGE_LENS_KEY) ?? {}; diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/types.ts b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/types.ts index 4614874777cc2..8e9a0af7cf206 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/types.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/types.ts @@ -8,6 +8,7 @@ import type { IconType, EuiButtonIconColor } from '@elastic/eui'; /** @internal **/ export interface LayerAction { + id: string; displayName: string; execute: () => void | Promise; icon: IconType; diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx index b3552c6fdf2e5..f440ffc149a68 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx @@ -332,12 +332,15 @@ export function LayerPanel( diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index e581064bcdbf2..6f84b7a11203f 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -50,6 +50,7 @@ import type { FormatSelectorOptions } from './indexpattern_datasource/dimension_ import type { DataViewsState } from './state_management/types'; import type { IndexPatternServiceAPI } from './data_views_service/service'; import type { Document } from './persistence/saved_object_store'; +import { LayerAction } from './editor_frame_service/editor_frame/config_panel/layer_actions/types'; export interface IndexPatternRef { id: string; @@ -1124,6 +1125,10 @@ export interface Visualization { getSuggestionFromConvertToLensContext?: ( props: VisualizationStateFromContextChangeProps ) => Suggestion; + + getLayerActions?: (layerId: string, state: T) => Array>; + + onLayerAction?: (layerId: string, actionId: string, state: T) => T; } // Use same technique as TriggerContext diff --git a/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx b/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx index 8de7ac85c9adf..f12f80451491d 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx @@ -70,6 +70,9 @@ const applyPaletteToColumnConfig = ( } }; +const ENABLE_MULTIPLE_METRICS_ACTION_ID = 'enableMultipleMetricsAction'; +const DISABLE_MULTIPLE_METRICS_ACTION_ID = 'disableMultipleMetricsAction'; + export const getPieVisualization = ({ paletteService, kibanaTheme, @@ -262,7 +265,7 @@ export const getPieVisualization = ({ }), }, accessors: layer.metrics.map((columnId) => ({ columnId })), - supportsMoreColumns: true, + supportsMoreColumns: layer.metrics.length === 0 || Boolean(layer.allowMultipleMetrics), filterOperations: numberMetricOperations, required: true, dataTestSubj: 'lnsPie_sizeByDimensionPanel', @@ -461,4 +464,47 @@ export const getPieVisualization = ({ ] : []; }, + + getLayerActions(layerId, state) { + const layerInQuestion = state.layers.find((layer) => layer.layerId === layerId); + + if (!layerInQuestion) { + return []; + } + + return layerInQuestion.allowMultipleMetrics + ? [ + { + id: DISABLE_MULTIPLE_METRICS_ACTION_ID, + displayName: 'Disable multiple metrics', + icon: 'visPie', + isCompatible: true, + }, + ] + : [ + { + id: ENABLE_MULTIPLE_METRICS_ACTION_ID, + displayName: 'Enable multiple metrics', + icon: 'visPie', + isCompatible: true, + }, + ]; + }, + + onLayerAction(layerId, actionId, state) { + return { + ...state, + layers: state.layers.map((layer) => { + const ret: PieLayerState = + layer.layerId !== layerId + ? layer + : { + ...layer, + allowMultipleMetrics: actionId === ENABLE_MULTIPLE_METRICS_ACTION_ID, + }; + + return ret; + }), + }; + }, }); From ddb2d8a30e1db2a8993061f1f5d64fabd972dd0f Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Tue, 4 Oct 2022 15:55:21 -0500 Subject: [PATCH 27/70] unify layer action strategies --- .../editor_frame/config_panel/layer_panel.tsx | 19 ++++++++---- x-pack/plugins/lens/public/types.ts | 19 ++++++------ .../partition/visualization.tsx | 2 +- .../visualizations/xy/annotations/actions.ts | 17 +++++------ .../visualizations/xy/visualization.tsx | 29 +++++++++++++++++-- 5 files changed, 58 insertions(+), 28 deletions(-) diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx index 3d1068ebd521f..e6ec434faa7cc 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx @@ -27,6 +27,7 @@ import { DragDropOperation, DropType, isOperation, + LayerAction, } from '../../../types'; import { DragDropIdentifier, ReorderProvider } from '../../../drag_drop'; import { LayerSettings } from './layer_settings'; @@ -311,16 +312,22 @@ export function LayerPanel( const [datasource] = Object.values(framePublicAPI.datasourceLayers); const isTextBasedLanguage = Boolean(datasource?.isTextBasedLanguage()); - const compatibleActions = useMemo( + const compatibleActions = useMemo( () => [ - ...(activeVisualization.getSupportedActionsForLayer?.( - layerId, - visualizationState, - updateVisualization - ) || []), + ...(activeVisualization + .getSupportedActionsForLayer?.(layerId, visualizationState) + .map((action) => ({ + ...action, + execute: () => + updateVisualization( + activeVisualization.onLayerAction?.(layerId, action.id, visualizationState) + ), + })) || []), ...getSharedActions({ + layerId, activeVisualization, + visualizationState, core, layerIndex, layerType: activeVisualization.getLayerType(layerId, visualizationState), diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index b7c65fcf4f821..9b3486c39d34e 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -512,6 +512,7 @@ export interface DatasourceDataPanelProps { /** @internal **/ export interface LayerAction { + id: string; displayName: string; description?: string; execute: () => void | Promise; @@ -521,6 +522,8 @@ export interface LayerAction { 'data-test-subj'?: string; } +export type LayerActionFromVisualization = Omit; + interface SharedDimensionProps { /** Visualizations can restrict operations based on their own rules. * For example, limiting to only bucketed or only numeric operations. @@ -981,11 +984,13 @@ export interface Visualization { * returns a list of custom actions supported by the visualization layer. * Default actions like delete/clear are not included in this list and are managed by the editor frame * */ - getSupportedActionsForLayer?: ( - layerId: string, - state: T, - setState: StateSetter - ) => LayerAction[]; + getSupportedActionsForLayer?: (layerId: string, state: T) => LayerActionFromVisualization[]; + + /** + * Perform state mutations in response to a layer action + */ + onLayerAction?: (layerId: string, actionId: string, state: T) => T; + /** returns the type string of the given layer */ getLayerType: (layerId: string, state?: T) => LayerType | undefined; /* returns the type of removal operation to perform for the specific layer in the current state */ @@ -1159,10 +1164,6 @@ export interface Visualization { getSuggestionFromConvertToLensContext?: ( props: VisualizationStateFromContextChangeProps ) => Suggestion | undefined; - - getLayerActions?: (layerId: string, state: T) => Array>; - - onLayerAction?: (layerId: string, actionId: string, state: T) => T; } // Use same technique as TriggerContext diff --git a/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx b/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx index d9655e3f660c8..5b605628b9eca 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx @@ -508,7 +508,7 @@ export const getPieVisualization = ({ : []; }, - getLayerActions(layerId, state) { + getSupportedActionsForLayer(layerId, state) { const layerInQuestion = state.layers.find((layer) => layer.layerId === layerId); if (!layerInQuestion) { diff --git a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions.ts b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions.ts index d7d3c9ca8a56a..4bd8a2f67d64d 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/annotations/actions.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/annotations/actions.ts @@ -6,20 +6,21 @@ */ import { i18n } from '@kbn/i18n'; -import type { LayerAction, StateSetter } from '../../../types'; +import type { LayerActionFromVisualization } from '../../../types'; import type { XYState, XYAnnotationLayerConfig } from '../types'; +export const IGNORE_GLOBAL_FILTERS_ACTION_ID = 'ignoreGlobalFilters'; +export const KEEP_GLOBAL_FILTERS_ACTION_ID = 'keepGlobalFilters'; + export const createAnnotationActions = ({ state, layer, layerIndex, - setState, }: { state: XYState; layer: XYAnnotationLayerConfig; layerIndex: number; - setState: StateSetter; -}): LayerAction[] => { +}): LayerActionFromVisualization[] => { const label = !layer.ignoreGlobalFilters ? i18n.translate('xpack.lens.xyChart.annotations.ignoreGlobalFiltersLabel', { defaultMessage: 'Ignore global filters', @@ -29,6 +30,9 @@ export const createAnnotationActions = ({ }); return [ { + id: !layer.ignoreGlobalFilters + ? IGNORE_GLOBAL_FILTERS_ACTION_ID + : KEEP_GLOBAL_FILTERS_ACTION_ID, displayName: label, description: !layer.ignoreGlobalFilters ? i18n.translate('xpack.lens.xyChart.annotations.ignoreGlobalFiltersDescription', { @@ -39,11 +43,6 @@ export const createAnnotationActions = ({ defaultMessage: 'All the dimensions configured in this layer respect filters defined at kibana level.', }), - execute: () => { - const newLayers = [...state.layers]; - newLayers[layerIndex] = { ...layer, ignoreGlobalFilters: !layer.ignoreGlobalFilters }; - return setState({ ...state, layers: newLayers }); - }, icon: !layer.ignoreGlobalFilters ? 'eyeClosed' : 'eye', isCompatible: true, 'data-test-subj': !layer.ignoreGlobalFilters diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx index c013f0cd1d079..ed5d0b1793702 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx @@ -38,6 +38,7 @@ import { type SeriesType, type XYSuggestion, type PersistedState, + type XYAnnotationLayerConfig, visualizationTypes, } from './types'; import { layerTypes } from '../../../common'; @@ -90,7 +91,11 @@ import { AnnotationsPanel } from './xy_config_panel/annotations_config_panel'; import { DimensionTrigger } from '../../shared_components/dimension_trigger'; import { defaultAnnotationLabel } from './annotations/helpers'; import { onDropForVisualization } from '../../editor_frame_service/editor_frame/config_panel/buttons/drop_targets_utils'; -import { createAnnotationActions } from './annotations/actions'; +import { + createAnnotationActions, + IGNORE_GLOBAL_FILTERS_ACTION_ID, + KEEP_GLOBAL_FILTERS_ACTION_ID, +} from './annotations/actions'; const XY_ID = 'lnsXY'; export const getXyVisualization = ({ @@ -241,16 +246,34 @@ export const getXyVisualization = ({ ]; }, - getSupportedActionsForLayer(layerId, state, setState) { + getSupportedActionsForLayer(layerId, state) { const layerIndex = state.layers.findIndex((l) => l.layerId === layerId); const layer = state.layers[layerIndex]; const actions = []; if (isAnnotationsLayer(layer)) { - actions.push(...createAnnotationActions({ state, layerIndex, layer, setState })); + actions.push(...createAnnotationActions({ state, layerIndex, layer })); } return actions; }, + onLayerAction(layerId, actionId, state) { + if ([IGNORE_GLOBAL_FILTERS_ACTION_ID, KEEP_GLOBAL_FILTERS_ACTION_ID].includes(actionId)) { + return { + ...state, + layers: state.layers.map((layer) => + layer.layerId === layerId + ? { + ...layer, + ignoreGlobalFilters: !(layer as XYAnnotationLayerConfig).ignoreGlobalFilters, + } + : layer + ), + }; + } + + return state; + }, + onIndexPatternChange(state, indexPatternId, layerId) { const layerIndex = state.layers.findIndex((l) => l.layerId === layerId); const layer = state.layers[layerIndex]; From 0f43752b11d4c7788d9e2b0ae30ea5537e9cfb55 Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Tue, 4 Oct 2022 16:48:25 -0500 Subject: [PATCH 28/70] clear layer when disabling multiple metrics --- .../editor_frame/config_panel/layer_panel.tsx | 9 ++- x-pack/plugins/lens/public/types.ts | 2 +- .../partition/visualization.tsx | 1 + .../visualizations/xy/visualization.test.ts | 79 +++++++++---------- 4 files changed, 47 insertions(+), 44 deletions(-) diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx index e6ec434faa7cc..963164e22b177 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx @@ -319,10 +319,15 @@ export function LayerPanel( .getSupportedActionsForLayer?.(layerId, visualizationState) .map((action) => ({ ...action, - execute: () => + execute: () => { updateVisualization( activeVisualization.onLayerAction?.(layerId, action.id, visualizationState) - ), + ); + + if (action.clearLayer) { + onRemoveLayer(); + } + }, })) || []), ...getSharedActions({ layerId, diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index 9b3486c39d34e..c3c43d08d83a6 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -522,7 +522,7 @@ export interface LayerAction { 'data-test-subj'?: string; } -export type LayerActionFromVisualization = Omit; +export type LayerActionFromVisualization = Omit & { clearLayer?: boolean }; interface SharedDimensionProps { /** Visualizations can restrict operations based on their own rules. diff --git a/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx b/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx index 5b605628b9eca..e6f10663896c8 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx @@ -522,6 +522,7 @@ export const getPieVisualization = ({ displayName: 'Disable multiple metrics', icon: 'visPie', isCompatible: true, + clearLayer: true, }, ] : [ diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts b/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts index e76633b349924..ad4c415c48a5c 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts @@ -31,6 +31,7 @@ import { DataViewsState } from '../../state_management'; import { createMockedIndexPattern } from '../../indexpattern_datasource/mocks'; import { createMockDataViewsState } from '../../data_views_service/mocks'; import { unifiedSearchPluginMock } from '@kbn/unified-search-plugin/public/mocks'; +import { KEEP_GLOBAL_FILTERS_ACTION_ID } from './annotations/actions'; const exampleAnnotation: EventAnnotationConfig = { id: 'an1', @@ -2858,33 +2859,29 @@ describe('xy_visualization', () => { }); }); - describe('getSupportedActionsForLayer', () => { + describe('layer actions', () => { it('should return no actions for a data layer', () => { - expect( - xyVisualization.getSupportedActionsForLayer?.('first', exampleState(), jest.fn()) - ).toHaveLength(0); + expect(xyVisualization.getSupportedActionsForLayer?.('first', exampleState())).toHaveLength( + 0 + ); }); it('should return one action for an annotation layer', () => { const baseState = exampleState(); expect( - xyVisualization.getSupportedActionsForLayer?.( - 'annotation', - { - ...baseState, - layers: [ - ...baseState.layers, - { - layerId: 'annotation', - layerType: layerTypes.ANNOTATIONS, - annotations: [exampleAnnotation2], - ignoreGlobalFilters: true, - indexPatternId: 'myIndexPattern', - }, - ], - }, - jest.fn() - ) + xyVisualization.getSupportedActionsForLayer?.('annotation', { + ...baseState, + layers: [ + ...baseState.layers, + { + layerId: 'annotation', + layerType: layerTypes.ANNOTATIONS, + annotations: [exampleAnnotation2], + ignoreGlobalFilters: true, + indexPatternId: 'myIndexPattern', + }, + ], + }) ).toEqual([ expect.objectContaining({ displayName: 'Keep global filters', @@ -2897,29 +2894,29 @@ describe('xy_visualization', () => { ]); }); - it('should return an action that performs a state update on click', () => { + it('should handle an annotation action', () => { const baseState = exampleState(); - const setState = jest.fn(); - const [action] = xyVisualization.getSupportedActionsForLayer?.( + const state = { + ...baseState, + layers: [ + ...baseState.layers, + { + layerId: 'annotation', + layerType: layerTypes.ANNOTATIONS, + annotations: [exampleAnnotation2], + ignoreGlobalFilters: true, + indexPatternId: 'myIndexPattern', + }, + ], + }; + + const newState = xyVisualization.onLayerAction!( 'annotation', - { - ...baseState, - layers: [ - ...baseState.layers, - { - layerId: 'annotation', - layerType: layerTypes.ANNOTATIONS, - annotations: [exampleAnnotation2], - ignoreGlobalFilters: true, - indexPatternId: 'myIndexPattern', - }, - ], - }, - setState - )!; - action.execute(); + KEEP_GLOBAL_FILTERS_ACTION_ID, + state + ); - expect(setState).toHaveBeenCalledWith( + expect(newState).toEqual( expect.objectContaining({ layers: expect.arrayContaining([ { From 66eab5a0425d31e18d11105c97c461984d06fc1f Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Wed, 5 Oct 2022 19:08:10 -0500 Subject: [PATCH 29/70] update filtering logic --- .../utils/consolidate_metric_columns.ts | 28 +---- .../components/partition_vis_component.tsx | 10 +- .../public/utils/filter_helpers.test.ts | 103 +++--------------- .../public/utils/filter_helpers.ts | 16 +-- 4 files changed, 31 insertions(+), 126 deletions(-) diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/utils/consolidate_metric_columns.ts b/src/plugins/chart_expressions/expression_partition_vis/common/utils/consolidate_metric_columns.ts index 5f9bd22a4cc3c..502e5ec89fec4 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/utils/consolidate_metric_columns.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/utils/consolidate_metric_columns.ts @@ -7,15 +7,13 @@ */ import { Datatable, DatatableColumn, DatatableRow } from '@kbn/expressions-plugin/common'; -import { getColumnByAccessor, getFormatByAccessor } from '@kbn/visualizations-plugin/common/utils'; +import { getColumnByAccessor } from '@kbn/visualizations-plugin/common/utils'; import type { ExpressionValueVisDimension } from '@kbn/visualizations-plugin/common'; -import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; export const consolidateMetricColumns = ( table: Datatable, bucketAccessors: Array = [], - metricAccessors: Array, - formatService: FieldFormatsStart + metricAccessors: Array ): { table: Datatable; metricAccessor: string | ExpressionValueVisDimension | undefined; @@ -39,17 +37,6 @@ export const consolidateMetricColumns = ( const transposedRows: DatatableRow[] = []; - const [priorBucketColumns, finalBucketColumn] = [ - bucketColumns.slice(0, bucketColumns.length - 1), - bucketColumns[bucketColumns.length - 1], - ]; - - const finalBucketFormatter = finalBucketColumn - ? formatService - .deserialize(getFormatByAccessor(finalBucketColumn.id, table.columns)) - .getConverterFor('text') - : undefined; - const nameColumnId = 'metric-name'; const valueColumnId = 'value'; @@ -57,13 +44,11 @@ export const consolidateMetricColumns = ( metricColumns.forEach((metricCol) => { const newRow: DatatableRow = {}; - priorBucketColumns.forEach(({ id }) => { + bucketColumns.forEach(({ id }) => { newRow[id] = row[id]; }); - newRow[nameColumnId] = finalBucketColumn - ? `${finalBucketFormatter!(row[finalBucketColumn.id])} - ${metricCol.name}` - : metricCol.name; + newRow[nameColumnId] = metricCol.name; newRow[valueColumnId] = row[metricCol.id]; transposedRows.push(newRow); @@ -71,7 +56,7 @@ export const consolidateMetricColumns = ( }); const transposedColumns: DatatableColumn[] = [ - ...priorBucketColumns, + ...bucketColumns, { id: nameColumnId, name: nameColumnId, @@ -79,7 +64,6 @@ export const consolidateMetricColumns = ( type: 'string', sourceParams: { consolidatedMetricsColumn: true, - combinedWithBucketColumn: Boolean(finalBucketColumn), }, }, }, @@ -94,7 +78,7 @@ export const consolidateMetricColumns = ( return { metricAccessor: valueColumnId, - bucketAccessors: [...priorBucketColumns.map(({ id }) => id), nameColumnId], + bucketAccessors: [...bucketColumns.map(({ id }) => id), nameColumnId], table: { type: 'datatable', columns: transposedColumns, diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.tsx b/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.tsx index 1cf82eac01f0b..986adb5c2370a 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.tsx +++ b/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.tsx @@ -112,15 +112,9 @@ const PartitionVisComponent = (props: PartitionVisComponentProps) => { consolidateMetricColumns( originalVisData, visParams.dimensions.buckets, - visParams.dimensions.metrics, - services.fieldFormats + visParams.dimensions.metrics ), - [ - originalVisData, - services.fieldFormats, - visParams.dimensions.buckets, - visParams.dimensions.metrics, - ] + [originalVisData, visParams.dimensions.buckets, visParams.dimensions.metrics] ); const { bucketColumns, metricColumn } = useMemo( diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/utils/filter_helpers.test.ts b/src/plugins/chart_expressions/expression_partition_vis/public/utils/filter_helpers.test.ts index 79cf40f0369a3..f79946a12c2ee 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/utils/filter_helpers.test.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/public/utils/filter_helpers.test.ts @@ -9,7 +9,6 @@ import { Datatable, DatatableColumn } from '@kbn/expressions-plugin/public'; import { getFilterClickData, getFilterEventData } from './filter_helpers'; import { createMockBucketColumns, createMockVisData } from '../mocks'; import { consolidateMetricColumns } from '../../common/utils'; -import { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; import { LayerValue } from '@elastic/charts'; import faker from 'faker'; @@ -114,83 +113,12 @@ describe('getFilterClickData', () => { }); describe('multi-metric scenarios', () => { - describe('without any original bucket columns', () => { - const originalTable: Datatable = { - type: 'datatable', - columns: [ - { - name: 'metric1', - id: '1', - meta: { - type: 'number', - }, - }, - { - name: 'metric2', - id: '2', - meta: { - type: 'number', - }, - }, - { - name: 'metric3', - id: '3', - meta: { - type: 'number', - }, - }, - ], - rows: [{ '1': 1, '2': 2, '3': 3 }], - }; - - const { table: consolidatedTable } = consolidateMetricColumns( - originalTable, - [], - ['1', '2', '3'], - { - deserialize: () => ({ - getConverterFor: () => (str: string) => `${str}`, - }), - } as FieldFormatsStart - ); - - it('generates the correct filters', () => { - const clickedLayers: LayerValue[] = [ - { - groupByRollup: 'metric2', - value: faker.random.number(), - depth: faker.random.number(), - path: [], - sortIndex: faker.random.number(), - smAccessorValue: '', - }, - ]; - - const data = getFilterClickData( - clickedLayers, - consolidatedTable.columns.slice(0, 1), - consolidatedTable, - originalTable, - 3 - ); - - expect(data).toHaveLength(1); - - const datum = data[0]; - - expect(datum.column).toBe(1); - expect(datum.row).toBe(0); - expect(datum.value).toBe('metric2'); - expect(datum.table).toBe(originalTable); - }); - }); - describe('with original bucket columns', () => { const originalTable: Datatable = { type: 'datatable', columns: [ - { name: 'bucket1', id: '0', meta: { type: 'string' } }, - { name: 'bucket2', id: '1', meta: { type: 'string' } }, + { name: 'shape', id: '0', meta: { type: 'string' } }, + { name: 'color', id: '1', meta: { type: 'string' } }, { name: 'metric1', id: '2', @@ -217,16 +145,11 @@ describe('getFilterClickData', () => { const { table: consolidatedTable } = consolidateMetricColumns( originalTable, ['0', '1'], - ['2', '3'], - { - deserialize: () => ({ - getConverterFor: () => (str: string) => `${str}`, - }), - } as FieldFormatsStart + ['2', '3'] ); it('generates the correct filters', () => { - const localBucketColumns = consolidatedTable.columns.slice(0, 2); + const localBucketColumns = consolidatedTable.columns.slice(0, 3); const clickedLayers: LayerValue[] = [ { @@ -238,7 +161,15 @@ describe('getFilterClickData', () => { smAccessorValue: '', }, { - groupByRollup: 'green - metric2', + groupByRollup: 'green', + value: faker.random.number(), + depth: faker.random.number(), + path: [], + sortIndex: faker.random.number(), + smAccessorValue: '', + }, + { + groupByRollup: 'metric2', value: faker.random.number(), depth: faker.random.number(), path: [], @@ -266,16 +197,16 @@ describe('getFilterClickData', () => { "value": "circle", }, Object { - "column": 3, + "column": 1, "row": 2, "table": undefined, - "value": "green - metric2", + "value": "green", }, Object { - "column": 1, + "column": 3, "row": 2, "table": undefined, - "value": "green - metric2", + "value": "metric2", }, ] `); diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/utils/filter_helpers.ts b/src/plugins/chart_expressions/expression_partition_vis/public/utils/filter_helpers.ts index f77f2b529bb88..9b10b0f6b2092 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/utils/filter_helpers.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/public/utils/filter_helpers.ts @@ -66,21 +66,17 @@ export const getFilterClickData = ( // this logic maps the indices of the elements in the // visualization's table to the indices in the table before // any multiple metrics were collapsed into one metric column - const originalColumnIndexes = currentColumn.meta?.sourceParams?.consolidatedMetricsColumn - ? // if this is the special combined column, expand it into both original columns - currentColumn.meta.sourceParams.combinedWithBucketColumn - ? [currentColumnIndex + 1 + (rowIndex % numOriginalMetrics), currentColumnIndex] - : [currentColumnIndex + (rowIndex % numOriginalMetrics)] - : [currentColumnIndex]; + const originalColumnIndex = currentColumn.meta?.sourceParams?.consolidatedMetricsColumn + ? currentColumnIndex + (rowIndex % numOriginalMetrics) + : currentColumnIndex; - return originalColumnIndexes.map((colIdx) => ({ - column: colIdx, + return { + column: originalColumnIndex, row: originalRowIndex, value: clickedLayer.groupByRollup, table: originalVisData, - })); + }; }) - .flat() .filter(Boolean) as ValueClickContext['data']['data']) ); From 25b33071753f763b26defece0007922356cf8cfc Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Fri, 14 Oct 2022 11:26:35 -0500 Subject: [PATCH 30/70] fake dimension signifier --- .../config_panel/layer_panel.test.tsx | 48 ++++++++++++++++ .../editor_frame/config_panel/layer_panel.tsx | 26 +++++++++ x-pack/plugins/lens/public/types.ts | 4 ++ .../partition/visualization.test.ts | 57 ++++++++++++++++++- .../partition/visualization.tsx | 36 +++++++++--- 5 files changed, 160 insertions(+), 11 deletions(-) diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx index 74394e89f0d63..5fa1eee12d873 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx @@ -706,6 +706,54 @@ describe('LayerPanel', () => { expect(mockDatasource.updateStateOnCloseDimension).toHaveBeenCalled(); expect(updateDatasource).toHaveBeenCalledWith('testDatasource', { newState: true }); }); + + it('should display the fake final accessor if present in the group config', async () => { + const fakeAccessorLabel = "I'm a fake!"; + mockVisualization.getConfiguration.mockReturnValue({ + groups: [ + { + groupLabel: 'A', + groupId: 'a', + accessors: [{ columnId: 'a' }], + filterOperations: () => true, + fakeFinalAccessor: { + label: fakeAccessorLabel, + }, + supportsMoreColumns: false, + dataTestSubj: 'lnsGroup', + }, + ], + }); + + const { instance } = await mountWithProvider(); + + expect(instance.exists('[data-test-subj="lns-fakeDimension"]')).toBeTruthy(); + expect( + instance + .find('[data-test-subj="lns-fakeDimension"] .lnsLayerPanel__triggerTextLabel') + .text() + ).toBe(fakeAccessorLabel); + }); + + it('should not display the fake final accessor if not present in the group config', async () => { + mockVisualization.getConfiguration.mockReturnValue({ + groups: [ + { + groupLabel: 'A', + groupId: 'a', + accessors: [{ columnId: 'a' }], + filterOperations: () => true, + fakeFinalAccessor: undefined, + supportsMoreColumns: false, + dataTestSubj: 'lnsGroup', + }, + ], + }); + + const { instance } = await mountWithProvider(); + + expect(instance.exists('[data-test-subj="lns-fakeDimension"]')).toBeFalsy(); + }); }); // This test is more like an integration test, since the layer panel owns all diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx index 963164e22b177..d0ee66353dd80 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx @@ -18,6 +18,7 @@ import { EuiIconTip, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { css } from '@emotion/react'; import { LayerActions } from './layer_actions'; import { IndexPatternServiceAPI } from '../../../data_views_service/service'; import { NativeRenderer } from '../../../native_renderer'; @@ -618,6 +619,31 @@ export function LayerPanel( ) : null} + {group.fakeFinalAccessor && ( +
+ + + + + {group.fakeFinalAccessor.label} + + + + +
+ )} + {group.supportsMoreColumns ? ( { }); it("doesn't count collapsed dimensions", () => { - state.layers[0].collapseFns = { + const localState = cloneDeep(state); + localState.layers[0].collapseFns = { [colIds[0]]: 'some-fn', }; - expect(pieVisualization.getErrorMessages(state)).toHaveLength(0); + expect(pieVisualization.getErrorMessages(localState)).toHaveLength(0); + }); + + it('counts multiple metrics as an extra bucket dimension', () => { + const localState = cloneDeep(state); + localState.layers[0].primaryGroups.pop(); + expect(pieVisualization.getErrorMessages(localState)).toHaveLength(0); + + localState.layers[0].metrics.push('one-metric', 'another-metric'); + + expect(pieVisualization.getErrorMessages(localState)).toHaveLength(1); }); }); }); @@ -271,5 +282,47 @@ describe('pie_visualization', () => { expect(getConfig(stateWithCollapsed).groups[0].supportsMoreColumns).toBeTruthy(); }); + + it('counts multiple metrics toward the dimension limits', () => { + const colIds = new Array(PartitionChartsMeta.pie.maxBuckets - 1) + .fill(undefined) + .map((_, i) => String(i + 1)); + + const frame = mockFrame(); + frame.datasourceLayers[LAYER_ID]!.getTableSpec = () => + colIds.map((id) => ({ columnId: id, fields: [] })); + + const state = getExampleState(); + state.layers[0].primaryGroups = colIds; + + const getConfig = (_state: PieVisualizationState) => + pieVisualization.getConfiguration({ + state: _state, + frame, + layerId: state.layers[0].layerId, + }); + + expect(getConfig(state).groups[0].supportsMoreColumns).toBeTruthy(); + + const stateWithMultipleMetrics = cloneDeep(state); + stateWithMultipleMetrics.layers[0].metrics.push('1', '2'); + + expect(getConfig(stateWithMultipleMetrics).groups[0].supportsMoreColumns).toBeFalsy(); + }); + + it.each(Object.values(PieChartTypes).filter((type) => type !== 'mosaic'))( + '%s adds fake dimension', + (type) => { + const state = { ...getExampleState(), type }; + state.layers[0].metrics.push('1', '2'); + expect( + pieVisualization.getConfiguration({ + state, + frame: mockFrame(), + layerId: state.layers[0].layerId, + }).groups[0].fakeFinalAccessor + ).toEqual({ label: '2 metrics' }); + } + ); }); }); diff --git a/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx b/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx index e6f10663896c8..7668b86bb63ce 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx @@ -171,17 +171,30 @@ export const getPieVisualization = ({ } const primaryGroupConfigBaseProps = { - requiredMinDimensionCount: 1, + requiredMinDimensionCount: layer.allowMultipleMetrics ? 0 : 1, groupId: 'primaryGroups', accessors, enableDimensionEditor: true, filterOperations: bucketedOperations, }; - const totalNonCollapsedAccessors = accessors.reduce( - (total, { columnId }) => total + (isCollapsed(columnId, layer) ? 0 : 1), - 0 - ); + const totalNonCollapsedAccessors = + accessors.reduce( + (total, { columnId }) => total + (isCollapsed(columnId, layer) ? 0 : 1), + 0 + ) + (layer.metrics.length > 1 ? 1 : 0); + + const fakeFinalAccessor = + layer.metrics.length > 1 + ? { + label: i18n.translate('xpack.lens.pie.multiMetricAccessorLabel', { + defaultMessage: '{number} metrics', + values: { + number: layer.metrics.length, + }, + }), + } + : undefined; switch (state.shape) { case 'donut': @@ -194,6 +207,7 @@ export const getPieVisualization = ({ dimensionEditorGroupLabel: i18n.translate('xpack.lens.pie.sliceDimensionGroupLabel', { defaultMessage: 'Slice', }), + fakeFinalAccessor, supportsMoreColumns: totalNonCollapsedAccessors < PartitionChartsMeta.pie.maxBuckets, dimensionsTooMany: totalNonCollapsedAccessors - PartitionChartsMeta.pie.maxBuckets, dataTestSubj: 'lnsPie_sliceByDimensionPanel', @@ -220,6 +234,7 @@ export const getPieVisualization = ({ dimensionEditorGroupLabel: i18n.translate('xpack.lens.pie.treemapDimensionGroupLabel', { defaultMessage: 'Group', }), + fakeFinalAccessor, supportsMoreColumns: totalNonCollapsedAccessors < PartitionChartsMeta[state.shape].maxBuckets, dimensionsTooMany: @@ -477,12 +492,15 @@ export const getPieVisualization = ({ getErrorMessages(state) { const hasTooManyBucketDimensions = state.layers - .map( - (layer) => + .map((layer) => { + const totalBucketDimensions = Array.from(new Set([...layer.primaryGroups, ...(layer.secondaryGroups ?? [])])).filter( (columnId) => !isCollapsed(columnId, layer) - ).length > PartitionChartsMeta[state.shape].maxBuckets - ) + ).length + + // multiple metrics counts as a dimension + (layer.metrics.length > 1 ? 1 : 0); + return totalBucketDimensions > PartitionChartsMeta[state.shape].maxBuckets; + }) .some(Boolean); return hasTooManyBucketDimensions From 78766f43250a5f0913ba9bf8f3b8f507b47b9b49 Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Fri, 14 Oct 2022 14:28:56 -0500 Subject: [PATCH 31/70] remove support for multiple metrics in mosaic --- .../mosaic_vis_function.ts | 10 ++--- .../common/types/expression_renderers.ts | 5 ++- .../visualizations/partition/to_expression.ts | 38 ++++++++-------- .../partition/visualization.test.ts | 43 +++++++++++++++++++ .../partition/visualization.tsx | 2 +- 5 files changed, 73 insertions(+), 25 deletions(-) diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/mosaic_vis_function.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/mosaic_vis_function.ts index 96d9da2c1a785..49ebb92c03684 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/mosaic_vis_function.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/mosaic_vis_function.ts @@ -25,11 +25,10 @@ export const mosaicVisFunction = (): MosaicVisExpressionFunctionDefinition => ({ inputTypes: ['datatable'], help: strings.getPieVisFunctionName(), args: { - metrics: { + metric: { types: ['string', 'vis_dimension'], help: strings.getMetricArgHelp(), required: true, - multi: true, }, buckets: { types: ['string', 'vis_dimension'], @@ -118,8 +117,7 @@ export const mosaicVisFunction = (): MosaicVisExpressionFunctionDefinition => ({ throw new Error(errors.splitRowAndSplitColumnAreSpecifiedError()); } - args.metrics.forEach((accessor) => validateAccessor(accessor, context.columns)); - + validateAccessor(args.metric, context.columns); if (args.buckets) { args.buckets.forEach((bucket) => validateAccessor(bucket, context.columns)); } @@ -138,7 +136,7 @@ export const mosaicVisFunction = (): MosaicVisExpressionFunctionDefinition => ({ handlers.getExecutionContext?.()?.description, palette: args.palette, dimensions: { - metrics: args.metrics, + metrics: [args.metric], buckets: args.buckets, splitColumn: args.splitColumn, splitRow: args.splitRow, @@ -152,7 +150,7 @@ export const mosaicVisFunction = (): MosaicVisExpressionFunctionDefinition => ({ const logTable = prepareLogTable( context, [ - [args.metrics, strings.getSliceSizeHelp()], + [[args.metric], strings.getSliceSizeHelp()], [args.buckets, strings.getSliceHelp()], [args.splitColumn, strings.getColumnSplitHelp()], [args.splitRow, strings.getRowSplitHelp()], diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/types/expression_renderers.ts b/src/plugins/chart_expressions/expression_partition_vis/common/types/expression_renderers.ts index 6f9b45e263851..302a3bbb80398 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/types/expression_renderers.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/types/expression_renderers.ts @@ -58,7 +58,6 @@ interface VisCommonParams { } interface VisCommonConfig extends VisCommonParams { - metrics: Array; splitColumn?: Array; splitRow?: Array; labels: ExpressionValuePartitionLabels; @@ -79,6 +78,7 @@ export interface PartitionVisParams extends VisCommonParams { } export interface PieVisConfig extends VisCommonConfig { + metrics: Array; buckets?: Array; partitionByColumn?: boolean; isDonut: boolean; @@ -90,16 +90,19 @@ export interface PieVisConfig extends VisCommonConfig { } export interface TreemapVisConfig extends VisCommonConfig { + metrics: Array; buckets?: Array; nestedLegend: boolean; } export interface MosaicVisConfig extends VisCommonConfig { + metric: ExpressionValueVisDimension | string; buckets?: Array; nestedLegend: boolean; } export interface WaffleVisConfig extends VisCommonConfig { + metrics: Array; bucket?: ExpressionValueVisDimension | string; showValuesInLegend: boolean; } diff --git a/x-pack/plugins/lens/public/visualizations/partition/to_expression.ts b/x-pack/plugins/lens/public/visualizations/partition/to_expression.ts index ff0925968e91d..f5bb73e3b7bea 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/to_expression.ts +++ b/x-pack/plugins/lens/public/visualizations/partition/to_expression.ts @@ -212,24 +212,28 @@ const generateTreemapVisAst: GenerateExpressionAstFunction = (...rest) => { }; }; -const generateMosaicVisAst: GenerateExpressionAstFunction = (...rest) => ({ - type: 'expression', - chain: [ - { - type: 'function', - function: 'mosaicVis', - arguments: { - ...generateCommonArguments(...rest), - // flip order of bucket dimensions so the rows are fetched before the columns to keep them stable - buckets: rest[2] - .filter(({ columnId }) => !isCollapsed(columnId, rest[3])) - .reverse() - .map((o) => o.columnId) - .map(prepareDimension), +const generateMosaicVisAst: GenerateExpressionAstFunction = (...rest) => { + const { metrics, ...args } = generateCommonArguments(...rest); + return { + type: 'expression', + chain: [ + { + type: 'function', + function: 'mosaicVis', + arguments: { + metric: metrics, + // flip order of bucket dimensions so the rows are fetched before the columns to keep them stable + buckets: rest[2] + .filter(({ columnId }) => !isCollapsed(columnId, rest[3])) + .reverse() + .map((o) => o.columnId) + .map(prepareDimension), + ...args, + }, }, - }, - ], -}); + ], + }; +}; const generateWaffleVisAst: GenerateExpressionAstFunction = (...rest) => { const { buckets, nestedLegend, ...args } = generateCommonArguments(...rest); diff --git a/x-pack/plugins/lens/public/visualizations/partition/visualization.test.ts b/x-pack/plugins/lens/public/visualizations/partition/visualization.test.ts index 20959a89d9f63..b6ae2d4741930 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/visualization.test.ts +++ b/x-pack/plugins/lens/public/visualizations/partition/visualization.test.ts @@ -101,6 +101,49 @@ describe('pie_visualization', () => { }); }); + describe('#getSupportedActionsForLayer', () => { + it('should return actions to toggle multiple metrics mode', () => { + const state = getExampleState(); + + expect(pieVisualization.getSupportedActionsForLayer!(LAYER_ID, state)).toMatchInlineSnapshot(` + Array [ + Object { + "displayName": "Enable multiple metrics", + "icon": "visPie", + "id": "enableMultipleMetricsAction", + "isCompatible": true, + }, + ] + `); + + expect( + pieVisualization.getSupportedActionsForLayer!(LAYER_ID, { + ...state, + layers: [{ ...state.layers[0], allowMultipleMetrics: true }], + }) + ).toMatchInlineSnapshot(` + Array [ + Object { + "clearLayer": true, + "displayName": "Disable multiple metrics", + "icon": "visPie", + "id": "disableMultipleMetricsAction", + "isCompatible": true, + }, + ] + `); + }); + + it('should return no actions for mosaic chart', () => { + expect( + pieVisualization.getSupportedActionsForLayer!(LAYER_ID, { + ...getExampleState(), + shape: 'mosaic', + }) + ).toEqual([]); + }); + }); + describe('#getLayerType', () => { it('should return the type only if the layer is in the state', () => { expect(pieVisualization.getLayerType(LAYER_ID, getExampleState())).toEqual(layerTypes.DATA); diff --git a/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx b/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx index 7668b86bb63ce..ad98add01a1ad 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx @@ -529,7 +529,7 @@ export const getPieVisualization = ({ getSupportedActionsForLayer(layerId, state) { const layerInQuestion = state.layers.find((layer) => layer.layerId === layerId); - if (!layerInQuestion) { + if (!layerInQuestion || state.shape === PieChartTypes.MOSAIC) { return []; } From c8c57a1f7618b706152418d9bedba3319a6ed651 Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Mon, 24 Oct 2022 09:50:13 -0500 Subject: [PATCH 32/70] fix unit tests and types --- .../mosaic_vis_function.test.ts.snap | 16 +- .../mosaic_vis_function.test.ts | 16 +- .../utils/consolidate_metric_columns.test.ts | 19 +-- .../partition_vis_component.test.tsx.snap | 154 ++++++++++++++---- .../configurations/index.test.ts | 2 +- .../public/lens/view_results_in_lens.tsx | 2 +- 6 files changed, 140 insertions(+), 69 deletions(-) diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/mosaic_vis_function.test.ts.snap b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/mosaic_vis_function.test.ts.snap index 59150e008e651..9fc8e917a46c3 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/mosaic_vis_function.test.ts.snap +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/mosaic_vis_function.test.ts.snap @@ -116,16 +116,14 @@ Object { "legendPosition": "right", "legendSize": "medium", "maxLegendLines": 2, - "metrics": Array [ - Object { - "accessor": 0, - "format": Object { - "id": "number", - "params": Object {}, - }, - "type": "vis_dimension", + "metric": Object { + "accessor": 0, + "format": Object { + "id": "number", + "params": Object {}, }, - ], + "type": "vis_dimension", + }, "nestedLegend": true, "palette": Object { "name": "kibana_palette", diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/mosaic_vis_function.test.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/mosaic_vis_function.test.ts index 5b56a8cfdafea..46816ee1f34b8 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/mosaic_vis_function.test.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/mosaic_vis_function.test.ts @@ -53,16 +53,14 @@ describe('interpreter/functions#mosaicVis', () => { truncate: 100, last_level: false, }, - metrics: [ - { - type: 'vis_dimension', - accessor: 0, - format: { - id: 'number', - params: {}, - }, + metric: { + type: 'vis_dimension', + accessor: 0, + format: { + id: 'number', + params: {}, }, - ], + }, buckets: [ { type: 'vis_dimension', diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/utils/consolidate_metric_columns.test.ts b/src/plugins/chart_expressions/expression_partition_vis/common/utils/consolidate_metric_columns.test.ts index c2983a5f9e998..fc717e0f7b6fe 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/utils/consolidate_metric_columns.test.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/utils/consolidate_metric_columns.test.ts @@ -7,16 +7,9 @@ */ import { Datatable } from '@kbn/expressions-plugin/common'; -import { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; import { consolidateMetricColumns } from './consolidate_metric_columns'; describe('consolidateMetricColumns', () => { - const formatServiceMock = { - deserialize: () => ({ - getConverterFor: () => (str: string) => `${str}`, - }), - } as FieldFormatsStart; - it('collapses multiple metrics into a single metric column', () => { const table: Datatable = { type: 'datatable', @@ -58,7 +51,7 @@ describe('consolidateMetricColumns', () => { ], }; - const result = consolidateMetricColumns(table, ['1', '2'], ['3', '4'], formatServiceMock); + const result = consolidateMetricColumns(table, ['1', '2'], ['3', '4']); expect(result.bucketAccessors).toEqual(['1', 'metric-name']); expect(result.metricAccessor).toEqual('value'); expect(result.table).toMatchInlineSnapshot(` @@ -173,12 +166,7 @@ describe('consolidateMetricColumns', () => { const bucketAccessors = ['1', '2']; const metricAccessors = ['3']; - const result = consolidateMetricColumns( - table, - bucketAccessors, - metricAccessors, - formatServiceMock - ); + const result = consolidateMetricColumns(table, bucketAccessors, metricAccessors); expect(result.table).toEqual(table); expect(result.bucketAccessors).toEqual(bucketAccessors); @@ -212,7 +200,7 @@ describe('consolidateMetricColumns', () => { ], }; - const result = consolidateMetricColumns(table, undefined, ['3', '4'], formatServiceMock); + const result = consolidateMetricColumns(table, undefined, ['3', '4']); expect(result.bucketAccessors).toEqual(['metric-name']); expect(result.metricAccessor).toEqual('value'); expect(result.table).toMatchInlineSnapshot(` @@ -222,7 +210,6 @@ describe('consolidateMetricColumns', () => { "id": "metric-name", "meta": Object { "sourceParams": Object { - "combinedWithBucketColumn": false, "consolidatedMetricsColumn": true, }, "type": "string", diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/components/__snapshots__/partition_vis_component.test.tsx.snap b/src/plugins/chart_expressions/expression_partition_vis/public/components/__snapshots__/partition_vis_component.test.tsx.snap index 7f25a2ad0ba1c..2c898e7f037f6 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/components/__snapshots__/partition_vis_component.test.tsx.snap +++ b/src/plugins/chart_expressions/expression_partition_vis/public/components/__snapshots__/partition_vis_component.test.tsx.snap @@ -860,12 +860,54 @@ exports[`PartitionVisComponent should render correct structure for multi-metric }, "name": "Carrier: Descending", }, + Object { + "format": Object { + "id": "terms", + "params": Object { + "id": "boolean", + "missingBucketLabel": "Missing", + "otherBucketLabel": "Other", + }, + }, + "id": "col-2-3", + "meta": Object { + "field": "Cancelled", + "index": "kibana_sample_data_flights", + "params": Object { + "id": "terms", + "params": Object { + "id": "boolean", + "missingBucketLabel": "Missing", + "otherBucketLabel": "Other", + }, + }, + "source": "esaggs", + "sourceParams": Object { + "enabled": true, + "id": "3", + "indexPatternId": "d3d7af60-4c81-11e8-b3d7-01146121b73d", + "params": Object { + "field": "Cancelled", + "missingBucket": false, + "missingBucketLabel": "Missing", + "order": "desc", + "orderBy": "1", + "otherBucket": false, + "otherBucketLabel": "Other", + "size": 5, + }, + "schema": "segment", + "type": "terms", + }, + "type": "boolean", + }, + "name": "Cancelled: Descending", + }, Object { "format": undefined, "id": "metric-name", "meta": Object { "sourceParams": Object { - "combinedWithBucketColumn": true, "consolidatedMetricsColumn": true, }, "type": "string", @@ -876,82 +918,98 @@ exports[`PartitionVisComponent should render correct structure for multi-metric "data": Array [ Object { "col-0-2": "Logstash Airways", - "metric-name": "0 - Count", + "col-2-3": 0, + "metric-name": "Count", "value": 797, }, Object { "col-0-2": "Logstash Airways", - "metric-name": "0 - Count", + "col-2-3": 0, + "metric-name": "Count", "value": 689, }, Object { "col-0-2": "Logstash Airways", - "metric-name": "1 - Count", + "col-2-3": 1, + "metric-name": "Count", "value": 797, }, Object { "col-0-2": "Logstash Airways", - "metric-name": "1 - Count", + "col-2-3": 1, + "metric-name": "Count", "value": 108, }, Object { "col-0-2": "JetBeats", - "metric-name": "0 - Count", + "col-2-3": 0, + "metric-name": "Count", "value": 766, }, Object { "col-0-2": "JetBeats", - "metric-name": "0 - Count", + "col-2-3": 0, + "metric-name": "Count", "value": 654, }, Object { "col-0-2": "JetBeats", - "metric-name": "1 - Count", + "col-2-3": 1, + "metric-name": "Count", "value": 766, }, Object { "col-0-2": "JetBeats", - "metric-name": "1 - Count", + "col-2-3": 1, + "metric-name": "Count", "value": 112, }, Object { "col-0-2": "ES-Air", - "metric-name": "0 - Count", + "col-2-3": 0, + "metric-name": "Count", "value": 744, }, Object { "col-0-2": "ES-Air", - "metric-name": "0 - Count", + "col-2-3": 0, + "metric-name": "Count", "value": 665, }, Object { "col-0-2": "ES-Air", - "metric-name": "1 - Count", + "col-2-3": 1, + "metric-name": "Count", "value": 744, }, Object { "col-0-2": "ES-Air", - "metric-name": "1 - Count", + "col-2-3": 1, + "metric-name": "Count", "value": 79, }, Object { "col-0-2": "Kibana Airlines", - "metric-name": "0 - Count", + "col-2-3": 0, + "metric-name": "Count", "value": 731, }, Object { "col-0-2": "Kibana Airlines", - "metric-name": "0 - Count", + "col-2-3": 0, + "metric-name": "Count", "value": 655, }, Object { "col-0-2": "Kibana Airlines", - "metric-name": "1 - Count", + "col-2-3": 1, + "metric-name": "Count", "value": 731, }, Object { "col-0-2": "Kibana Airlines", - "metric-name": "1 - Count", + "col-2-3": 1, + "metric-name": "Count", "value": 76, }, ], @@ -1070,82 +1128,98 @@ exports[`PartitionVisComponent should render correct structure for multi-metric Array [ Object { "col-0-2": "Logstash Airways", - "metric-name": "0 - Count", + "col-2-3": 0, + "metric-name": "Count", "value": 797, }, Object { "col-0-2": "Logstash Airways", - "metric-name": "0 - Count", + "col-2-3": 0, + "metric-name": "Count", "value": 689, }, Object { "col-0-2": "Logstash Airways", - "metric-name": "1 - Count", + "col-2-3": 1, + "metric-name": "Count", "value": 797, }, Object { "col-0-2": "Logstash Airways", - "metric-name": "1 - Count", + "col-2-3": 1, + "metric-name": "Count", "value": 108, }, Object { "col-0-2": "JetBeats", - "metric-name": "0 - Count", + "col-2-3": 0, + "metric-name": "Count", "value": 766, }, Object { "col-0-2": "JetBeats", - "metric-name": "0 - Count", + "col-2-3": 0, + "metric-name": "Count", "value": 654, }, Object { "col-0-2": "JetBeats", - "metric-name": "1 - Count", + "col-2-3": 1, + "metric-name": "Count", "value": 766, }, Object { "col-0-2": "JetBeats", - "metric-name": "1 - Count", + "col-2-3": 1, + "metric-name": "Count", "value": 112, }, Object { "col-0-2": "ES-Air", - "metric-name": "0 - Count", + "col-2-3": 0, + "metric-name": "Count", "value": 744, }, Object { "col-0-2": "ES-Air", - "metric-name": "0 - Count", + "col-2-3": 0, + "metric-name": "Count", "value": 665, }, Object { "col-0-2": "ES-Air", - "metric-name": "1 - Count", + "col-2-3": 1, + "metric-name": "Count", "value": 744, }, Object { "col-0-2": "ES-Air", - "metric-name": "1 - Count", + "col-2-3": 1, + "metric-name": "Count", "value": 79, }, Object { "col-0-2": "Kibana Airlines", - "metric-name": "0 - Count", + "col-2-3": 0, + "metric-name": "Count", "value": 731, }, Object { "col-0-2": "Kibana Airlines", - "metric-name": "0 - Count", + "col-2-3": 0, + "metric-name": "Count", "value": 655, }, Object { "col-0-2": "Kibana Airlines", - "metric-name": "1 - Count", + "col-2-3": 1, + "metric-name": "Count", "value": 731, }, Object { "col-0-2": "Kibana Airlines", - "metric-name": "1 - Count", + "col-2-3": 1, + "metric-name": "Count", "value": 76, }, ] @@ -1181,6 +1255,20 @@ exports[`PartitionVisComponent should render correct structure for multi-metric "showAccessor": [Function], "sortPredicate": undefined, }, + Object { + "fillLabel": Object { + "valueFont": Object { + "fontWeight": 700, + }, + }, + "groupByRollup": [Function], + "nodeLabel": [Function], + "shape": Object { + "fillColor": [Function], + }, + "showAccessor": [Function], + "sortPredicate": undefined, + }, ] } layout="sunburst" diff --git a/src/plugins/vis_types/pie/public/convert_to_lens/configurations/index.test.ts b/src/plugins/vis_types/pie/public/convert_to_lens/configurations/index.test.ts index 87ec0d3b57b3f..1f558da376e0f 100644 --- a/src/plugins/vis_types/pie/public/convert_to_lens/configurations/index.test.ts +++ b/src/plugins/vis_types/pie/public/convert_to_lens/configurations/index.test.ts @@ -32,7 +32,7 @@ describe('getConfiguration', () => { legendMaxLines: 1, legendPosition: 'right', legendSize: 'large', - metric: 'metric-1', + metrics: ['metric-1'], nestedLegend: true, numberDisplay: 'percent', percentDecimals: 2, diff --git a/x-pack/plugins/osquery/public/lens/view_results_in_lens.tsx b/x-pack/plugins/osquery/public/lens/view_results_in_lens.tsx index a61e986fd1201..b3a16e420712d 100644 --- a/x-pack/plugins/osquery/public/lens/view_results_in_lens.tsx +++ b/x-pack/plugins/osquery/public/lens/view_results_in_lens.tsx @@ -135,7 +135,7 @@ function getLensAttributes( legendDisplay: 'default', nestedLegend: false, layerId: 'layer1', - metric: 'ed999e9d-204c-465b-897f-fe1a125b39ed', + metrics: ['ed999e9d-204c-465b-897f-fe1a125b39ed'], numberDisplay: 'percent', primaryGroups: ['8690befd-fd69-4246-af4a-dd485d2a3b38'], categoryDisplay: 'default', From 46372505f78c9f07c3ec223ae75dc25a76d0fcdb Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Mon, 24 Oct 2022 13:22:17 -0500 Subject: [PATCH 33/70] fix filtering on small multiples with no bucket column --- .../utils/consolidate_metric_columns.test.ts | 34 +++++++++++++------ .../components/partition_vis_component.tsx | 3 +- .../public/utils/filter_helpers.test.ts | 26 ++++++++++++-- .../public/utils/filter_helpers.ts | 11 ++++++ 4 files changed, 60 insertions(+), 14 deletions(-) diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/utils/consolidate_metric_columns.test.ts b/src/plugins/chart_expressions/expression_partition_vis/common/utils/consolidate_metric_columns.test.ts index fc717e0f7b6fe..e8cc121e2060d 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/utils/consolidate_metric_columns.test.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/utils/consolidate_metric_columns.test.ts @@ -52,7 +52,7 @@ describe('consolidateMetricColumns', () => { }; const result = consolidateMetricColumns(table, ['1', '2'], ['3', '4']); - expect(result.bucketAccessors).toEqual(['1', 'metric-name']); + expect(result.bucketAccessors).toEqual(['1', '2', 'metric-name']); expect(result.metricAccessor).toEqual('value'); expect(result.table).toMatchInlineSnapshot(` Object { @@ -64,11 +64,17 @@ describe('consolidateMetricColumns', () => { }, "name": "bucket1", }, + Object { + "id": "2", + "meta": Object { + "type": "string", + }, + "name": "bucket2", + }, Object { "id": "metric-name", "meta": Object { "sourceParams": Object { - "combinedWithBucketColumn": true, "consolidatedMetricsColumn": true, }, "type": "string", @@ -86,42 +92,50 @@ describe('consolidateMetricColumns', () => { "rows": Array [ Object { "1": "square", - "metric-name": "red - metric1", + "2": "red", + "metric-name": "metric1", "value": 1, }, Object { "1": "square", - "metric-name": "red - metric2", + "2": "red", + "metric-name": "metric2", "value": 2, }, Object { "1": "square", - "metric-name": "blue - metric1", + "2": "blue", + "metric-name": "metric1", "value": 3, }, Object { "1": "square", - "metric-name": "blue - metric2", + "2": "blue", + "metric-name": "metric2", "value": 4, }, Object { "1": "circle", - "metric-name": "red - metric1", + "2": "red", + "metric-name": "metric1", "value": 5, }, Object { "1": "circle", - "metric-name": "red - metric2", + "2": "red", + "metric-name": "metric2", "value": 6, }, Object { "1": "circle", - "metric-name": "blue - metric1", + "2": "blue", + "metric-name": "metric1", "value": 7, }, Object { "1": "circle", - "metric-name": "blue - metric2", + "2": "blue", + "metric-name": "metric2", "value": 8, }, ], diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.tsx b/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.tsx index 986adb5c2370a..968de5e38caa7 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.tsx +++ b/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.tsx @@ -176,6 +176,7 @@ const PartitionVisComponent = (props: PartitionVisComponentProps) => { const data = getFilterClickData( clickedLayers, buckets, + metricColumn.id, vData, originalVisData, visParams.dimensions.metrics.length, @@ -184,7 +185,7 @@ const PartitionVisComponent = (props: PartitionVisComponentProps) => { ); props.fireEvent({ name: 'filter', data: { data } }); }, - [originalVisData, props, visParams.dimensions.metrics.length] + [metricColumn.id, originalVisData, props, visParams.dimensions.metrics.length] ); // handles legend action event data diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/utils/filter_helpers.test.ts b/src/plugins/chart_expressions/expression_partition_vis/public/utils/filter_helpers.test.ts index f79946a12c2ee..d8a766244dd8f 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/utils/filter_helpers.test.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/public/utils/filter_helpers.test.ts @@ -27,7 +27,14 @@ describe('getFilterClickData', () => { smAccessorValue: '', }, ]; - const data = getFilterClickData(clickedLayers, bucketColumns, visData, visData, 1); + const data = getFilterClickData( + clickedLayers, + bucketColumns, + visData.columns[1].id, + visData, + visData, + 1 + ); expect(data.length).toEqual(clickedLayers.length); expect(data[0].value).toEqual('Logstash Airways'); expect(data[0].row).toEqual(0); @@ -45,7 +52,14 @@ describe('getFilterClickData', () => { smAccessorValue: '', }, ]; - const data = getFilterClickData(clickedLayers, bucketColumns, visData, visData, 1); + const data = getFilterClickData( + clickedLayers, + bucketColumns, + visData.columns[1].id, + visData, + visData, + 1 + ); expect(data.length).toEqual(clickedLayers.length); expect(data[0].value).toEqual('ES-Air'); expect(data[0].row).toEqual(4); @@ -70,6 +84,7 @@ describe('getFilterClickData', () => { const data = getFilterClickData( clickedLayers, bucketColumns, + visData.columns[1].id, visData, visData, 1, @@ -100,6 +115,7 @@ describe('getFilterClickData', () => { const data = getFilterClickData( clickedLayers, [{ name: 'Count' }], + visData.columns[1].id, visData, visData, 1, @@ -108,8 +124,11 @@ describe('getFilterClickData', () => { expect(data.length).toEqual(2); expect(data[0].value).toEqual('Count'); expect(data[0].row).toEqual(4); - expect(data[1].column).toEqual(0); + expect(data[0].column).toEqual(1); + expect(data[1].value).toEqual('ES-Air'); + expect(data[1].row).toEqual(4); + expect(data[1].column).toEqual(0); }); describe('multi-metric scenarios', () => { @@ -181,6 +200,7 @@ describe('getFilterClickData', () => { const data = getFilterClickData( clickedLayers, localBucketColumns, + 'value', consolidatedTable, originalTable, 2 diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/utils/filter_helpers.ts b/src/plugins/chart_expressions/expression_partition_vis/public/utils/filter_helpers.ts index 9b10b0f6b2092..6855062658bc9 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/utils/filter_helpers.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/public/utils/filter_helpers.ts @@ -28,6 +28,7 @@ export const canFilter = async ( export const getFilterClickData = ( clickedLayers: LayerValue[], bucketColumns: Array>, + metricColId: string, visData: Datatable, originalVisData: Datatable, // before multiple metrics are consolidated with collapseMetricColumns numOriginalMetrics: number, @@ -82,6 +83,16 @@ export const getFilterClickData = ( // Allows filtering with the small multiples value if (splitChartDimension) { + if (!bucketColumns[0].id) { + // this is a split chart without any real bucket columns, so filter by the metric column + data.push({ + column: visData.columns.findIndex((col) => col.id === metricColId), + row: rowIndex, + table: visData, + value: visData.columns.find((col) => col.id === metricColId)?.name, + }); + } + data.push({ column: visData.columns.findIndex((col) => col.id === splitChartDimension.id), row: rowIndex, From 25f6e44886f0de2fc3d96810314032ad4b044d56 Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Tue, 25 Oct 2022 10:50:27 -0500 Subject: [PATCH 34/70] show the palette in the right places --- .../visualizations/partition/toolbar.tsx | 50 ++++++------ .../partition/visualization.test.ts | 78 +++++++++++++++++++ .../partition/visualization.tsx | 78 +++++++++++-------- 3 files changed, 149 insertions(+), 57 deletions(-) diff --git a/x-pack/plugins/lens/public/visualizations/partition/toolbar.tsx b/x-pack/plugins/lens/public/visualizations/partition/toolbar.tsx index 10d34a4f891ae..97844baba2d45 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/toolbar.tsx +++ b/x-pack/plugins/lens/public/visualizations/partition/toolbar.tsx @@ -32,7 +32,7 @@ import { import { getDefaultVisualValuesForLayer } from '../../shared_components/datasource_default_values'; import { shouldShowValuesInLegend } from './render_helpers'; import { CollapseSetting } from '../../shared_components/collapse_setting'; -import { isCollapsed } from './visualization'; +import { shouldShowPaletteOnDimension } from './visualization'; const legendOptions: Array<{ value: SharedPieLayerState['legendDisplay']; @@ -314,13 +314,9 @@ export function DimensionEditor( return null; } - const firstNonCollapsedColumnId = currentLayer.primaryGroups.find( - (columnId) => !isCollapsed(columnId, currentLayer) - ); - return ( <> - {props.accessor === firstNonCollapsedColumnId && ( + {shouldShowPaletteOnDimension(props.accessor, currentLayer) && ( )} - { - props.setState({ - ...props.state, - layers: props.state.layers.map((layer) => - layer.layerId !== props.layerId - ? layer - : { - ...layer, - collapseFns: { - ...layer.collapseFns, - [props.accessor]: collapseFn, - }, - } - ), - }); - }} - /> + {[...currentLayer.primaryGroups, ...(currentLayer.secondaryGroups ?? [])].includes( + props.accessor + ) && ( + { + props.setState({ + ...props.state, + layers: props.state.layers.map((layer) => + layer.layerId !== props.layerId + ? layer + : { + ...layer, + collapseFns: { + ...layer.collapseFns, + [props.accessor]: collapseFn, + }, + } + ), + }); + }} + /> + )} ); } diff --git a/x-pack/plugins/lens/public/visualizations/partition/visualization.test.ts b/x-pack/plugins/lens/public/visualizations/partition/visualization.test.ts index ae5a7d04d2538..45fcfd3fec970 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/visualization.test.ts +++ b/x-pack/plugins/lens/public/visualizations/partition/visualization.test.ts @@ -300,6 +300,84 @@ describe('pie_visualization', () => { `); }); + it('assigns palette to multiple metric accessors if no bucket dimension', () => { + const colIds = ['1', '2', '3', '4']; + + const frame = mockFrame(); + frame.datasourceLayers[LAYER_ID]!.getTableSpec = () => + colIds.map((id) => ({ columnId: id, fields: [] })); + + const state = getExampleState(); + state.layers[0].primaryGroups = []; + state.layers[0].metrics = colIds; + state.layers[0].allowMultipleMetrics = true; + + expect( + pieVisualization.getConfiguration({ + state, + frame, + layerId: state.layers[0].layerId, + }).groups[1].accessors + ).toMatchInlineSnapshot(` + Array [ + Object { + "columnId": "1", + "palette": Array [ + "red", + "black", + ], + "triggerIcon": "colorBy", + }, + Object { + "columnId": "2", + "palette": Array [ + "red", + "black", + ], + "triggerIcon": "colorBy", + }, + Object { + "columnId": "3", + "palette": Array [ + "red", + "black", + ], + "triggerIcon": "colorBy", + }, + Object { + "columnId": "4", + "palette": Array [ + "red", + "black", + ], + "triggerIcon": "colorBy", + }, + ] + `); + + state.layers[0].primaryGroups.push(state.layers[0].metrics.pop() as string); + + expect( + pieVisualization.getConfiguration({ + state, + frame, + layerId: state.layers[0].layerId, + }).groups[1].accessors + ).toMatchInlineSnapshot(` + Array [ + Object { + "columnId": "1", + }, + Object { + "columnId": "2", + }, + Object { + "columnId": "3", + }, + ] + `); + }); + it("doesn't count collapsed columns toward the dimension limits", () => { const colIds = new Array(PartitionChartsMeta.pie.maxBuckets) .fill(undefined) diff --git a/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx b/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx index 4e6dabc3e1ecb..0d23df753972a 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx @@ -66,25 +66,33 @@ const numberMetricOperations = (op: OperationMetadata) => export const isCollapsed = (columnId: string, layer: PieLayerState) => Boolean(layer.collapseFns?.[columnId]); -const applyPaletteToColumnConfig = ( +export const shouldShowPaletteOnDimension = (columnId: string, currentLayer: PieLayerState) => { + const firstNonCollapsedColumnId = currentLayer.primaryGroups.find( + (id) => !isCollapsed(id, currentLayer) + ); + + return ( + columnId === firstNonCollapsedColumnId || + (!firstNonCollapsedColumnId && + currentLayer.metrics.includes(columnId) && + currentLayer.allowMultipleMetrics) + ); +}; + +const applyPaletteToAccessorConfigs = ( columns: AccessorConfig[], layer: PieLayerState, palette: PieVisualizationState['palette'], paletteService: PaletteRegistry ) => { - const firstNonCollapsedColumnIdx = columns.findIndex( - (column) => !isCollapsed(column.columnId, layer) - ); - - if (firstNonCollapsedColumnIdx > -1) { - columns[firstNonCollapsedColumnIdx] = { - columnId: columns[firstNonCollapsedColumnIdx].columnId, - triggerIcon: 'colorBy', - palette: paletteService + columns.forEach((accessorConfig) => { + if (shouldShowPaletteOnDimension(accessorConfig.columnId, layer)) { + accessorConfig.triggerIcon = 'colorBy'; + accessorConfig.palette = paletteService .get(palette?.name || 'default') - .getCategoricalColors(10, palette?.params), - }; - } + .getCategoricalColors(10, palette?.params); + } + }); }; const ENABLE_MULTIPLE_METRICS_ACTION_ID = 'enableMultipleMetricsAction'; @@ -164,7 +172,7 @@ export const getPieVisualization = ({ })); if (accessors.length) { - applyPaletteToColumnConfig(accessors, layer, state.palette, paletteService); + applyPaletteToAccessorConfigs(accessors, layer, state.palette, paletteService); } const primaryGroupConfigBaseProps = { @@ -285,25 +293,31 @@ export const getPieVisualization = ({ } }; - const getMetricGroupConfig = (): VisualizationDimensionGroupConfig => ({ - groupId: 'metric', - groupLabel: i18n.translate('xpack.lens.pie.groupMetricLabel', { - defaultMessage: 'Metrics', - }), - dimensionEditorGroupLabel: i18n.translate('xpack.lens.pie.groupMetricLabel', { - defaultMessage: 'Metrics', - }), - paramEditorCustomProps: { - headingLabel: i18n.translate('xpack.lens.pie.headingLabel', { - defaultMessage: 'Value', + const getMetricGroupConfig = (): VisualizationDimensionGroupConfig => { + const accessors = layer.metrics.map((columnId) => ({ columnId })); + applyPaletteToAccessorConfigs(accessors, layer, state.palette, paletteService); + + return { + groupId: 'metric', + groupLabel: i18n.translate('xpack.lens.pie.groupMetricLabel', { + defaultMessage: 'Metrics', }), - }, - accessors: layer.metrics.map((columnId) => ({ columnId })), - supportsMoreColumns: layer.metrics.length === 0 || Boolean(layer.allowMultipleMetrics), - filterOperations: numberMetricOperations, - requiredMinDimensionCount: 1, - dataTestSubj: 'lnsPie_sizeByDimensionPanel', - }); + dimensionEditorGroupLabel: i18n.translate('xpack.lens.pie.groupMetricLabel', { + defaultMessage: 'Metrics', + }), + paramEditorCustomProps: { + headingLabel: i18n.translate('xpack.lens.pie.headingLabel', { + defaultMessage: 'Value', + }), + }, + accessors, + supportsMoreColumns: layer.metrics.length === 0 || Boolean(layer.allowMultipleMetrics), + filterOperations: numberMetricOperations, + requiredMinDimensionCount: 1, + dataTestSubj: 'lnsPie_sizeByDimensionPanel', + enableDimensionEditor: true, + }; + }; return { groups: [getPrimaryGroupConfig(), getSecondaryGroupConfig(), getMetricGroupConfig()].filter( From 11a9b3078a081d8f27c824eb519a6cf9ee273b5e Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Tue, 25 Oct 2022 11:49:34 -0500 Subject: [PATCH 35/70] fix types and unit tests --- .../convert_to_lens/configurations/index.ts | 2 +- .../convert_to_lens/types/configurations.ts | 2 +- x-pack/plugins/lens/common/types.ts | 1 - .../datasources/form_based/form_based.tsx | 1 + .../editor_frame/config_panel/layer_panel.tsx | 2 +- .../partition/suggestions.test.ts | 26 +-- .../visualizations/partition/suggestions.ts | 16 +- .../visualizations/xy/visualization.test.ts | 170 ++++++++---------- .../make_lens_embeddable_factory.ts | 3 +- .../server/migrations/common_migrations.ts | 8 +- .../migrations/saved_object_migrations.ts | 2 +- 11 files changed, 106 insertions(+), 127 deletions(-) diff --git a/src/plugins/vis_types/pie/public/convert_to_lens/configurations/index.ts b/src/plugins/vis_types/pie/public/convert_to_lens/configurations/index.ts index d1d1daf9fe009..1ff3148315fac 100644 --- a/src/plugins/vis_types/pie/public/convert_to_lens/configurations/index.ts +++ b/src/plugins/vis_types/pie/public/convert_to_lens/configurations/index.ts @@ -35,7 +35,7 @@ const getLayers = ( layerType: 'data' as const, primaryGroups: buckets, secondaryGroups: [], - metric: metrics[0], + metrics: metrics.length ? [metrics[0]] : [], numberDisplay: showValuesInLegend === false ? NumberDisplayTypes.HIDDEN 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..12e94a1c63e7f 100644 --- a/src/plugins/visualizations/common/convert_to_lens/types/configurations.ts +++ b/src/plugins/visualizations/common/convert_to_lens/types/configurations.ts @@ -219,9 +219,9 @@ export interface MetricVisConfiguration { export interface PartitionLayerState { layerId: string; layerType: LayerType; + metrics: string[]; primaryGroups: string[]; secondaryGroups?: string[]; - metric?: string; collapseFns?: Record; numberDisplay: NumberDisplayType; categoryDisplay: CategoryDisplayType; diff --git a/x-pack/plugins/lens/common/types.ts b/x-pack/plugins/lens/common/types.ts index ecffdf6e08d55..930a199cd039d 100644 --- a/x-pack/plugins/lens/common/types.ts +++ b/x-pack/plugins/lens/common/types.ts @@ -61,7 +61,6 @@ export interface SharedPieLayerState { primaryGroups: string[]; secondaryGroups?: string[]; allowMultipleMetrics?: boolean; - metrics?: string[]; collapseFns?: Record; numberDisplay: NumberDisplayType; categoryDisplay: CategoryDisplayType; diff --git a/x-pack/plugins/lens/public/datasources/form_based/form_based.tsx b/x-pack/plugins/lens/public/datasources/form_based/form_based.tsx index b863c69d7f7a6..344b3645f613a 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/form_based.tsx +++ b/x-pack/plugins/lens/public/datasources/form_based/form_based.tsx @@ -623,6 +623,7 @@ export function getFormBasedDatasource({ } return [ { + id: 'openLayerSettings', displayName: i18n.translate('xpack.lens.indexPattern.layerSettingsAction', { defaultMessage: 'Layer settings', }), diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx index ac290eb12e86b..638e06c15bbab 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx @@ -329,7 +329,7 @@ export function LayerPanel( ); if (action.clearLayer) { - onRemoveLayer(); + onRemoveLayer(layerId); } }, })) || []), diff --git a/x-pack/plugins/lens/public/visualizations/partition/suggestions.test.ts b/x-pack/plugins/lens/public/visualizations/partition/suggestions.test.ts index b64bc3e9cb8f4..e325e5563d080 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/suggestions.test.ts +++ b/x-pack/plugins/lens/public/visualizations/partition/suggestions.test.ts @@ -16,7 +16,7 @@ import { PieLayerState, PieVisualizationState, } from '../../../common'; -import { LayerTypes } from '@kbn/expression-xy-plugin/public'; +import { layerTypes } from '../../../common/layer_types'; describe('suggestions', () => { describe('pie', () => { @@ -64,7 +64,7 @@ describe('suggestions', () => { layers: [ { layerId: 'first', - layerType: LayerTypes.DATA, + layerType: layerTypes.DATA, primaryGroups: [], metrics: ['a'], numberDisplay: NumberDisplay.HIDDEN, @@ -619,7 +619,7 @@ describe('suggestions', () => { layers: [ { layerId: 'first', - layerType: LayerTypes.DATA, + layerType: layerTypes.DATA, primaryGroups: ['a'], metrics: ['b'], numberDisplay: NumberDisplay.HIDDEN, @@ -642,7 +642,7 @@ describe('suggestions', () => { layers: [ { layerId: 'first', - layerType: LayerTypes.DATA, + layerType: layerTypes.DATA, primaryGroups: ['a'], metrics: ['b'], numberDisplay: NumberDisplay.HIDDEN, @@ -675,7 +675,7 @@ describe('suggestions', () => { layers: [ { layerId: 'first', - layerType: LayerTypes.DATA, + layerType: layerTypes.DATA, primaryGroups: [], metrics: ['a'], @@ -725,7 +725,7 @@ describe('suggestions', () => { layers: [ { layerId: 'first', - layerType: LayerTypes.DATA, + layerType: layerTypes.DATA, primaryGroups: ['a', 'b'], metrics: ['e'], numberDisplay: NumberDisplay.VALUE, @@ -774,7 +774,7 @@ describe('suggestions', () => { layers: [ { layerId: 'first', - layerType: LayerTypes.DATA, + layerType: layerTypes.DATA, primaryGroups: ['a', 'b'], metrics: ['e'], numberDisplay: NumberDisplay.PERCENT, @@ -847,7 +847,7 @@ describe('suggestions', () => { layers: [ { layerId: 'first', - layerType: LayerTypes.DATA, + layerType: layerTypes.DATA, primaryGroups: ['a'], metrics: ['b'], @@ -870,7 +870,7 @@ describe('suggestions', () => { layers: [ { layerId: 'first', - layerType: LayerTypes.DATA, + layerType: layerTypes.DATA, primaryGroups: ['a'], metrics: ['b'], @@ -904,7 +904,7 @@ describe('suggestions', () => { layers: [ { layerId: 'first', - layerType: LayerTypes.DATA, + layerType: layerTypes.DATA, primaryGroups: [], metrics: ['a'], @@ -946,7 +946,7 @@ describe('suggestions', () => { layers: [ { layerId: 'first', - layerType: LayerTypes.DATA, + layerType: layerTypes.DATA, primaryGroups: ['a', 'b'], metrics: ['c'], @@ -981,7 +981,7 @@ describe('suggestions', () => { layers: [ { layerId: 'first', - layerType: LayerTypes.DATA, + layerType: layerTypes.DATA, primaryGroups: [], metrics: ['a'], @@ -1019,7 +1019,7 @@ describe('suggestions', () => { layers: [ { layerId: 'first', - layerType: LayerTypes.DATA, + layerType: layerTypes.DATA, primaryGroups: ['a', 'b'], metrics: ['c'], numberDisplay: NumberDisplay.HIDDEN, diff --git a/x-pack/plugins/lens/public/visualizations/partition/suggestions.ts b/x-pack/plugins/lens/public/visualizations/partition/suggestions.ts index b6b1a55a454ff..a9777e2678fe6 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/suggestions.ts +++ b/x-pack/plugins/lens/public/visualizations/partition/suggestions.ts @@ -7,7 +7,6 @@ import { partition } from 'lodash'; import { i18n } from '@kbn/i18n'; -import { LayerTypes } from '@kbn/expression-xy-plugin/public'; import type { SuggestionRequest, TableSuggestionColumn, @@ -23,6 +22,7 @@ import { import { isPartitionShape } from '../../../common/visualizations'; import type { PieChartType } from '../../../common/types'; import { PartitionChartsMeta } from './partition_charts_meta'; +import { layerTypes } from '../..'; function hasIntervalScale(columns: TableSuggestionColumn[]) { return columns.some((col) => col.operation.scale === 'interval'); @@ -145,7 +145,7 @@ export function suggestions({ categoryDisplay: CategoryDisplay.DEFAULT, legendDisplay: LegendDisplay.DEFAULT, nestedLegend: false, - layerType: LayerTypes.DATA, + layerType: layerTypes.DATA, }, ], }, @@ -204,7 +204,7 @@ export function suggestions({ state.layers[0].categoryDisplay === CategoryDisplay.INSIDE ? CategoryDisplay.DEFAULT : state.layers[0].categoryDisplay, - layerType: LayerTypes.DATA, + layerType: layerTypes.DATA, } : { layerId: table.layerId, @@ -214,7 +214,7 @@ export function suggestions({ categoryDisplay: CategoryDisplay.DEFAULT, legendDisplay: LegendDisplay.DEFAULT, nestedLegend: false, - layerType: LayerTypes.DATA, + layerType: layerTypes.DATA, }, ], }, @@ -249,7 +249,7 @@ export function suggestions({ secondaryGroups: groups[1] ? [groups[1].columnId] : [], metrics: metricColumns, categoryDisplay: CategoryDisplay.DEFAULT, - layerType: LayerTypes.DATA, + layerType: layerTypes.DATA, } : { layerId: table.layerId, @@ -260,7 +260,7 @@ export function suggestions({ categoryDisplay: CategoryDisplay.DEFAULT, legendDisplay: LegendDisplay.DEFAULT, nestedLegend: false, - layerType: LayerTypes.DATA, + layerType: layerTypes.DATA, }, ], }, @@ -290,7 +290,7 @@ export function suggestions({ metrics: metricColumns, secondaryGroups: [], categoryDisplay: CategoryDisplay.DEFAULT, - layerType: LayerTypes.DATA, + layerType: layerTypes.DATA, } : { layerId: table.layerId, @@ -300,7 +300,7 @@ export function suggestions({ categoryDisplay: CategoryDisplay.DEFAULT, legendDisplay: LegendDisplay.DEFAULT, nestedLegend: false, - layerType: LayerTypes.DATA, + layerType: layerTypes.DATA, }, ], }, diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts b/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts index 4e6af0d4ff2d6..e1fe1d9c7f4cb 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts @@ -16,7 +16,6 @@ import type { XYReferenceLineLayerConfig, SeriesType, } from './types'; -import { LayerTypes } from '@kbn/expression-xy-plugin/public'; import { createMockDatasource, createMockFramePublicAPI } from '../../mocks'; import { IconChartBar } from '@kbn/chart-icons'; import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; @@ -32,6 +31,7 @@ import { createMockedIndexPattern } from '../../datasources/form_based/mocks'; import { createMockDataViewsState } from '../../data_views_service/mocks'; import { unifiedSearchPluginMock } from '@kbn/unified-search-plugin/public/mocks'; import { KEEP_GLOBAL_FILTERS_ACTION_ID } from './annotations/actions'; +import { layerTypes } from '../..'; const exampleAnnotation: EventAnnotationConfig = { id: 'an1', @@ -62,7 +62,7 @@ function exampleState(): XYState { layers: [ { layerId: 'first', - layerType: LayerTypes.DATA, + layerType: layerTypes.DATA, seriesType: 'area', splitAccessor: 'd', xAccessor: 'a', @@ -222,7 +222,7 @@ describe('xy_visualization', () => { ...exampleState().layers, { layerId: 'second', - layerType: LayerTypes.DATA, + layerType: layerTypes.DATA, seriesType: 'area', splitAccessor: 'e', xAccessor: 'f', @@ -240,7 +240,7 @@ describe('xy_visualization', () => { const layers = xyVisualization.appendLayer!( exampleState(), 'foo', - LayerTypes.DATA, + layerTypes.DATA, 'indexPattern1' ).layers; expect(layers.length).toEqual(exampleState().layers.length + 1); @@ -330,7 +330,7 @@ describe('xy_visualization', () => { describe('#getLayerType', () => { it('should return the type only if the layer is in the state', () => { - expect(xyVisualization.getLayerType('first', exampleState())).toEqual(LayerTypes.DATA); + expect(xyVisualization.getLayerType('first', exampleState())).toEqual(layerTypes.DATA); expect(xyVisualization.getLayerType('foo', exampleState())).toBeUndefined(); }); }); @@ -379,7 +379,7 @@ describe('xy_visualization', () => { layers: [ { layerId: 'first', - layerType: LayerTypes.DATA, + layerType: layerTypes.DATA, seriesType: 'area', xAccessor: undefined, accessors: [], @@ -392,7 +392,7 @@ describe('xy_visualization', () => { }).layers[0] ).toEqual({ layerId: 'first', - layerType: LayerTypes.DATA, + layerType: layerTypes.DATA, seriesType: 'area', xAccessor: 'newCol', accessors: [], @@ -408,7 +408,7 @@ describe('xy_visualization', () => { layers: [ { layerId: 'first', - layerType: LayerTypes.DATA, + layerType: layerTypes.DATA, seriesType: 'area', xAccessor: 'a', accessors: [], @@ -421,7 +421,7 @@ describe('xy_visualization', () => { }).layers[0] ).toEqual({ layerId: 'first', - layerType: LayerTypes.DATA, + layerType: layerTypes.DATA, seriesType: 'area', xAccessor: 'newCol', accessors: [], @@ -437,7 +437,7 @@ describe('xy_visualization', () => { layers: [ { layerId: 'referenceLine', - layerType: LayerTypes.REFERENCELINE, + layerType: layerTypes.REFERENCELINE, accessors: [], }, ], @@ -448,7 +448,7 @@ describe('xy_visualization', () => { }).layers[0] ).toEqual({ layerId: 'referenceLine', - layerType: LayerTypes.REFERENCELINE, + layerType: layerTypes.REFERENCELINE, accessors: ['newCol'], yConfig: [ { @@ -469,7 +469,7 @@ describe('xy_visualization', () => { layers: [ { layerId: 'annotation', - layerType: LayerTypes.ANNOTATIONS, + layerType: layerTypes.ANNOTATIONS, indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], ignoreGlobalFilters: true, @@ -482,7 +482,7 @@ describe('xy_visualization', () => { }).layers[0] ).toEqual({ layerId: 'annotation', - layerType: LayerTypes.ANNOTATIONS, + layerType: layerTypes.ANNOTATIONS, indexPatternId: 'indexPattern1', ignoreGlobalFilters: true, annotations: [ @@ -718,7 +718,7 @@ describe('xy_visualization', () => { layers: [ { layerId: 'annotation', - layerType: LayerTypes.ANNOTATIONS, + layerType: layerTypes.ANNOTATIONS, indexPatternId: 'indexPattern1', annotations: [exampleAnnotation2], ignoreGlobalFilters: true, @@ -747,7 +747,7 @@ describe('xy_visualization', () => { }).layers[0] ).toEqual({ layerId: 'annotation', - layerType: LayerTypes.ANNOTATIONS, + layerType: layerTypes.ANNOTATIONS, indexPatternId: 'indexPattern1', annotations: [ exampleAnnotation2, @@ -778,7 +778,7 @@ describe('xy_visualization', () => { layers: [ { layerId: 'annotation', - layerType: LayerTypes.ANNOTATIONS, + layerType: layerTypes.ANNOTATIONS, indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], ignoreGlobalFilters: true, @@ -807,7 +807,7 @@ describe('xy_visualization', () => { }).layers[0] ).toEqual({ layerId: 'annotation', - layerType: LayerTypes.ANNOTATIONS, + layerType: layerTypes.ANNOTATIONS, indexPatternId: 'indexPattern1', annotations: [ { @@ -838,7 +838,7 @@ describe('xy_visualization', () => { layers: [ { layerId: 'annotation', - layerType: LayerTypes.ANNOTATIONS, + layerType: layerTypes.ANNOTATIONS, indexPatternId: 'indexPattern1', annotations: [exampleAnnotation2], ignoreGlobalFilters: true, @@ -864,7 +864,7 @@ describe('xy_visualization', () => { }).layers[0] ).toEqual({ layerId: 'annotation', - layerType: LayerTypes.ANNOTATIONS, + layerType: layerTypes.ANNOTATIONS, indexPatternId: 'indexPattern1', annotations: [exampleAnnotation2, { ...exampleAnnotation2, id: 'newColId' }], ignoreGlobalFilters: true, @@ -879,7 +879,7 @@ describe('xy_visualization', () => { layers: [ { layerId: 'annotation', - layerType: LayerTypes.ANNOTATIONS, + layerType: layerTypes.ANNOTATIONS, indexPatternId: 'indexPattern1', annotations: [exampleAnnotation, exampleAnnotation2], ignoreGlobalFilters: true, @@ -906,7 +906,7 @@ describe('xy_visualization', () => { }).layers[0] ).toEqual({ layerId: 'annotation', - layerType: LayerTypes.ANNOTATIONS, + layerType: layerTypes.ANNOTATIONS, indexPatternId: 'indexPattern1', annotations: [exampleAnnotation2, exampleAnnotation], ignoreGlobalFilters: true, @@ -922,14 +922,14 @@ describe('xy_visualization', () => { layers: [ { layerId: 'first', - layerType: LayerTypes.ANNOTATIONS, + layerType: layerTypes.ANNOTATIONS, indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], ignoreGlobalFilters: true, }, { layerId: 'second', - layerType: LayerTypes.ANNOTATIONS, + layerType: layerTypes.ANNOTATIONS, indexPatternId: 'indexPattern1', annotations: [exampleAnnotation2], ignoreGlobalFilters: true, @@ -957,14 +957,14 @@ describe('xy_visualization', () => { ).toEqual([ { layerId: 'first', - layerType: LayerTypes.ANNOTATIONS, + layerType: layerTypes.ANNOTATIONS, indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], ignoreGlobalFilters: true, }, { layerId: 'second', - layerType: LayerTypes.ANNOTATIONS, + layerType: layerTypes.ANNOTATIONS, indexPatternId: 'indexPattern1', annotations: [{ ...exampleAnnotation, id: 'an2' }], ignoreGlobalFilters: true, @@ -980,14 +980,14 @@ describe('xy_visualization', () => { layers: [ { layerId: 'first', - layerType: LayerTypes.ANNOTATIONS, + layerType: layerTypes.ANNOTATIONS, indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], ignoreGlobalFilters: true, }, { layerId: 'second', - layerType: LayerTypes.ANNOTATIONS, + layerType: layerTypes.ANNOTATIONS, indexPatternId: 'indexPattern1', annotations: [exampleAnnotation2], ignoreGlobalFilters: true, @@ -1015,14 +1015,14 @@ describe('xy_visualization', () => { ).toEqual([ { layerId: 'first', - layerType: LayerTypes.ANNOTATIONS, + layerType: layerTypes.ANNOTATIONS, indexPatternId: 'indexPattern1', annotations: [exampleAnnotation2], ignoreGlobalFilters: true, }, { layerId: 'second', - layerType: LayerTypes.ANNOTATIONS, + layerType: layerTypes.ANNOTATIONS, indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], ignoreGlobalFilters: true, @@ -1038,14 +1038,14 @@ describe('xy_visualization', () => { layers: [ { layerId: 'first', - layerType: LayerTypes.ANNOTATIONS, + layerType: layerTypes.ANNOTATIONS, indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], ignoreGlobalFilters: true, }, { layerId: 'second', - layerType: LayerTypes.ANNOTATIONS, + layerType: layerTypes.ANNOTATIONS, indexPatternId: 'indexPattern1', annotations: [exampleAnnotation2], ignoreGlobalFilters: true, @@ -1072,14 +1072,14 @@ describe('xy_visualization', () => { ).toEqual([ { layerId: 'first', - layerType: LayerTypes.ANNOTATIONS, + layerType: layerTypes.ANNOTATIONS, indexPatternId: 'indexPattern1', annotations: [], ignoreGlobalFilters: true, }, { layerId: 'second', - layerType: LayerTypes.ANNOTATIONS, + layerType: layerTypes.ANNOTATIONS, indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], ignoreGlobalFilters: true, @@ -1095,14 +1095,14 @@ describe('xy_visualization', () => { layers: [ { layerId: 'first', - layerType: LayerTypes.ANNOTATIONS, + layerType: layerTypes.ANNOTATIONS, indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], ignoreGlobalFilters: true, }, { layerId: 'second', - layerType: LayerTypes.ANNOTATIONS, + layerType: layerTypes.ANNOTATIONS, indexPatternId: 'indexPattern1', annotations: [], ignoreGlobalFilters: true, @@ -1130,14 +1130,14 @@ describe('xy_visualization', () => { ).toEqual([ { layerId: 'first', - layerType: LayerTypes.ANNOTATIONS, + layerType: layerTypes.ANNOTATIONS, indexPatternId: 'indexPattern1', annotations: [], ignoreGlobalFilters: true, }, { layerId: 'second', - layerType: LayerTypes.ANNOTATIONS, + layerType: layerTypes.ANNOTATIONS, indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], ignoreGlobalFilters: true, @@ -1185,7 +1185,7 @@ describe('xy_visualization', () => { layers: [ { layerId: 'first', - layerType: LayerTypes.DATA, + layerType: layerTypes.DATA, seriesType: 'area', xAccessor: 'a', accessors: [], @@ -1197,7 +1197,7 @@ describe('xy_visualization', () => { }).layers[0] ).toEqual({ layerId: 'first', - layerType: LayerTypes.DATA, + layerType: layerTypes.DATA, seriesType: 'area', xAccessor: undefined, accessors: [], @@ -1212,14 +1212,14 @@ describe('xy_visualization', () => { layers: [ { layerId: 'first', - layerType: LayerTypes.DATA, + layerType: layerTypes.DATA, seriesType: 'area', xAccessor: 'a', accessors: [], }, { layerId: 'ann', - layerType: LayerTypes.ANNOTATIONS, + layerType: layerTypes.ANNOTATIONS, indexPatternId: 'indexPattern1', annotations: [exampleAnnotation, { ...exampleAnnotation, id: 'an2' }], ignoreGlobalFilters: true, @@ -1232,14 +1232,14 @@ describe('xy_visualization', () => { ).toEqual([ { layerId: 'first', - layerType: LayerTypes.DATA, + layerType: layerTypes.DATA, seriesType: 'area', xAccessor: 'a', accessors: [], }, { layerId: 'ann', - layerType: LayerTypes.ANNOTATIONS, + layerType: layerTypes.ANNOTATIONS, indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], ignoreGlobalFilters: true, @@ -1535,7 +1535,7 @@ describe('xy_visualization', () => { ...baseState.layers[0], accessors: ['e'], seriesType: 'bar_percentage_stacked', - layerType: LayerTypes.REFERENCELINE, + layerType: layerTypes.REFERENCELINE, }, ], ], @@ -1602,7 +1602,7 @@ describe('xy_visualization', () => { layers: [ { layerId: 'first', - layerType: LayerTypes.DATA, + layerType: layerTypes.DATA, seriesType: 'area', splitAccessor: undefined, xAccessor: undefined, @@ -1610,7 +1610,7 @@ describe('xy_visualization', () => { }, { layerId: 'referenceLine', - layerType: LayerTypes.REFERENCELINE, + layerType: layerTypes.REFERENCELINE, accessors: [], yConfig: [{ axisMode: 'left', forAccessor: 'a' }], }, @@ -1958,7 +1958,7 @@ describe('xy_visualization', () => { layers: [ { layerId: 'first', - layerType: LayerTypes.DATA, + layerType: layerTypes.DATA, seriesType: 'area', splitAccessor: undefined, xAccessor: 'a', @@ -1966,7 +1966,7 @@ describe('xy_visualization', () => { }, { layerId: 'annotations', - layerType: LayerTypes.ANNOTATIONS, + layerType: layerTypes.ANNOTATIONS, indexPatternId: 'indexPattern1', annotations: [exampleAnnotation], ignoreGlobalFilters: true, @@ -2190,7 +2190,7 @@ describe('xy_visualization', () => { layers: [ { layerId: 'first', - layerType: LayerTypes.DATA, + layerType: layerTypes.DATA, seriesType: 'area', xAccessor: 'a', accessors: [], @@ -2206,14 +2206,14 @@ describe('xy_visualization', () => { layers: [ { layerId: 'first', - layerType: LayerTypes.DATA, + layerType: layerTypes.DATA, seriesType: 'area', xAccessor: 'a', accessors: [], }, { layerId: 'second', - layerType: LayerTypes.DATA, + layerType: layerTypes.DATA, seriesType: 'area', xAccessor: 'a', accessors: [], @@ -2229,14 +2229,14 @@ describe('xy_visualization', () => { layers: [ { layerId: 'first', - layerType: LayerTypes.DATA, + layerType: layerTypes.DATA, seriesType: 'area', xAccessor: 'a', accessors: ['a'], }, { layerId: 'second', - layerType: LayerTypes.DATA, + layerType: layerTypes.DATA, seriesType: 'area', xAccessor: undefined, accessors: ['a'], @@ -2253,7 +2253,7 @@ describe('xy_visualization', () => { layers: [ { layerId: 'first', - layerType: LayerTypes.DATA, + layerType: layerTypes.DATA, seriesType: 'area', xAccessor: undefined, accessors: [], @@ -2269,7 +2269,7 @@ describe('xy_visualization', () => { layers: [ { layerId: 'first', - layerType: LayerTypes.DATA, + layerType: layerTypes.DATA, seriesType: 'area', xAccessor: undefined, accessors: [], @@ -2277,7 +2277,7 @@ describe('xy_visualization', () => { }, { layerId: 'second', - layerType: LayerTypes.DATA, + layerType: layerTypes.DATA, seriesType: 'area', xAccessor: undefined, accessors: [], @@ -2294,14 +2294,14 @@ describe('xy_visualization', () => { layers: [ { layerId: 'first', - layerType: LayerTypes.DATA, + layerType: layerTypes.DATA, seriesType: 'area', xAccessor: 'a', accessors: [], }, { layerId: 'second', - layerType: LayerTypes.DATA, + layerType: layerTypes.DATA, seriesType: 'area', xAccessor: undefined, accessors: ['a'], @@ -2322,14 +2322,14 @@ describe('xy_visualization', () => { layers: [ { layerId: 'first', - layerType: LayerTypes.DATA, + layerType: layerTypes.DATA, seriesType: 'area', xAccessor: 'a', accessors: ['a'], }, { layerId: 'second', - layerType: LayerTypes.DATA, + layerType: layerTypes.DATA, seriesType: 'area', xAccessor: undefined, accessors: [], @@ -2337,7 +2337,7 @@ describe('xy_visualization', () => { }, { layerId: 'third', - layerType: LayerTypes.DATA, + layerType: layerTypes.DATA, seriesType: 'area', xAccessor: undefined, accessors: [], @@ -2359,21 +2359,21 @@ describe('xy_visualization', () => { layers: [ { layerId: 'first', - layerType: LayerTypes.DATA, + layerType: layerTypes.DATA, seriesType: 'area', xAccessor: 'a', accessors: [], }, { layerId: 'second', - layerType: LayerTypes.DATA, + layerType: layerTypes.DATA, seriesType: 'area', xAccessor: 'a', accessors: ['a'], }, { layerId: 'third', - layerType: LayerTypes.DATA, + layerType: layerTypes.DATA, seriesType: 'area', xAccessor: 'a', accessors: ['a'], @@ -2396,7 +2396,7 @@ describe('xy_visualization', () => { layers: [ { layerId: 'first', - layerType: LayerTypes.DATA, + layerType: layerTypes.DATA, seriesType: 'area', splitAccessor: 'd', xAccessor: 'a', @@ -2444,7 +2444,7 @@ describe('xy_visualization', () => { layers: [ { layerId: 'first', - layerType: LayerTypes.DATA, + layerType: layerTypes.DATA, seriesType: 'area', splitAccessor: 'd', xAccessor: 'a', @@ -2452,7 +2452,7 @@ describe('xy_visualization', () => { }, { layerId: 'second', - layerType: LayerTypes.DATA, + layerType: layerTypes.DATA, seriesType: 'area', splitAccessor: 'd', xAccessor: 'e', @@ -2500,7 +2500,7 @@ describe('xy_visualization', () => { layers: [ { layerId: 'first', - layerType: LayerTypes.DATA, + layerType: layerTypes.DATA, seriesType: 'area', splitAccessor: 'd', xAccessor: 'a', @@ -2508,7 +2508,7 @@ describe('xy_visualization', () => { }, { layerId: 'second', - layerType: LayerTypes.DATA, + layerType: layerTypes.DATA, seriesType: 'area', splitAccessor: 'd', xAccessor: 'e', @@ -2678,7 +2678,7 @@ describe('xy_visualization', () => { layers: [ { layerId: 'first', - layerType: LayerTypes.DATA, + layerType: layerTypes.DATA, seriesType: 'area', xAccessor: 'a', accessors: ['b'], @@ -2798,7 +2798,7 @@ describe('xy_visualization', () => { ...baseState.layers, { layerId: 'annotation', - layerType: LayerTypes.ANNOTATIONS, + layerType: layerTypes.ANNOTATIONS, annotations: [exampleAnnotation2], ignoreGlobalFilters: true, }, @@ -2818,7 +2818,7 @@ describe('xy_visualization', () => { ...baseState.layers, { layerId: 'annotation', - layerType: LayerTypes.ANNOTATIONS, + layerType: layerTypes.ANNOTATIONS, indexPatternId: 'indexPattern1', annotations: [exampleAnnotation2], ignoreGlobalFilters: true, @@ -2837,7 +2837,7 @@ describe('xy_visualization', () => { ...baseState.layers, { layerId: 'annotation', - layerType: LayerTypes.ANNOTATIONS, + layerType: layerTypes.ANNOTATIONS, annotations: [exampleAnnotation2], ignoreGlobalFilters: true, }, @@ -2857,7 +2857,7 @@ describe('xy_visualization', () => { ...baseState.layers, { layerId: 'annotation', - layerType: LayerTypes.ANNOTATIONS, + layerType: layerTypes.ANNOTATIONS, indexPatternId: 'indexPattern1', annotations: [exampleAnnotation2], ignoreGlobalFilters: true, @@ -2877,7 +2877,6 @@ describe('xy_visualization', () => { it('should return one action for an annotation layer', () => { const baseState = exampleState(); expect( -<<<<<<< HEAD xyVisualization.getSupportedActionsForLayer?.('annotation', { ...baseState, layers: [ @@ -2891,25 +2890,6 @@ describe('xy_visualization', () => { }, ], }) -======= - xyVisualization.getSupportedActionsForLayer?.( - 'annotation', - { - ...baseState, - layers: [ - ...baseState.layers, - { - layerId: 'annotation', - layerType: LayerTypes.ANNOTATIONS, - annotations: [exampleAnnotation2], - ignoreGlobalFilters: true, - indexPatternId: 'myIndexPattern', - }, - ], - }, - jest.fn() - ) ->>>>>>> 12660923f1730eb472eb087657e566e1393f31dd ).toEqual([ expect.objectContaining({ displayName: 'Keep global filters', @@ -2949,7 +2929,7 @@ describe('xy_visualization', () => { layers: expect.arrayContaining([ { layerId: 'annotation', - layerType: LayerTypes.ANNOTATIONS, + layerType: layerTypes.ANNOTATIONS, annotations: [exampleAnnotation2], ignoreGlobalFilters: false, indexPatternId: 'myIndexPattern', diff --git a/x-pack/plugins/lens/server/embeddable/make_lens_embeddable_factory.ts b/x-pack/plugins/lens/server/embeddable/make_lens_embeddable_factory.ts index cc8d33fed20a2..ba62a93ce8f65 100644 --- a/x-pack/plugins/lens/server/embeddable/make_lens_embeddable_factory.ts +++ b/x-pack/plugins/lens/server/embeddable/make_lens_embeddable_factory.ts @@ -153,7 +153,6 @@ export const makeLensEmbeddableFactory = layers: Array<{ groups?: string[] }>; }> ); - migratedLensState = commonMigratePartitionMetrics(migratedLensState); return { ...lensState, attributes: migratedLensState, @@ -163,7 +162,7 @@ export const makeLensEmbeddableFactory = const lensState = state as unknown as SavedObject>; let migratedLensState = commonMigrateIndexPatternDatasource(lensState.attributes); - migratedLensState = commonMigratePartitionMetrics(lensState.attributes); + migratedLensState = commonMigratePartitionMetrics(migratedLensState); return { ...lensState, attributes: migratedLensState, diff --git a/x-pack/plugins/lens/server/migrations/common_migrations.ts b/x-pack/plugins/lens/server/migrations/common_migrations.ts index 0e3498a6de551..3559e79def68a 100644 --- a/x-pack/plugins/lens/server/migrations/common_migrations.ts +++ b/x-pack/plugins/lens/server/migrations/common_migrations.ts @@ -542,12 +542,12 @@ export const commonMigratePartitionChartGroups = ( }>; }; -export const commonMigratePartitionMetrics = (attributes: LensDocShape850) => { +export const commonMigratePartitionMetrics = (attributes: LensDocShape860) => { if (attributes.visualizationType !== 'lnsPie') { - return attributes as LensDocShape850; + return attributes as LensDocShape860; } - const partitionAttributes = attributes as LensDocShape850<{ + const partitionAttributes = attributes as LensDocShape860<{ shape: string; layers: Array<{ metric: string }>; }>; @@ -565,7 +565,7 @@ export const commonMigratePartitionMetrics = (attributes: LensDocShape850; }>; diff --git a/x-pack/plugins/lens/server/migrations/saved_object_migrations.ts b/x-pack/plugins/lens/server/migrations/saved_object_migrations.ts index 608f645e14f2b..5f5274963f3bc 100644 --- a/x-pack/plugins/lens/server/migrations/saved_object_migrations.ts +++ b/x-pack/plugins/lens/server/migrations/saved_object_migrations.ts @@ -555,7 +555,7 @@ const migratePartitionChartGroups: SavedObjectMigrationFn = ( +const migratePartitionMetrics: SavedObjectMigrationFn = ( doc ) => ({ ...doc, From e84d2ab9153955554fdc8f06b93b29630395edff Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Tue, 25 Oct 2022 19:26:12 -0500 Subject: [PATCH 36/70] fix unit test --- .../lens/server/migrations/saved_object_migrations.test.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/x-pack/plugins/lens/server/migrations/saved_object_migrations.test.ts b/x-pack/plugins/lens/server/migrations/saved_object_migrations.test.ts index ead1215197c75..eec31924fdc85 100644 --- a/x-pack/plugins/lens/server/migrations/saved_object_migrations.test.ts +++ b/x-pack/plugins/lens/server/migrations/saved_object_migrations.test.ts @@ -2382,6 +2382,9 @@ describe('Lens migrations', () => { }, ], }, + datasourceStates: { + indexpattern: {}, + }, }, }, } as unknown as SavedObjectUnsanitizedDoc; From 9086a9fe5e8715bd8827d7d2a4b8618add408f63 Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Wed, 26 Oct 2022 13:35:40 -0500 Subject: [PATCH 37/70] fix ast generation for agg-based --- src/plugins/vis_types/pie/public/to_ast.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/vis_types/pie/public/to_ast.ts b/src/plugins/vis_types/pie/public/to_ast.ts index f5e55e5fc2529..853e354cf4d06 100644 --- a/src/plugins/vis_types/pie/public/to_ast.ts +++ b/src/plugins/vis_types/pie/public/to_ast.ts @@ -70,7 +70,7 @@ export const toExpressionAst: VisToExpressionAst = async (vi emptySizeRatio: vis.params.emptySizeRatio, palette: preparePalette(vis.params.palette), labels: prepareLabels(vis.params.labels), - metrics: prepareDimension(schemas.metric[0]), + metrics: prepareDimension(schemas.metric[schemas.metric.length - 1]), buckets: schemas.segment?.map(prepareDimension), splitColumn: schemas.split_column?.map(prepareDimension), splitRow: schemas.split_row?.map(prepareDimension), From 065e2c68fab261be87f983bf7edbbcec3de580dd Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Thu, 27 Oct 2022 09:35:36 -0500 Subject: [PATCH 38/70] don't show visualization when no bucket dimensions and not multi-metric --- .../common/utils/consolidate_metric_columns.ts | 8 ++++++-- .../lens/public/visualizations/partition/to_expression.ts | 5 ++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/utils/consolidate_metric_columns.ts b/src/plugins/chart_expressions/expression_partition_vis/common/utils/consolidate_metric_columns.ts index 502e5ec89fec4..7b597b21c6f6d 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/utils/consolidate_metric_columns.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/utils/consolidate_metric_columns.ts @@ -10,6 +10,10 @@ import { Datatable, DatatableColumn, DatatableRow } from '@kbn/expressions-plugi import { getColumnByAccessor } from '@kbn/visualizations-plugin/common/utils'; import type { ExpressionValueVisDimension } from '@kbn/visualizations-plugin/common'; +function nonNullable(value: T): value is NonNullable { + return value !== null && value !== undefined; +} + export const consolidateMetricColumns = ( table: Datatable, bucketAccessors: Array = [], @@ -29,11 +33,11 @@ export const consolidateMetricColumns = ( const bucketColumns = bucketAccessors ?.map((accessor) => getColumnByAccessor(accessor, table.columns)) - .filter(Boolean) as DatatableColumn[]; + .filter(nonNullable); const metricColumns = metricAccessors ?.map((accessor) => getColumnByAccessor(accessor, table.columns)) - .filter(Boolean) as DatatableColumn[]; + .filter(nonNullable); const transposedRows: DatatableRow[] = []; diff --git a/x-pack/plugins/lens/public/visualizations/partition/to_expression.ts b/x-pack/plugins/lens/public/visualizations/partition/to_expression.ts index f5bb73e3b7bea..67c84e01da364 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/to_expression.ts +++ b/x-pack/plugins/lens/public/visualizations/partition/to_expression.ts @@ -226,8 +226,7 @@ const generateMosaicVisAst: GenerateExpressionAstFunction = (...rest) => { buckets: rest[2] .filter(({ columnId }) => !isCollapsed(columnId, rest[3])) .reverse() - .map((o) => o.columnId) - .map(prepareDimension), + .map((o) => prepareDimension(o.columnId)), ...args, }, }, @@ -290,7 +289,7 @@ function expressionHelper( })) .filter((o): o is { columnId: string; operation: Operation } => !!o.operation); - if (!layer.metrics.length) { + if (!layer.metrics.length || (!operations.length && !layer.allowMultipleMetrics)) { return null; } const visualizationAst = generateExprAst( From 54323ebe778e6e043df79b9c898ea4cd8eb21913 Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Thu, 27 Oct 2022 09:41:37 -0500 Subject: [PATCH 39/70] filter generation optimization --- .../public/utils/filter_helpers.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/utils/filter_helpers.ts b/src/plugins/chart_expressions/expression_partition_vis/public/utils/filter_helpers.ts index 6855062658bc9..6a42bc6f7b601 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/utils/filter_helpers.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/public/utils/filter_helpers.ts @@ -56,13 +56,15 @@ export const getFilterClickData = ( data.push( ...(clickedLayers .map((clickedLayer, index) => { - const currentColumn = visData.columns.find((col) => col.id === bucketColumns[index].id); + const currentColumnIndex = visData.columns.findIndex( + (col) => col.id === bucketColumns[index].id + ); - if (!currentColumn) { + if (currentColumnIndex === -1) { return undefined; } - const currentColumnIndex = visData.columns.findIndex((col) => col === currentColumn); + const currentColumn = visData.columns[currentColumnIndex]; // this logic maps the indices of the elements in the // visualization's table to the indices in the table before From 17000f7a5e01649a73a27c83a22803784f70039c Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Thu, 27 Oct 2022 09:42:09 -0500 Subject: [PATCH 40/70] update comment Co-authored-by: Marta Bondyra <4283304+mbondyra@users.noreply.github.com> --- x-pack/plugins/lens/public/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index 6bf12142cce73..5e49eb2df5ee0 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -766,7 +766,7 @@ export type VisualizationDimensionGroupConfig = SharedDimensionProps & { /** ID is passed back to visualization. For example, `x` */ groupId: string; accessors: AccessorConfig[]; - // this is just to show a UI signifier + // currently used only on partition charts to display non-editable UI dimension trigger in the buckets group when multiple metrics exist fakeFinalAccessor?: { label: string; }; From 5bb923026b88cda6ec5f75db2da31a8568462475 Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Thu, 27 Oct 2022 09:45:59 -0500 Subject: [PATCH 41/70] make metrics arg required for pie vis chart expression renderer --- .../common/expression_functions/pie_vis_function.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts index df0d1fc97d267..1e032b33741d8 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts @@ -28,6 +28,7 @@ export const pieVisFunction = (): PieVisExpressionFunctionDefinition => ({ metrics: { types: ['vis_dimension', 'string'], help: strings.getMetricArgHelp(), + required: true, multi: true, }, buckets: { From fb250abe7963f0903d2b30475818efa81724ed23 Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Thu, 27 Oct 2022 09:47:45 -0500 Subject: [PATCH 42/70] unskip inspector test --- .../common/expression_functions/pie_vis_function.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.test.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.test.ts index 9f7e1c41b27b5..474a3b8b35e45 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.test.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.test.ts @@ -124,7 +124,7 @@ describe('interpreter/functions#pieVis', () => { ).toThrowErrorMatchingSnapshot(); }); - it.skip('logs correct datatable to inspector', async () => { + it('logs correct datatable to inspector', async () => { let loggedTable: Datatable; const handlers = { inspectorAdapters: { From 79b2651bc8633fb0e2a576e28d6d0b06647b7008 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 27 Oct 2022 15:24:39 +0000 Subject: [PATCH 43/70] [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' --- x-pack/plugins/lens/public/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index 5e49eb2df5ee0..2f5a73f27f819 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -766,7 +766,7 @@ export type VisualizationDimensionGroupConfig = SharedDimensionProps & { /** ID is passed back to visualization. For example, `x` */ groupId: string; accessors: AccessorConfig[]; - // currently used only on partition charts to display non-editable UI dimension trigger in the buckets group when multiple metrics exist + // currently used only on partition charts to display non-editable UI dimension trigger in the buckets group when multiple metrics exist fakeFinalAccessor?: { label: string; }; From ea623cdde91a72c1d141c077c299e4fdfa932bd9 Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Thu, 27 Oct 2022 13:37:12 -0500 Subject: [PATCH 44/70] style change Co-authored-by: Michael Marcialis --- .../editor_frame/config_panel/layer_panel.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx index 638e06c15bbab..ad69a77831e72 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx @@ -612,7 +612,7 @@ export function LayerPanel( className="lnsLayerPanel__dimension lnsDragDrop-isDraggable" css={css` cursor: default !important; - border-color: #0000 !important; + border-color: transparent !important; margin-top: ${group.accessors.length ? 8 : 0}px !important; `} > From a80e75efb1cbb08680bc5f2b5eb178c04bf5bf9a Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Thu, 27 Oct 2022 15:54:17 -0500 Subject: [PATCH 45/70] move multi-metric setting to layer settings --- .../editor_frame/config_panel/layer_panel.tsx | 19 ++++--- .../partition/visualization.test.ts | 43 -------------- .../partition/visualization.tsx | 57 ++++--------------- 3 files changed, 23 insertions(+), 96 deletions(-) diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx index 638e06c15bbab..33ccc8ee98f08 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx @@ -699,14 +699,17 @@ export function LayerPanel( /> )} {activeVisualization?.renderLayerSettings && ( - + <> + + + )}
diff --git a/x-pack/plugins/lens/public/visualizations/partition/visualization.test.ts b/x-pack/plugins/lens/public/visualizations/partition/visualization.test.ts index 45fcfd3fec970..875240b50d772 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/visualization.test.ts +++ b/x-pack/plugins/lens/public/visualizations/partition/visualization.test.ts @@ -102,49 +102,6 @@ describe('pie_visualization', () => { }); }); - describe('#getSupportedActionsForLayer', () => { - it('should return actions to toggle multiple metrics mode', () => { - const state = getExampleState(); - - expect(pieVisualization.getSupportedActionsForLayer!(LAYER_ID, state)).toMatchInlineSnapshot(` - Array [ - Object { - "displayName": "Enable multiple metrics", - "icon": "visPie", - "id": "enableMultipleMetricsAction", - "isCompatible": true, - }, - ] - `); - - expect( - pieVisualization.getSupportedActionsForLayer!(LAYER_ID, { - ...state, - layers: [{ ...state.layers[0], allowMultipleMetrics: true }], - }) - ).toMatchInlineSnapshot(` - Array [ - Object { - "clearLayer": true, - "displayName": "Disable multiple metrics", - "icon": "visPie", - "id": "disableMultipleMetricsAction", - "isCompatible": true, - }, - ] - `); - }); - - it('should return no actions for mosaic chart', () => { - expect( - pieVisualization.getSupportedActionsForLayer!(LAYER_ID, { - ...getExampleState(), - shape: 'mosaic', - }) - ).toEqual([]); - }); - }); - describe('#getLayerType', () => { it('should return the type only if the layer is in the state', () => { expect(pieVisualization.getLayerType(LAYER_ID, getExampleState())).toEqual(LayerTypes.DATA); diff --git a/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx b/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx index 0d23df753972a..ee2e50d39ee17 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx @@ -36,7 +36,7 @@ import { } from '../../../common'; import { suggestions } from './suggestions'; import { PartitionChartsMeta } from './partition_charts_meta'; -import { DimensionEditor, PieToolbar } from './toolbar'; +import { DimensionEditor, LayerSettings, PieToolbar } from './toolbar'; import { checkTableForContainsSmallValues } from './render_helpers'; function newLayerState(layerId: string): PieLayerState { @@ -420,6 +420,17 @@ export const getPieVisualization = ({ ); }, + renderLayerSettings(domElement, props) { + render( + + + + + , + domElement + ); + }, + getWarningMessages(state, frame) { if (state?.layers.length === 0 || !frame.activeData) { return; @@ -539,48 +550,4 @@ export const getPieVisualization = ({ ] : []; }, - - getSupportedActionsForLayer(layerId, state) { - const layerInQuestion = state.layers.find((layer) => layer.layerId === layerId); - - if (!layerInQuestion || state.shape === PieChartTypes.MOSAIC) { - return []; - } - - return layerInQuestion.allowMultipleMetrics - ? [ - { - id: DISABLE_MULTIPLE_METRICS_ACTION_ID, - displayName: 'Disable multiple metrics', - icon: 'visPie', - isCompatible: true, - clearLayer: true, - }, - ] - : [ - { - id: ENABLE_MULTIPLE_METRICS_ACTION_ID, - displayName: 'Enable multiple metrics', - icon: 'visPie', - isCompatible: true, - }, - ]; - }, - - onLayerAction(layerId, actionId, state) { - return { - ...state, - layers: state.layers.map((layer) => { - const ret: PieLayerState = - layer.layerId !== layerId - ? layer - : { - ...layer, - allowMultipleMetrics: actionId === ENABLE_MULTIPLE_METRICS_ACTION_ID, - }; - - return ret; - }), - }; - }, }); From 4c602e2840fd1e0fc70ac3e8b508a28ca69dbbd8 Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Thu, 27 Oct 2022 15:54:57 -0500 Subject: [PATCH 46/70] add layer settings --- .../partition/layer_settings.test.tsx | 94 +++++++++++++++++++ .../partition/layer_settings.tsx | 52 ++++++++++ 2 files changed, 146 insertions(+) create mode 100644 x-pack/plugins/lens/public/visualizations/partition/layer_settings.test.tsx create mode 100644 x-pack/plugins/lens/public/visualizations/partition/layer_settings.tsx diff --git a/x-pack/plugins/lens/public/visualizations/partition/layer_settings.test.tsx b/x-pack/plugins/lens/public/visualizations/partition/layer_settings.test.tsx new file mode 100644 index 0000000000000..9acedbc9b8dd2 --- /dev/null +++ b/x-pack/plugins/lens/public/visualizations/partition/layer_settings.test.tsx @@ -0,0 +1,94 @@ +/* + * 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 React from 'react'; +import { shallow } from 'enzyme'; +import { PieLayerState, PieVisualizationState } from '../..'; +import { LayerSettings } from './layer_settings'; +import { FramePublicAPI, VisualizationLayerSettingsProps } from '../../types'; +import { EuiSwitch, EuiSwitchEvent } from '@elastic/eui'; + +describe('layer settings', () => { + describe('multiple metrics switch', () => { + const getState = (allowMultipleMetrics: boolean): PieVisualizationState => ({ + shape: 'pie', + layers: [ + { + layerId, + allowMultipleMetrics, + } as PieLayerState, + ], + }); + + const layerId = 'layer-id'; + const props: VisualizationLayerSettingsProps = { + setState: jest.fn(), + layerId, + state: getState(false), + frame: {} as FramePublicAPI, + panelRef: {} as React.MutableRefObject, + }; + + it('toggles multiple metrics', () => { + const toggleOn = () => + shallow() + .find(EuiSwitch) + .props() + .onChange({} as EuiSwitchEvent); + + const toggleOff = () => + shallow() + .find(EuiSwitch) + .props() + .onChange({} as EuiSwitchEvent); + + expect(props.setState).not.toHaveBeenCalled(); + + toggleOn(); + + expect(props.setState).toHaveBeenLastCalledWith({ + ...props.state, + layers: [ + { + ...props.state.layers[0], + allowMultipleMetrics: true, + }, + ], + }); + + toggleOff(); + + expect(props.setState).toHaveBeenLastCalledWith({ + ...props.state, + layers: [ + { + ...props.state.layers[0], + allowMultipleMetrics: false, + }, + ], + }); + }); + + test('switch reflects state', () => { + const isChecked = (state: PieVisualizationState) => + shallow() + .find(EuiSwitch) + .props().checked; + + expect(isChecked(getState(false))).toBeFalsy(); + expect(isChecked(getState(true))).toBeTruthy(); + }); + + test('hides option for mosaic', () => { + expect( + shallow( + + ).isEmptyRender() + ).toBeTruthy(); + }); + }); +}); diff --git a/x-pack/plugins/lens/public/visualizations/partition/layer_settings.tsx b/x-pack/plugins/lens/public/visualizations/partition/layer_settings.tsx new file mode 100644 index 0000000000000..6876b218c235e --- /dev/null +++ b/x-pack/plugins/lens/public/visualizations/partition/layer_settings.tsx @@ -0,0 +1,52 @@ +/* + * 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 { EuiFormRow, EuiSwitch } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React from 'react'; +import { PieChartTypes } from '../../../common'; +import { PieVisualizationState } from '../..'; +import { VisualizationLayerSettingsProps } from '../../types'; + +export function LayerSettings(props: VisualizationLayerSettingsProps) { + if (props.state.shape === PieChartTypes.MOSAIC) { + return null; + } + + const currentLayer = props.state.layers.find((layer) => layer.layerId === props.layerId); + + if (!currentLayer) { + return null; + } + + return ( + <> + + { + props.setState({ + ...props.state, + layers: props.state.layers.map((layer) => + layer.layerId !== props.layerId + ? layer + : { + ...layer, + allowMultipleMetrics: !layer.allowMultipleMetrics, + } + ), + }); + }} + /> + + + ); +} From e85c3fb2a3f31fcd8c0967b61ed2d5f780b81118 Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Thu, 27 Oct 2022 16:16:51 -0500 Subject: [PATCH 47/70] fix import --- .../lens/public/visualizations/partition/visualization.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx b/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx index ee2e50d39ee17..4abb1ed04cc75 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx @@ -36,7 +36,8 @@ import { } from '../../../common'; import { suggestions } from './suggestions'; import { PartitionChartsMeta } from './partition_charts_meta'; -import { DimensionEditor, LayerSettings, PieToolbar } from './toolbar'; +import { DimensionEditor, PieToolbar } from './toolbar'; +import { LayerSettings } from './layer_settings'; import { checkTableForContainsSmallValues } from './render_helpers'; function newLayerState(layerId: string): PieLayerState { From 25ac5fe26d24667a2b6469b4149a146e19271395 Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Thu, 27 Oct 2022 16:28:52 -0500 Subject: [PATCH 48/70] ignore extra metrics but do not delete --- .../editor_frame/config_panel/layer_panel.tsx | 4 --- x-pack/plugins/lens/public/types.ts | 2 +- .../visualizations/partition/to_expression.ts | 4 ++- .../partition/visualization.test.ts | 28 +++++++++++++++++++ .../partition/visualization.tsx | 1 + 5 files changed, 33 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx index 251decca82039..fef14b9718c4d 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx @@ -327,10 +327,6 @@ export function LayerPanel( updateVisualization( activeVisualization.onLayerAction?.(layerId, action.id, visualizationState) ); - - if (action.clearLayer) { - onRemoveLayer(layerId); - } }, })) || []), ...(layerDatasource?.getSupportedActionsForLayer?.( diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index 2f5a73f27f819..d2f275493f5b1 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -560,7 +560,7 @@ export interface LayerAction { 'data-test-subj'?: string; } -export type LayerActionFromVisualization = Omit & { clearLayer?: boolean }; +export type LayerActionFromVisualization = Omit; interface SharedDimensionProps { /** Visualizations can restrict operations based on their own rules. diff --git a/x-pack/plugins/lens/public/visualizations/partition/to_expression.ts b/x-pack/plugins/lens/public/visualizations/partition/to_expression.ts index 67c84e01da364..ecdf9e379d994 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/to_expression.ts +++ b/x-pack/plugins/lens/public/visualizations/partition/to_expression.ts @@ -147,7 +147,9 @@ const generateCommonArguments: GenerateExpressionAstArguments = ( .filter(({ columnId }) => !isCollapsed(columnId, layer)) .map(({ columnId }) => columnId) .map(prepareDimension), - metrics: layer.metrics.map(prepareDimension), + metrics: (layer.allowMultipleMetrics ? layer.metrics : [layer.metrics[0]]).map( + prepareDimension + ), legendDisplay: [attributes.isPreview ? LegendDisplay.HIDE : layer.legendDisplay], legendPosition: [layer.legendPosition || Position.Right], maxLegendLines: [layer.legendMaxLines ?? 1], diff --git a/x-pack/plugins/lens/public/visualizations/partition/visualization.test.ts b/x-pack/plugins/lens/public/visualizations/partition/visualization.test.ts index 875240b50d772..39135ce8ebf09 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/visualization.test.ts +++ b/x-pack/plugins/lens/public/visualizations/partition/visualization.test.ts @@ -389,6 +389,34 @@ describe('pie_visualization', () => { expect(getConfig(stateWithMultipleMetrics).groups[0].supportsMoreColumns).toBeFalsy(); }); + it('reports too many metric dimensions if multiple not enabled', () => { + const colIds = ['1', '2', '3', '4']; + + const frame = mockFrame(); + frame.datasourceLayers[LAYER_ID]!.getTableSpec = () => + colIds.map((id) => ({ columnId: id, fields: [] })); + + const state = getExampleState(); + state.layers[0].metrics = colIds; + state.layers[0].allowMultipleMetrics = false; + expect( + pieVisualization.getConfiguration({ + state, + frame, + layerId: state.layers[0].layerId, + }).groups[1].dimensionsTooMany + ).toBe(3); + + state.layers[0].allowMultipleMetrics = true; + expect( + pieVisualization.getConfiguration({ + state, + frame, + layerId: state.layers[0].layerId, + }).groups[1].dimensionsTooMany + ).toBe(0); + }); + it.each(Object.values(PieChartTypes).filter((type) => type !== 'mosaic'))( '%s adds fake dimension', (type) => { diff --git a/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx b/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx index 4abb1ed04cc75..05b251634e61b 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx @@ -315,6 +315,7 @@ export const getPieVisualization = ({ supportsMoreColumns: layer.metrics.length === 0 || Boolean(layer.allowMultipleMetrics), filterOperations: numberMetricOperations, requiredMinDimensionCount: 1, + dimensionsTooMany: layer.allowMultipleMetrics ? 0 : layer.metrics.length - 1, dataTestSubj: 'lnsPie_sizeByDimensionPanel', enableDimensionEditor: true, }; From fd67bac8468be529f26f94fa1af805f4e9d09782 Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Mon, 31 Oct 2022 19:38:37 -0500 Subject: [PATCH 49/70] unify migration order --- .../lens/server/embeddable/make_lens_embeddable_factory.ts | 2 ++ .../plugins/lens/server/migrations/saved_object_migrations.ts | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/lens/server/embeddable/make_lens_embeddable_factory.ts b/x-pack/plugins/lens/server/embeddable/make_lens_embeddable_factory.ts index ba62a93ce8f65..aa4f634b700af 100644 --- a/x-pack/plugins/lens/server/embeddable/make_lens_embeddable_factory.ts +++ b/x-pack/plugins/lens/server/embeddable/make_lens_embeddable_factory.ts @@ -168,6 +168,8 @@ export const makeLensEmbeddableFactory = attributes: migratedLensState, } as unknown as SerializableRecord; }, + // FOLLOW THESE GUIDELINES IF YOU ARE ADDING A NEW MIGRATION! + // 1. Make sure you are applying migrations for a given version in the same order here as they are applied in x-pack/plugins/lens/server/migrations/saved_object_migrations.ts }), getLensCustomVisualizationMigrations(customVisualizationMigrations) ), diff --git a/x-pack/plugins/lens/server/migrations/saved_object_migrations.ts b/x-pack/plugins/lens/server/migrations/saved_object_migrations.ts index 5f5274963f3bc..aa480c4c0b151 100644 --- a/x-pack/plugins/lens/server/migrations/saved_object_migrations.ts +++ b/x-pack/plugins/lens/server/migrations/saved_object_migrations.ts @@ -583,7 +583,9 @@ const lensMigrations: SavedObjectMigrationMap = { ), '8.3.0': flow(lockOldMetricVisSettings, preserveOldLegendSizeDefault, fixValueLabelsInXY), '8.5.0': flow(migrateMetricIds, enrichAnnotationLayers, migratePartitionChartGroups), - '8.6.0': flow(migratePartitionMetrics, migrateIndexPatternDatasource), + '8.6.0': flow(migrateIndexPatternDatasource, migratePartitionMetrics), + // FOLLOW THESE GUIDELINES IF YOU ARE ADDING A NEW MIGRATION! + // 1. Make sure you are applying migrations for a given version in the same order here as they are applied in x-pack/plugins/lens/server/embeddable/make_lens_embeddable_factory.ts }; export const getAllMigrations = ( From c419dcf8bfe8fecfcbfbb4552b8ed505823216e3 Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Mon, 31 Oct 2022 19:39:02 -0500 Subject: [PATCH 50/70] remove unused variables --- .../lens/public/visualizations/partition/visualization.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx b/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx index 05b251634e61b..3bc47ba88f82f 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx @@ -96,9 +96,6 @@ const applyPaletteToAccessorConfigs = ( }); }; -const ENABLE_MULTIPLE_METRICS_ACTION_ID = 'enableMultipleMetricsAction'; -const DISABLE_MULTIPLE_METRICS_ACTION_ID = 'disableMultipleMetricsAction'; - export const getPieVisualization = ({ paletteService, kibanaTheme, From 71f95fa6a45d848156865a2859e87928610772d1 Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Mon, 31 Oct 2022 20:10:47 -0500 Subject: [PATCH 51/70] move metric group to top --- .../partition/visualization.test.ts | 41 ++++--------------- .../partition/visualization.tsx | 2 +- 2 files changed, 10 insertions(+), 33 deletions(-) diff --git a/x-pack/plugins/lens/public/visualizations/partition/visualization.test.ts b/x-pack/plugins/lens/public/visualizations/partition/visualization.test.ts index 39135ce8ebf09..f418d588509a2 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/visualization.test.ts +++ b/x-pack/plugins/lens/public/visualizations/partition/visualization.test.ts @@ -187,30 +187,7 @@ describe('pie_visualization', () => { }); // palette should be assigned to the first non-collapsed dimension - expect(configuration.groups[0].accessors).toMatchInlineSnapshot(` - Array [ - Object { - "columnId": "1", - "triggerIcon": "aggregate", - }, - Object { - "columnId": "2", - "palette": Array [ - "red", - "black", - ], - "triggerIcon": "colorBy", - }, - Object { - "columnId": "3", - "triggerIcon": "aggregate", - }, - Object { - "columnId": "4", - "triggerIcon": undefined, - }, - ] - `); + expect(configuration.groups[0].accessors).toMatchInlineSnapshot(`Array []`); const mosaicState = getExampleState(); mosaicState.shape = PieChartTypes.MOSAIC; @@ -228,6 +205,7 @@ describe('pie_visualization', () => { expect(mosaicConfiguration.groups.map(({ accessors }) => accessors)).toMatchInlineSnapshot(` Array [ + Array [], Array [ Object { "columnId": "1", @@ -252,7 +230,6 @@ describe('pie_visualization', () => { "triggerIcon": undefined, }, ], - Array [], ] `); }); @@ -274,7 +251,7 @@ describe('pie_visualization', () => { state, frame, layerId: state.layers[0].layerId, - }).groups[1].accessors + }).groups[0].accessors ).toMatchInlineSnapshot(` Array [ Object { @@ -319,7 +296,7 @@ describe('pie_visualization', () => { state, frame, layerId: state.layers[0].layerId, - }).groups[1].accessors + }).groups[0].accessors ).toMatchInlineSnapshot(` Array [ Object { @@ -354,12 +331,12 @@ describe('pie_visualization', () => { layerId: state.layers[0].layerId, }); - expect(getConfig(state).groups[0].supportsMoreColumns).toBeFalsy(); + expect(getConfig(state).groups[1].supportsMoreColumns).toBeFalsy(); const stateWithCollapsed = cloneDeep(state); stateWithCollapsed.layers[0].collapseFns = { '1': 'sum' }; - expect(getConfig(stateWithCollapsed).groups[0].supportsMoreColumns).toBeTruthy(); + expect(getConfig(stateWithCollapsed).groups[1].supportsMoreColumns).toBeTruthy(); }); it('counts multiple metrics toward the dimension limits', () => { @@ -404,7 +381,7 @@ describe('pie_visualization', () => { state, frame, layerId: state.layers[0].layerId, - }).groups[1].dimensionsTooMany + }).groups[0].dimensionsTooMany ).toBe(3); state.layers[0].allowMultipleMetrics = true; @@ -413,7 +390,7 @@ describe('pie_visualization', () => { state, frame, layerId: state.layers[0].layerId, - }).groups[1].dimensionsTooMany + }).groups[0].dimensionsTooMany ).toBe(0); }); @@ -427,7 +404,7 @@ describe('pie_visualization', () => { state, frame: mockFrame(), layerId: state.layers[0].layerId, - }).groups[0].fakeFinalAccessor + }).groups[1].fakeFinalAccessor ).toEqual({ label: '2 metrics' }); } ); diff --git a/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx b/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx index 3bc47ba88f82f..c82b554d21c75 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx @@ -319,7 +319,7 @@ export const getPieVisualization = ({ }; return { - groups: [getPrimaryGroupConfig(), getSecondaryGroupConfig(), getMetricGroupConfig()].filter( + groups: [getMetricGroupConfig(), getPrimaryGroupConfig(), getSecondaryGroupConfig()].filter( Boolean ) as VisualizationDimensionGroupConfig[], }; From 761d6c6ff792d9ddcacdca99480c704cbddbfe26 Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Tue, 1 Nov 2022 20:03:47 -0500 Subject: [PATCH 52/70] mosaic transition --- .../partition/suggestions.test.ts | 175 +++++++++++++----- .../visualizations/partition/suggestions.ts | 24 ++- .../partition/visualization.test.ts | 31 +++- .../partition/visualization.tsx | 14 +- 4 files changed, 179 insertions(+), 65 deletions(-) diff --git a/x-pack/plugins/lens/public/visualizations/partition/suggestions.test.ts b/x-pack/plugins/lens/public/visualizations/partition/suggestions.test.ts index e325e5563d080..0be4fbde6c1d2 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/suggestions.test.ts +++ b/x-pack/plugins/lens/public/visualizations/partition/suggestions.test.ts @@ -267,57 +267,86 @@ describe('suggestions', () => { ).toHaveLength(0); }); - it('should accept multiple metrics when active', () => { - expect( - suggestions({ - table: { - layerId: 'first', - isMultiRow: true, - columns: [ - { - columnId: 'a', - operation: { label: 'Top 5', dataType: 'string' as DataType, isBucketed: true }, - }, - { - columnId: 'b', - operation: { label: 'Top 5', dataType: 'string' as DataType, isBucketed: true }, - }, - { - columnId: 'c', - operation: { label: 'Top 5', dataType: 'string' as DataType, isBucketed: true }, - }, - { - columnId: 'd', - operation: { label: 'Avg', dataType: 'number' as DataType, isBucketed: false }, - }, - { - columnId: 'e', - operation: { label: 'Count', dataType: 'number' as DataType, isBucketed: false }, - }, - ], - changeType: 'initial', - }, - state: { - shape: PieChartTypes.PIE, - layers: [ - { - layerId: 'first', - layerType: layerTypes.DATA, - primaryGroups: ['a'], - metrics: ['b'], - numberDisplay: NumberDisplay.HIDDEN, - categoryDisplay: CategoryDisplay.INSIDE, - legendDisplay: LegendDisplay.SHOW, - percentDecimals: 0, - legendMaxLines: 1, - truncateLegend: true, - nestedLegend: true, - }, - ], - }, - keptLayerIds: ['first'], - }) - ).toHaveLength(2); + it('should accept multiple metrics when active and multi-metric', () => { + const chk = suggestions({ + table: { + layerId: 'first', + isMultiRow: true, + columns: [ + { + columnId: 'a', + operation: { label: 'Top 5', dataType: 'string' as DataType, isBucketed: true }, + }, + { + columnId: 'b', + operation: { label: 'Top 5', dataType: 'string' as DataType, isBucketed: true }, + }, + { + columnId: 'c', + operation: { label: 'Top 5', dataType: 'string' as DataType, isBucketed: true }, + }, + { + columnId: 'd', + operation: { label: 'Avg', dataType: 'number' as DataType, isBucketed: false }, + }, + { + columnId: 'e', + operation: { label: 'Count', dataType: 'number' as DataType, isBucketed: false }, + }, + ], + changeType: 'initial', + }, + state: { + shape: PieChartTypes.PIE, + layers: [ + { + layerId: 'first', + layerType: layerTypes.DATA, + primaryGroups: ['a'], + metrics: ['b'], + numberDisplay: NumberDisplay.HIDDEN, + categoryDisplay: CategoryDisplay.INSIDE, + legendDisplay: LegendDisplay.SHOW, + allowMultipleMetrics: true, + }, + ], + }, + keptLayerIds: ['first'], + }); + + expect(chk).toHaveLength(2); + chk.forEach(({ state }) => { + expect(state.layers[0].allowMultipleMetrics).toBeTruthy(); + expect(state.layers[0].metrics).toEqual(['d', 'e']); + }); + }); + + it('should reject multiple metrics when NOT currently active', () => { + const chk = suggestions({ + table: { + layerId: 'first', + isMultiRow: true, + columns: [ + { + columnId: 'a', + operation: { label: 'Top 5', dataType: 'string' as DataType, isBucketed: true }, + }, + { + columnId: 'b', + operation: { label: 'Avg', dataType: 'number' as DataType, isBucketed: false }, + }, + { + columnId: 'c', + operation: { label: 'Count', dataType: 'number' as DataType, isBucketed: false }, + }, + ], + changeType: 'initial', + }, + state: undefined, + keptLayerIds: ['first'], + }); + + expect(chk).toHaveLength(0); }); it('should reject if there are no buckets and it is not a specific chart type switch', () => { @@ -919,6 +948,50 @@ describe('suggestions', () => { ).toHaveLength(0); }); + it('should turn off multiple metrics for mosaic when switching from other partition type', () => { + const suggs = suggestions({ + table: { + layerId: 'first', + isMultiRow: true, + columns: [ + { + columnId: 'a', + operation: { label: 'Top 5', dataType: 'string' as DataType, isBucketed: true }, + }, + { + columnId: 'b', + operation: { label: 'Avg', dataType: 'number' as DataType, isBucketed: false }, + }, + { + columnId: 'c', + operation: { label: 'Count', dataType: 'number' as DataType, isBucketed: false }, + }, + ], + changeType: 'initial', + }, + state: { + shape: PieChartTypes.PIE, + layers: [ + { + layerId: 'first', + layerType: layerTypes.DATA, + primaryGroups: ['a'], + metrics: ['b', 'c'], + numberDisplay: NumberDisplay.PERCENT, + categoryDisplay: CategoryDisplay.DEFAULT, + legendDisplay: LegendDisplay.DEFAULT, + allowMultipleMetrics: true, + }, + ], + }, + keptLayerIds: ['first'], + subVisualizationId: 'mosaic', + }); + + expect(suggs).toHaveLength(1); + expect(suggs[0].state.layers[0].allowMultipleMetrics).toBeFalsy(); + }); + it('mosaic type should be hidden from the suggestion list', () => { expect( suggestions({ diff --git a/x-pack/plugins/lens/public/visualizations/partition/suggestions.ts b/x-pack/plugins/lens/public/visualizations/partition/suggestions.ts index a9777e2678fe6..93887559a094e 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/suggestions.ts +++ b/x-pack/plugins/lens/public/visualizations/partition/suggestions.ts @@ -105,7 +105,7 @@ export function suggestions({ return []; } - const metricColumns = metrics.map(({ columnId }) => columnId); + const metricColumnIds = metrics.map(({ columnId }) => columnId); const results: Array> = []; @@ -134,13 +134,13 @@ export function suggestions({ ...state.layers[0], layerId: table.layerId, primaryGroups: groups.map((col) => col.columnId), - metrics: metricColumns, + metrics: metricColumnIds, layerType: layerTypes.DATA, } : { layerId: table.layerId, primaryGroups: groups.map((col) => col.columnId), - metrics: metricColumns, + metrics: metricColumnIds, numberDisplay: NumberDisplay.PERCENT, categoryDisplay: CategoryDisplay.DEFAULT, legendDisplay: LegendDisplay.DEFAULT, @@ -199,7 +199,7 @@ export function suggestions({ ...state.layers[0], layerId: table.layerId, primaryGroups: groups.map((col) => col.columnId), - metrics: metricColumns, + metrics: metricColumnIds, categoryDisplay: state.layers[0].categoryDisplay === CategoryDisplay.INSIDE ? CategoryDisplay.DEFAULT @@ -209,7 +209,7 @@ export function suggestions({ : { layerId: table.layerId, primaryGroups: groups.map((col) => col.columnId), - metrics: metricColumns, + metrics: metricColumnIds, numberDisplay: NumberDisplay.PERCENT, categoryDisplay: CategoryDisplay.DEFAULT, legendDisplay: LegendDisplay.DEFAULT, @@ -247,20 +247,22 @@ export function suggestions({ layerId: table.layerId, primaryGroups: groups[0] ? [groups[0].columnId] : [], secondaryGroups: groups[1] ? [groups[1].columnId] : [], - metrics: metricColumns, + metrics: metricColumnIds, categoryDisplay: CategoryDisplay.DEFAULT, layerType: layerTypes.DATA, + allowMultipleMetrics: false, } : { layerId: table.layerId, primaryGroups: groups[0] ? [groups[0].columnId] : [], secondaryGroups: groups[1] ? [groups[1].columnId] : [], - metrics: metricColumns, + metrics: metricColumnIds, numberDisplay: NumberDisplay.PERCENT, categoryDisplay: CategoryDisplay.DEFAULT, legendDisplay: LegendDisplay.DEFAULT, nestedLegend: false, layerType: layerTypes.DATA, + allowMultipleMetrics: false, }, ], }, @@ -287,7 +289,7 @@ export function suggestions({ ...state.layers[0], layerId: table.layerId, primaryGroups: groups.map((col) => col.columnId), - metrics: metricColumns, + metrics: metricColumnIds, secondaryGroups: [], categoryDisplay: CategoryDisplay.DEFAULT, layerType: layerTypes.DATA, @@ -295,7 +297,7 @@ export function suggestions({ : { layerId: table.layerId, primaryGroups: groups.map((col) => col.columnId), - metrics: metricColumns, + metrics: metricColumnIds, numberDisplay: NumberDisplay.PERCENT, categoryDisplay: CategoryDisplay.DEFAULT, legendDisplay: LegendDisplay.DEFAULT, @@ -312,7 +314,9 @@ export function suggestions({ return [...results] .map((suggestion) => ({ ...suggestion, - score: shouldHideSuggestion ? 0 : suggestion.score + 0.05 * groups.length, + score: shouldHideSuggestion + ? 0 + : suggestion.score + 0.05 * groups.length + 0.01 * metricColumnIds.length, })) .sort((a, b) => b.score - a.score) .map((suggestion) => ({ diff --git a/x-pack/plugins/lens/public/visualizations/partition/visualization.test.ts b/x-pack/plugins/lens/public/visualizations/partition/visualization.test.ts index f418d588509a2..a2b89f7e849a9 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/visualization.test.ts +++ b/x-pack/plugins/lens/public/visualizations/partition/visualization.test.ts @@ -339,7 +339,7 @@ describe('pie_visualization', () => { expect(getConfig(stateWithCollapsed).groups[1].supportsMoreColumns).toBeTruthy(); }); - it('counts multiple metrics toward the dimension limits', () => { + it('counts multiple metrics toward the dimension limits when not mosaic', () => { const colIds = new Array(PartitionChartsMeta.pie.maxBuckets - 1) .fill(undefined) .map((_, i) => String(i + 1)); @@ -350,6 +350,31 @@ describe('pie_visualization', () => { const state = getExampleState(); state.layers[0].primaryGroups = colIds; + state.layers[0].allowMultipleMetrics = true; + + const getConfig = (_state: PieVisualizationState) => + pieVisualization.getConfiguration({ + state: _state, + frame, + layerId: state.layers[0].layerId, + }); + + expect(getConfig(state).groups[1].supportsMoreColumns).toBeTruthy(); + + const stateWithMultipleMetrics = cloneDeep(state); + stateWithMultipleMetrics.layers[0].metrics.push('1', '2'); + + expect(getConfig(stateWithMultipleMetrics).groups[1].supportsMoreColumns).toBeFalsy(); + }); + + it('does NOT count multiple metrics toward the dimension limits when mosaic', () => { + const frame = mockFrame(); + frame.datasourceLayers[LAYER_ID]!.getTableSpec = () => []; + + const state = getExampleState(); + state.shape = 'mosaic'; + state.layers[0].primaryGroups = []; + state.layers[0].allowMultipleMetrics = false; // always true for mosaic const getConfig = (_state: PieVisualizationState) => pieVisualization.getConfiguration({ @@ -358,12 +383,12 @@ describe('pie_visualization', () => { layerId: state.layers[0].layerId, }); - expect(getConfig(state).groups[0].supportsMoreColumns).toBeTruthy(); + expect(getConfig(state).groups[1].supportsMoreColumns).toBeTruthy(); const stateWithMultipleMetrics = cloneDeep(state); stateWithMultipleMetrics.layers[0].metrics.push('1', '2'); - expect(getConfig(stateWithMultipleMetrics).groups[0].supportsMoreColumns).toBeFalsy(); + expect(getConfig(stateWithMultipleMetrics).groups[1].supportsMoreColumns).toBeTruthy(); }); it('reports too many metric dimensions if multiple not enabled', () => { diff --git a/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx b/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx index c82b554d21c75..b8b579f572d7e 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx @@ -181,11 +181,23 @@ export const getPieVisualization = ({ filterOperations: bucketedOperations, }; + // We count multiple metrics as a bucket dimension. + // + // However, if this is a mosaic chart, we don't support multiple metrics + // so if there is more than one metric we got here via a chart switch from + // a subtype that supports multi-metrics e.g. pie. + // + // The user will be prompted to remove the extra metric dimensions and we don't + // count multiple metrics as a bucket dimension so that the rest of the dimension + // groups UI behaves correctly. + const multiMetricsBucketDimensionCount = + layer.metrics.length > 1 && state.shape !== 'mosaic' ? 1 : 0; + const totalNonCollapsedAccessors = accessors.reduce( (total, { columnId }) => total + (isCollapsed(columnId, layer) ? 0 : 1), 0 - ) + (layer.metrics.length > 1 ? 1 : 0); + ) + multiMetricsBucketDimensionCount; const fakeFinalAccessor = layer.metrics.length > 1 From cf27c6666e1a1dafa50e3a921f2780394dc5ff2d Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Wed, 2 Nov 2022 14:39:35 -0500 Subject: [PATCH 53/70] improve styling and structure of faux dimension --- .../editor_frame/config_panel/layer_panel.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx index fef14b9718c4d..30f81c37c801c 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx @@ -19,6 +19,7 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { css } from '@emotion/react'; +import { euiThemeVars } from '@kbn/ui-theme'; import { LayerType } from '../../../../common'; import { LayerActions } from './layer_actions'; import { IndexPatternServiceAPI } from '../../../data_views_service/service'; @@ -610,20 +611,19 @@ export function LayerPanel( cursor: default !important; border-color: transparent !important; margin-top: ${group.accessors.length ? 8 : 0}px !important; + background-color: ${euiThemeVars.euiColorLightShade} !important; + box-shadow: none !important; `} > - - - - {group.fakeFinalAccessor.label} - - - + + {group.fakeFinalAccessor.label} + )} From cffbb51bc605861a01f230faff63b2582890dd11 Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Thu, 3 Nov 2022 10:04:02 -0500 Subject: [PATCH 54/70] no longer surfacing palette on multi-metric dimensions --- .../visualizations/partition/toolbar.tsx | 8 +- .../partition/visualization.test.ts | 107 +++++------------- .../partition/visualization.tsx | 70 ++++++------ 3 files changed, 72 insertions(+), 113 deletions(-) diff --git a/x-pack/plugins/lens/public/visualizations/partition/toolbar.tsx b/x-pack/plugins/lens/public/visualizations/partition/toolbar.tsx index 97844baba2d45..cbd69d54486bd 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/toolbar.tsx +++ b/x-pack/plugins/lens/public/visualizations/partition/toolbar.tsx @@ -32,7 +32,7 @@ import { import { getDefaultVisualValuesForLayer } from '../../shared_components/datasource_default_values'; import { shouldShowValuesInLegend } from './render_helpers'; import { CollapseSetting } from '../../shared_components/collapse_setting'; -import { shouldShowPaletteOnDimension } from './visualization'; +import { isCollapsed } from './visualization'; const legendOptions: Array<{ value: SharedPieLayerState['legendDisplay']; @@ -314,9 +314,13 @@ export function DimensionEditor( return null; } + const firstNonCollapsedColumnId = currentLayer.primaryGroups.find( + (id) => !isCollapsed(id, currentLayer) + ); + return ( <> - {shouldShowPaletteOnDimension(props.accessor, currentLayer) && ( + {props.accessor === firstNonCollapsedColumnId && ( { expect(newState.layers[0].collapseFns).not.toHaveProperty('3'); }); + it('removes custom palette if removing final slice-by dimension in multi-metric chart', () => { + const state = getExampleState(); + + state.layers[0].primaryGroups = ['1', '2']; + state.layers[0].allowMultipleMetrics = true; + state.layers[0].metrics = ['3', '4']; + state.palette = {} as PaletteOutput; + + let newState = pieVisualization.removeDimension({ + layerId: LAYER_ID, + columnId: '1', + prevState: state, + frame: mockFrame(), + }); + + expect(newState.layers[0].primaryGroups).toEqual(['2']); + expect(newState.palette).toBeDefined(); + + newState = pieVisualization.removeDimension({ + layerId: LAYER_ID, + columnId: '2', + prevState: newState, + frame: mockFrame(), + }); + + expect(newState.layers[0].primaryGroups).toEqual([]); + expect(newState.palette).toBeUndefined(); + }); }); describe('#getConfiguration', () => { @@ -234,84 +263,6 @@ describe('pie_visualization', () => { `); }); - it('assigns palette to multiple metric accessors if no bucket dimension', () => { - const colIds = ['1', '2', '3', '4']; - - const frame = mockFrame(); - frame.datasourceLayers[LAYER_ID]!.getTableSpec = () => - colIds.map((id) => ({ columnId: id, fields: [] })); - - const state = getExampleState(); - state.layers[0].primaryGroups = []; - state.layers[0].metrics = colIds; - state.layers[0].allowMultipleMetrics = true; - - expect( - pieVisualization.getConfiguration({ - state, - frame, - layerId: state.layers[0].layerId, - }).groups[0].accessors - ).toMatchInlineSnapshot(` - Array [ - Object { - "columnId": "1", - "palette": Array [ - "red", - "black", - ], - "triggerIcon": "colorBy", - }, - Object { - "columnId": "2", - "palette": Array [ - "red", - "black", - ], - "triggerIcon": "colorBy", - }, - Object { - "columnId": "3", - "palette": Array [ - "red", - "black", - ], - "triggerIcon": "colorBy", - }, - Object { - "columnId": "4", - "palette": Array [ - "red", - "black", - ], - "triggerIcon": "colorBy", - }, - ] - `); - - state.layers[0].primaryGroups.push(state.layers[0].metrics.pop() as string); - - expect( - pieVisualization.getConfiguration({ - state, - frame, - layerId: state.layers[0].layerId, - }).groups[0].accessors - ).toMatchInlineSnapshot(` - Array [ - Object { - "columnId": "1", - }, - Object { - "columnId": "2", - }, - Object { - "columnId": "3", - }, - ] - `); - }); - it("doesn't count collapsed columns toward the dimension limits", () => { const colIds = new Array(PartitionChartsMeta.pie.maxBuckets) .fill(undefined) diff --git a/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx b/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx index b8b579f572d7e..0f390fff31573 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx @@ -67,27 +67,16 @@ const numberMetricOperations = (op: OperationMetadata) => export const isCollapsed = (columnId: string, layer: PieLayerState) => Boolean(layer.collapseFns?.[columnId]); -export const shouldShowPaletteOnDimension = (columnId: string, currentLayer: PieLayerState) => { - const firstNonCollapsedColumnId = currentLayer.primaryGroups.find( - (id) => !isCollapsed(id, currentLayer) - ); - - return ( - columnId === firstNonCollapsedColumnId || - (!firstNonCollapsedColumnId && - currentLayer.metrics.includes(columnId) && - currentLayer.allowMultipleMetrics) - ); -}; - const applyPaletteToAccessorConfigs = ( columns: AccessorConfig[], layer: PieLayerState, palette: PieVisualizationState['palette'], paletteService: PaletteRegistry ) => { + const firstNonCollapsedColumnId = layer.primaryGroups.find((id) => !isCollapsed(id, layer)); + columns.forEach((accessorConfig) => { - if (shouldShowPaletteOnDimension(accessorConfig.columnId, layer)) { + if (firstNonCollapsedColumnId === accessorConfig.columnId) { accessorConfig.triggerIcon = 'colorBy'; accessorConfig.palette = paletteService .get(palette?.name || 'default') @@ -364,28 +353,43 @@ export const getPieVisualization = ({ }; }, removeDimension({ prevState, layerId, columnId }) { - return { - ...prevState, - layers: prevState.layers.map((l) => { - if (l.layerId !== layerId) { - return l; - } + const newState = { ...prevState }; - const newLayer = { ...l }; + const layerToChange = prevState.layers.find((l) => l.layerId === layerId); - if (l.collapseFns?.[columnId]) { - const newCollapseFns = { ...l.collapseFns }; - delete newCollapseFns[columnId]; - newLayer.collapseFns = newCollapseFns; - } + if (!layerToChange) { + return prevState; + } - return { - ...newLayer, - primaryGroups: newLayer.primaryGroups.filter((c) => c !== columnId), - secondaryGroups: newLayer.secondaryGroups?.filter((c) => c !== columnId) ?? undefined, - metrics: l.metrics.filter((c) => c !== columnId), - }; - }), + if ( + layerToChange.primaryGroups.includes(columnId) && + layerToChange.primaryGroups.length === 1 && + layerToChange.allowMultipleMetrics && + layerToChange.metrics.length + ) { + // we don't support palette selection for multiple metrics without a slice-by dimension + // so revert to default if the last slice-by is removed + delete newState.palette; + } + + let newLayer = { ...layerToChange }; + + if (layerToChange.collapseFns?.[columnId]) { + const newCollapseFns = { ...layerToChange.collapseFns }; + delete newCollapseFns[columnId]; + newLayer.collapseFns = newCollapseFns; + } + + newLayer = { + ...newLayer, + primaryGroups: newLayer.primaryGroups.filter((c) => c !== columnId), + secondaryGroups: newLayer.secondaryGroups?.filter((c) => c !== columnId) ?? undefined, + metrics: newLayer.metrics.filter((c) => c !== columnId), + }; + + return { + ...newState, + layers: newState.layers.map((l) => (l.layerId === layerId ? newLayer : l)), }; }, renderDimensionEditor(domElement, props) { From 83a85df60b8a584b2fb5c23d5f5d7e428def08fb Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Thu, 3 Nov 2022 13:10:20 -0500 Subject: [PATCH 55/70] only show fake dimension when multiple metrics are allowed --- .../visualizations/partition/visualization.test.ts | 11 +++++++++++ .../public/visualizations/partition/visualization.tsx | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/lens/public/visualizations/partition/visualization.test.ts b/x-pack/plugins/lens/public/visualizations/partition/visualization.test.ts index 5770f802e4e79..f798e301b4dde 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/visualization.test.ts +++ b/x-pack/plugins/lens/public/visualizations/partition/visualization.test.ts @@ -375,6 +375,7 @@ describe('pie_visualization', () => { (type) => { const state = { ...getExampleState(), type }; state.layers[0].metrics.push('1', '2'); + state.layers[0].allowMultipleMetrics = true; expect( pieVisualization.getConfiguration({ state, @@ -382,6 +383,16 @@ describe('pie_visualization', () => { layerId: state.layers[0].layerId, }).groups[1].fakeFinalAccessor ).toEqual({ label: '2 metrics' }); + + // but not when multiple metrics aren't allowed + state.layers[0].allowMultipleMetrics = false; + expect( + pieVisualization.getConfiguration({ + state, + frame: mockFrame(), + layerId: state.layers[0].layerId, + }).groups[1].fakeFinalAccessor + ).toBeUndefined(); } ); }); diff --git a/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx b/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx index 0f390fff31573..7b92df0ceb311 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx @@ -189,7 +189,7 @@ export const getPieVisualization = ({ ) + multiMetricsBucketDimensionCount; const fakeFinalAccessor = - layer.metrics.length > 1 + layer.metrics.length > 1 && layer.allowMultipleMetrics ? { label: i18n.translate('xpack.lens.pie.multiMetricAccessorLabel', { defaultMessage: '{number} metrics', From 1480e3fd35332f2af0c34d797b5808e13e9c7556 Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Thu, 3 Nov 2022 13:18:44 -0500 Subject: [PATCH 56/70] allow visualization to render whether or not there's a slice-by --- .../lens/public/visualizations/partition/to_expression.ts | 2 +- .../lens/public/visualizations/partition/visualization.tsx | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/x-pack/plugins/lens/public/visualizations/partition/to_expression.ts b/x-pack/plugins/lens/public/visualizations/partition/to_expression.ts index ecdf9e379d994..2a23e45b0ce7b 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/to_expression.ts +++ b/x-pack/plugins/lens/public/visualizations/partition/to_expression.ts @@ -291,7 +291,7 @@ function expressionHelper( })) .filter((o): o is { columnId: string; operation: Operation } => !!o.operation); - if (!layer.metrics.length || (!operations.length && !layer.allowMultipleMetrics)) { + if (!layer.metrics.length) { return null; } const visualizationAst = generateExprAst( diff --git a/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx b/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx index 7b92df0ceb311..e8f99e075d8bc 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx @@ -163,7 +163,6 @@ export const getPieVisualization = ({ } const primaryGroupConfigBaseProps = { - requiredMinDimensionCount: layer.allowMultipleMetrics ? 0 : 1, groupId: 'primaryGroups', accessors, enableDimensionEditor: true, From 34d35a9085a6b17b6affa9004193241cf9039e39 Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Mon, 7 Nov 2022 14:05:46 -0600 Subject: [PATCH 57/70] change label to plural when multiple metrics are enabled --- .../visualizations/partition/visualization.tsx | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx b/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx index e8f99e075d8bc..c119c6124c723 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx @@ -295,14 +295,18 @@ export const getPieVisualization = ({ const accessors = layer.metrics.map((columnId) => ({ columnId })); applyPaletteToAccessorConfigs(accessors, layer, state.palette, paletteService); + const groupLabel = layer.allowMultipleMetrics + ? i18n.translate('xpack.lens.pie.groupMetricLabel', { + defaultMessage: 'Metrics', + }) + : i18n.translate('xpack.lens.pie.groupMetricLabelSingular', { + defaultMessage: 'Metric', + }); + return { groupId: 'metric', - groupLabel: i18n.translate('xpack.lens.pie.groupMetricLabel', { - defaultMessage: 'Metrics', - }), - dimensionEditorGroupLabel: i18n.translate('xpack.lens.pie.groupMetricLabel', { - defaultMessage: 'Metrics', - }), + groupLabel, + dimensionEditorGroupLabel: groupLabel, paramEditorCustomProps: { headingLabel: i18n.translate('xpack.lens.pie.headingLabel', { defaultMessage: 'Value', From 177fb00c0714405e8dbc8ff6967dbf18ddc8d3b1 Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Wed, 9 Nov 2022 11:54:23 -0600 Subject: [PATCH 58/70] revert group order change --- .../partition/visualization.test.ts | 87 ++++++++++++++----- .../partition/visualization.tsx | 2 +- 2 files changed, 64 insertions(+), 25 deletions(-) diff --git a/x-pack/plugins/lens/public/visualizations/partition/visualization.test.ts b/x-pack/plugins/lens/public/visualizations/partition/visualization.test.ts index f798e301b4dde..1266169e6673e 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/visualization.test.ts +++ b/x-pack/plugins/lens/public/visualizations/partition/visualization.test.ts @@ -16,7 +16,7 @@ import { import { LayerTypes } from '@kbn/expression-xy-plugin/public'; import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; import { createMockDatasource, createMockFramePublicAPI } from '../../mocks'; -import { FramePublicAPI } from '../../types'; +import { FramePublicAPI, Visualization } from '../../types'; import { themeServiceMock } from '@kbn/core/public/mocks'; import { cloneDeep } from 'lodash'; import { PartitionChartsMeta } from './partition_charts_meta'; @@ -27,6 +27,12 @@ jest.mock('../../id_generator'); const LAYER_ID = 'l1'; +const findPrimaryGroup = (config: ReturnType) => + config.groups.find((group) => group.groupId === 'primaryGroups'); + +const findMetricGroup = (config: ReturnType) => + config.groups.find((group) => group.groupId === 'metric'); + const pieVisualization = getPieVisualization({ paletteService: chartPluginMock.createPaletteRegistry(), kibanaTheme: themeServiceMock.createStartContract(), @@ -216,7 +222,30 @@ describe('pie_visualization', () => { }); // palette should be assigned to the first non-collapsed dimension - expect(configuration.groups[0].accessors).toMatchInlineSnapshot(`Array []`); + expect(configuration.groups[0].accessors).toMatchInlineSnapshot(` + Array [ + Object { + "columnId": "1", + "triggerIcon": "aggregate", + }, + Object { + "columnId": "2", + "palette": Array [ + "red", + "black", + ], + "triggerIcon": "colorBy", + }, + Object { + "columnId": "3", + "triggerIcon": "aggregate", + }, + Object { + "columnId": "4", + "triggerIcon": undefined, + }, + ] + `); const mosaicState = getExampleState(); mosaicState.shape = PieChartTypes.MOSAIC; @@ -234,7 +263,6 @@ describe('pie_visualization', () => { expect(mosaicConfiguration.groups.map(({ accessors }) => accessors)).toMatchInlineSnapshot(` Array [ - Array [], Array [ Object { "columnId": "1", @@ -259,6 +287,7 @@ describe('pie_visualization', () => { "triggerIcon": undefined, }, ], + Array [], ] `); }); @@ -282,12 +311,12 @@ describe('pie_visualization', () => { layerId: state.layers[0].layerId, }); - expect(getConfig(state).groups[1].supportsMoreColumns).toBeFalsy(); + expect(findPrimaryGroup(getConfig(state))?.supportsMoreColumns).toBeFalsy(); const stateWithCollapsed = cloneDeep(state); stateWithCollapsed.layers[0].collapseFns = { '1': 'sum' }; - expect(getConfig(stateWithCollapsed).groups[1].supportsMoreColumns).toBeTruthy(); + expect(findPrimaryGroup(getConfig(stateWithCollapsed))?.supportsMoreColumns).toBeTruthy(); }); it('counts multiple metrics toward the dimension limits when not mosaic', () => { @@ -310,12 +339,14 @@ describe('pie_visualization', () => { layerId: state.layers[0].layerId, }); - expect(getConfig(state).groups[1].supportsMoreColumns).toBeTruthy(); + expect(findPrimaryGroup(getConfig(state))?.supportsMoreColumns).toBeTruthy(); const stateWithMultipleMetrics = cloneDeep(state); stateWithMultipleMetrics.layers[0].metrics.push('1', '2'); - expect(getConfig(stateWithMultipleMetrics).groups[1].supportsMoreColumns).toBeFalsy(); + expect( + findPrimaryGroup(getConfig(stateWithMultipleMetrics))?.supportsMoreColumns + ).toBeFalsy(); }); it('does NOT count multiple metrics toward the dimension limits when mosaic', () => { @@ -334,12 +365,14 @@ describe('pie_visualization', () => { layerId: state.layers[0].layerId, }); - expect(getConfig(state).groups[1].supportsMoreColumns).toBeTruthy(); + expect(findPrimaryGroup(getConfig(state))?.supportsMoreColumns).toBeTruthy(); const stateWithMultipleMetrics = cloneDeep(state); stateWithMultipleMetrics.layers[0].metrics.push('1', '2'); - expect(getConfig(stateWithMultipleMetrics).groups[1].supportsMoreColumns).toBeTruthy(); + expect( + findPrimaryGroup(getConfig(stateWithMultipleMetrics))?.supportsMoreColumns + ).toBeTruthy(); }); it('reports too many metric dimensions if multiple not enabled', () => { @@ -353,20 +386,24 @@ describe('pie_visualization', () => { state.layers[0].metrics = colIds; state.layers[0].allowMultipleMetrics = false; expect( - pieVisualization.getConfiguration({ - state, - frame, - layerId: state.layers[0].layerId, - }).groups[0].dimensionsTooMany + findMetricGroup( + pieVisualization.getConfiguration({ + state, + frame, + layerId: state.layers[0].layerId, + }) + )?.dimensionsTooMany ).toBe(3); state.layers[0].allowMultipleMetrics = true; expect( - pieVisualization.getConfiguration({ - state, - frame, - layerId: state.layers[0].layerId, - }).groups[0].dimensionsTooMany + findMetricGroup( + pieVisualization.getConfiguration({ + state, + frame, + layerId: state.layers[0].layerId, + }) + )?.dimensionsTooMany ).toBe(0); }); @@ -377,11 +414,13 @@ describe('pie_visualization', () => { state.layers[0].metrics.push('1', '2'); state.layers[0].allowMultipleMetrics = true; expect( - pieVisualization.getConfiguration({ - state, - frame: mockFrame(), - layerId: state.layers[0].layerId, - }).groups[1].fakeFinalAccessor + findPrimaryGroup( + pieVisualization.getConfiguration({ + state, + frame: mockFrame(), + layerId: state.layers[0].layerId, + }) + )?.fakeFinalAccessor ).toEqual({ label: '2 metrics' }); // but not when multiple metrics aren't allowed diff --git a/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx b/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx index ac22c69163a2d..37c4316b3d215 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx @@ -323,7 +323,7 @@ export const getPieVisualization = ({ }; return { - groups: [getMetricGroupConfig(), getPrimaryGroupConfig(), getSecondaryGroupConfig()].filter( + groups: [getPrimaryGroupConfig(), getSecondaryGroupConfig(), getMetricGroupConfig()].filter( Boolean ) as VisualizationDimensionGroupConfig[], }; From fd10c4accad6afed4286cd6e8fedf8760a3aaaef Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Wed, 9 Nov 2022 13:49:00 -0600 Subject: [PATCH 59/70] fix lens attributes --- .../__snapshots__/most_used_chart.test.tsx.snap | 4 +++- .../most_used_chart/get_lens_attributes.ts | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_charts/most_used_chart/__snapshots__/most_used_chart.test.tsx.snap b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_charts/most_used_chart/__snapshots__/most_used_chart.test.tsx.snap index b98475f93e0aa..63f86a7265a4a 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_charts/most_used_chart/__snapshots__/most_used_chart.test.tsx.snap +++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_charts/most_used_chart/__snapshots__/most_used_chart.test.tsx.snap @@ -85,7 +85,9 @@ Object { "layerId": "host-os-version", "layerType": "data", "legendDisplay": "hide", - "metric": "countColumn", + "metrics": Array [ + "countColumn", + ], "numberDisplay": "percent", "primaryGroups": Array [ "termsColumn", diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_charts/most_used_chart/get_lens_attributes.ts b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_charts/most_used_chart/get_lens_attributes.ts index 87435bbe68969..de458300caec7 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_charts/most_used_chart/get_lens_attributes.ts +++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_charts/most_used_chart/get_lens_attributes.ts @@ -93,7 +93,7 @@ export function getLensAttributes({ { layerId: metricId, primaryGroups: [columnA], - metric: columnB, + metrics: [columnB], categoryDisplay: 'default', legendDisplay: 'hide', numberDisplay: 'percent', From 6c56190d1c7cfaa975998ce87b8f8ef6c3960563 Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Wed, 9 Nov 2022 14:50:14 -0600 Subject: [PATCH 60/70] use operation labels in chart --- .../common/expression_functions/i18n.ts | 4 ++ .../expression_functions/pie_vis_function.ts | 5 ++ .../treemap_vis_function.ts | 5 ++ .../waffle_vis_function.ts | 5 ++ .../common/types/expression_renderers.ts | 14 ++-- .../utils/consolidate_metric_columns.test.ts | 30 +++++---- .../utils/consolidate_metric_columns.ts | 5 +- .../partition_vis_component.test.tsx.snap | 64 +++++++++---------- .../partition_vis_component.test.tsx | 2 + .../components/partition_vis_component.tsx | 10 ++- .../expression_partition_vis/public/mocks.ts | 1 + .../visualizations/partition/to_expression.ts | 17 +++++ 12 files changed, 107 insertions(+), 55 deletions(-) diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/i18n.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/i18n.ts index ec4357c269f37..974e3d660e5d4 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/i18n.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/i18n.ts @@ -17,6 +17,10 @@ export const strings = { i18n.translate('expressionPartitionVis.reusable.function.args.metricHelpText', { defaultMessage: 'Metric dimensions config', }), + getMetricToLabelHelp: () => + i18n.translate('expressionXY.layer.columnToLabel.help', { + defaultMessage: 'JSON key-value pairs of column ID to label', + }), getBucketsArgHelp: () => i18n.translate('expressionPartitionVis.reusable.function.args.bucketsHelpText', { defaultMessage: 'Buckets dimensions config', diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts index 1e032b33741d8..bfecd3bde8f6c 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts @@ -31,6 +31,10 @@ export const pieVisFunction = (): PieVisExpressionFunctionDefinition => ({ required: true, multi: true, }, + metricsToLabels: { + types: ['string'], + help: strings.getMetricToLabelHelp(), + }, buckets: { types: ['vis_dimension', 'string'], help: strings.getBucketsArgHelp(), @@ -152,6 +156,7 @@ export const pieVisFunction = (): PieVisExpressionFunctionDefinition => ({ const visConfig: PartitionVisParams = { ...args, + metricsToLabels: JSON.parse(args.metricsToLabels), ariaLabel: args.ariaLabel ?? (handlers.variables?.embeddableTitle as string) ?? diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/treemap_vis_function.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/treemap_vis_function.ts index 7ba5c842907d6..3e1dc9f5581cb 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/treemap_vis_function.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/treemap_vis_function.ts @@ -31,6 +31,10 @@ export const treemapVisFunction = (): TreemapVisExpressionFunctionDefinition => required: true, multi: true, }, + metricsToLabels: { + types: ['string'], + help: strings.getMetricToLabelHelp(), + }, buckets: { types: ['vis_dimension'], help: strings.getBucketsArgHelp(), @@ -132,6 +136,7 @@ export const treemapVisFunction = (): TreemapVisExpressionFunctionDefinition => const visConfig: PartitionVisParams = { ...args, + metricsToLabels: JSON.parse(args.metricsToLabels), ariaLabel: args.ariaLabel ?? (handlers.variables?.embeddableTitle as string) ?? diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/waffle_vis_function.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/waffle_vis_function.ts index 20b72dad3dfaa..b522ffa017fa4 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/waffle_vis_function.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/waffle_vis_function.ts @@ -31,6 +31,10 @@ export const waffleVisFunction = (): WaffleVisExpressionFunctionDefinition => ({ required: true, multi: true, }, + metricsToLabels: { + types: ['string'], + help: strings.getMetricToLabelHelp(), + }, bucket: { types: ['vis_dimension'], help: strings.getBucketArgHelp(), @@ -127,6 +131,7 @@ export const waffleVisFunction = (): WaffleVisExpressionFunctionDefinition => ({ const buckets = args.bucket ? [args.bucket] : []; const visConfig: PartitionVisParams = { ...args, + metricsToLabels: JSON.parse(args.metricsToLabels), ariaLabel: args.ariaLabel ?? (handlers.variables?.embeddableTitle as string) ?? diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/types/expression_renderers.ts b/src/plugins/chart_expressions/expression_partition_vis/common/types/expression_renderers.ts index 302a3bbb80398..1611d3565ded1 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/types/expression_renderers.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/types/expression_renderers.ts @@ -58,6 +58,9 @@ interface VisCommonParams { } interface VisCommonConfig extends VisCommonParams { + metrics: Array; + metricsToLabels: string; + buckets?: Array; splitColumn?: Array; splitRow?: Array; labels: ExpressionValuePartitionLabels; @@ -66,6 +69,7 @@ interface VisCommonConfig extends VisCommonParams { export interface PartitionVisParams extends VisCommonParams { dimensions: Dimensions; + metricsToLabels: Record; labels: LabelsParams; palette: PaletteOutput; isDonut?: boolean; @@ -78,8 +82,6 @@ export interface PartitionVisParams extends VisCommonParams { } export interface PieVisConfig extends VisCommonConfig { - metrics: Array; - buckets?: Array; partitionByColumn?: boolean; isDonut: boolean; emptySizeRatio?: EmptySizeRatios; @@ -90,19 +92,15 @@ export interface PieVisConfig extends VisCommonConfig { } export interface TreemapVisConfig extends VisCommonConfig { - metrics: Array; - buckets?: Array; nestedLegend: boolean; } -export interface MosaicVisConfig extends VisCommonConfig { +export interface MosaicVisConfig extends Omit { metric: ExpressionValueVisDimension | string; - buckets?: Array; nestedLegend: boolean; } -export interface WaffleVisConfig extends VisCommonConfig { - metrics: Array; +export interface WaffleVisConfig extends Omit { bucket?: ExpressionValueVisDimension | string; showValuesInLegend: boolean; } diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/utils/consolidate_metric_columns.test.ts b/src/plugins/chart_expressions/expression_partition_vis/common/utils/consolidate_metric_columns.test.ts index e8cc121e2060d..6d94809e403b7 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/utils/consolidate_metric_columns.test.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/utils/consolidate_metric_columns.test.ts @@ -51,7 +51,10 @@ describe('consolidateMetricColumns', () => { ], }; - const result = consolidateMetricColumns(table, ['1', '2'], ['3', '4']); + const result = consolidateMetricColumns(table, ['1', '2'], ['3', '4'], { + 3: 'metric1 label', + 4: 'metric2 label', + }); expect(result.bucketAccessors).toEqual(['1', '2', 'metric-name']); expect(result.metricAccessor).toEqual('value'); expect(result.table).toMatchInlineSnapshot(` @@ -93,49 +96,49 @@ describe('consolidateMetricColumns', () => { Object { "1": "square", "2": "red", - "metric-name": "metric1", + "metric-name": "metric1 label", "value": 1, }, Object { "1": "square", "2": "red", - "metric-name": "metric2", + "metric-name": "metric2 label", "value": 2, }, Object { "1": "square", "2": "blue", - "metric-name": "metric1", + "metric-name": "metric1 label", "value": 3, }, Object { "1": "square", "2": "blue", - "metric-name": "metric2", + "metric-name": "metric2 label", "value": 4, }, Object { "1": "circle", "2": "red", - "metric-name": "metric1", + "metric-name": "metric1 label", "value": 5, }, Object { "1": "circle", "2": "red", - "metric-name": "metric2", + "metric-name": "metric2 label", "value": 6, }, Object { "1": "circle", "2": "blue", - "metric-name": "metric1", + "metric-name": "metric1 label", "value": 7, }, Object { "1": "circle", "2": "blue", - "metric-name": "metric2", + "metric-name": "metric2 label", "value": 8, }, ], @@ -180,7 +183,9 @@ describe('consolidateMetricColumns', () => { const bucketAccessors = ['1', '2']; const metricAccessors = ['3']; - const result = consolidateMetricColumns(table, bucketAccessors, metricAccessors); + const result = consolidateMetricColumns(table, bucketAccessors, metricAccessors, { + 3: 'metric1', + }); expect(result.table).toEqual(table); expect(result.bucketAccessors).toEqual(bucketAccessors); @@ -214,7 +219,10 @@ describe('consolidateMetricColumns', () => { ], }; - const result = consolidateMetricColumns(table, undefined, ['3', '4']); + const result = consolidateMetricColumns(table, undefined, ['3', '4'], { + 3: 'metric1', + 4: 'metric2', + }); expect(result.bucketAccessors).toEqual(['metric-name']); expect(result.metricAccessor).toEqual('value'); expect(result.table).toMatchInlineSnapshot(` diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/utils/consolidate_metric_columns.ts b/src/plugins/chart_expressions/expression_partition_vis/common/utils/consolidate_metric_columns.ts index 7b597b21c6f6d..009744cc06f3e 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/utils/consolidate_metric_columns.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/utils/consolidate_metric_columns.ts @@ -17,7 +17,8 @@ function nonNullable(value: T): value is NonNullable { export const consolidateMetricColumns = ( table: Datatable, bucketAccessors: Array = [], - metricAccessors: Array + metricAccessors: Array, + metricsToLabels: Record ): { table: Datatable; metricAccessor: string | ExpressionValueVisDimension | undefined; @@ -52,7 +53,7 @@ export const consolidateMetricColumns = ( newRow[id] = row[id]; }); - newRow[nameColumnId] = metricCol.name; + newRow[nameColumnId] = metricsToLabels[metricCol.id]; newRow[valueColumnId] = row[metricCol.id]; transposedRows.push(newRow); diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/components/__snapshots__/partition_vis_component.test.tsx.snap b/src/plugins/chart_expressions/expression_partition_vis/public/components/__snapshots__/partition_vis_component.test.tsx.snap index 2c898e7f037f6..c91e491887a99 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/components/__snapshots__/partition_vis_component.test.tsx.snap +++ b/src/plugins/chart_expressions/expression_partition_vis/public/components/__snapshots__/partition_vis_component.test.tsx.snap @@ -919,97 +919,97 @@ exports[`PartitionVisComponent should render correct structure for multi-metric Object { "col-0-2": "Logstash Airways", "col-2-3": 0, - "metric-name": "Count", + "metric-name": "metric2 label", "value": 797, }, Object { "col-0-2": "Logstash Airways", "col-2-3": 0, - "metric-name": "Count", + "metric-name": "metric1 label", "value": 689, }, Object { "col-0-2": "Logstash Airways", "col-2-3": 1, - "metric-name": "Count", + "metric-name": "metric2 label", "value": 797, }, Object { "col-0-2": "Logstash Airways", "col-2-3": 1, - "metric-name": "Count", + "metric-name": "metric1 label", "value": 108, }, Object { "col-0-2": "JetBeats", "col-2-3": 0, - "metric-name": "Count", + "metric-name": "metric2 label", "value": 766, }, Object { "col-0-2": "JetBeats", "col-2-3": 0, - "metric-name": "Count", + "metric-name": "metric1 label", "value": 654, }, Object { "col-0-2": "JetBeats", "col-2-3": 1, - "metric-name": "Count", + "metric-name": "metric2 label", "value": 766, }, Object { "col-0-2": "JetBeats", "col-2-3": 1, - "metric-name": "Count", + "metric-name": "metric1 label", "value": 112, }, Object { "col-0-2": "ES-Air", "col-2-3": 0, - "metric-name": "Count", + "metric-name": "metric2 label", "value": 744, }, Object { "col-0-2": "ES-Air", "col-2-3": 0, - "metric-name": "Count", + "metric-name": "metric1 label", "value": 665, }, Object { "col-0-2": "ES-Air", "col-2-3": 1, - "metric-name": "Count", + "metric-name": "metric2 label", "value": 744, }, Object { "col-0-2": "ES-Air", "col-2-3": 1, - "metric-name": "Count", + "metric-name": "metric1 label", "value": 79, }, Object { "col-0-2": "Kibana Airlines", "col-2-3": 0, - "metric-name": "Count", + "metric-name": "metric2 label", "value": 731, }, Object { "col-0-2": "Kibana Airlines", "col-2-3": 0, - "metric-name": "Count", + "metric-name": "metric1 label", "value": 655, }, Object { "col-0-2": "Kibana Airlines", "col-2-3": 1, - "metric-name": "Count", + "metric-name": "metric2 label", "value": 731, }, Object { "col-0-2": "Kibana Airlines", "col-2-3": 1, - "metric-name": "Count", + "metric-name": "metric1 label", "value": 76, }, ], @@ -1129,97 +1129,97 @@ exports[`PartitionVisComponent should render correct structure for multi-metric Object { "col-0-2": "Logstash Airways", "col-2-3": 0, - "metric-name": "Count", + "metric-name": "metric2 label", "value": 797, }, Object { "col-0-2": "Logstash Airways", "col-2-3": 0, - "metric-name": "Count", + "metric-name": "metric1 label", "value": 689, }, Object { "col-0-2": "Logstash Airways", "col-2-3": 1, - "metric-name": "Count", + "metric-name": "metric2 label", "value": 797, }, Object { "col-0-2": "Logstash Airways", "col-2-3": 1, - "metric-name": "Count", + "metric-name": "metric1 label", "value": 108, }, Object { "col-0-2": "JetBeats", "col-2-3": 0, - "metric-name": "Count", + "metric-name": "metric2 label", "value": 766, }, Object { "col-0-2": "JetBeats", "col-2-3": 0, - "metric-name": "Count", + "metric-name": "metric1 label", "value": 654, }, Object { "col-0-2": "JetBeats", "col-2-3": 1, - "metric-name": "Count", + "metric-name": "metric2 label", "value": 766, }, Object { "col-0-2": "JetBeats", "col-2-3": 1, - "metric-name": "Count", + "metric-name": "metric1 label", "value": 112, }, Object { "col-0-2": "ES-Air", "col-2-3": 0, - "metric-name": "Count", + "metric-name": "metric2 label", "value": 744, }, Object { "col-0-2": "ES-Air", "col-2-3": 0, - "metric-name": "Count", + "metric-name": "metric1 label", "value": 665, }, Object { "col-0-2": "ES-Air", "col-2-3": 1, - "metric-name": "Count", + "metric-name": "metric2 label", "value": 744, }, Object { "col-0-2": "ES-Air", "col-2-3": 1, - "metric-name": "Count", + "metric-name": "metric1 label", "value": 79, }, Object { "col-0-2": "Kibana Airlines", "col-2-3": 0, - "metric-name": "Count", + "metric-name": "metric2 label", "value": 731, }, Object { "col-0-2": "Kibana Airlines", "col-2-3": 0, - "metric-name": "Count", + "metric-name": "metric1 label", "value": 655, }, Object { "col-0-2": "Kibana Airlines", "col-2-3": 1, - "metric-name": "Count", + "metric-name": "metric2 label", "value": 731, }, Object { "col-0-2": "Kibana Airlines", "col-2-3": 1, - "metric-name": "Count", + "metric-name": "metric1 label", "value": 76, }, ] diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.test.tsx b/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.test.tsx index b555b309cbde4..b26d368caa438 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.test.tsx +++ b/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.test.tsx @@ -99,6 +99,8 @@ describe('PartitionVisComponent', function () { localParams.dimensions.metrics = [...localParams.dimensions.metrics, 'col-3-1']; + localParams.metricsToLabels = { 'col-3-1': 'metric1 label', 'col-1-1': 'metric2 label' }; + const component = shallow(); expect(component).toMatchSnapshot(); }); diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.tsx b/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.tsx index 968de5e38caa7..ed1789f2ae4a9 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.tsx +++ b/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.tsx @@ -112,9 +112,15 @@ const PartitionVisComponent = (props: PartitionVisComponentProps) => { consolidateMetricColumns( originalVisData, visParams.dimensions.buckets, - visParams.dimensions.metrics + visParams.dimensions.metrics, + visParams.metricsToLabels ), - [originalVisData, visParams.dimensions.buckets, visParams.dimensions.metrics] + [ + originalVisData, + visParams.dimensions.buckets, + visParams.dimensions.metrics, + visParams.metricsToLabels, + ] ); const { bucketColumns, metricColumn } = useMemo( diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/mocks.ts b/src/plugins/chart_expressions/expression_partition_vis/public/mocks.ts index d9aaf309fc108..c125243f3a09a 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/mocks.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/public/mocks.ts @@ -292,6 +292,7 @@ export const createMockPartitionVisParams = (): PartitionVisParams => { name: 'default', type: 'palette', }, + metricsToLabels: {}, dimensions: { metrics: [ { diff --git a/x-pack/plugins/lens/public/visualizations/partition/to_expression.ts b/x-pack/plugins/lens/public/visualizations/partition/to_expression.ts index 89d53f69cc567..2e02613a54bfd 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/to_expression.ts +++ b/x-pack/plugins/lens/public/visualizations/partition/to_expression.ts @@ -64,6 +64,20 @@ type GenerateLabelsAstArguments = ( layer: PieLayerState ) => [Ast]; +export const getColumnToLabelMap = ( + columnIds: string[], + datasource: DatasourcePublicAPI | undefined +) => { + const columnToLabel: Record = {}; + columnIds.forEach((accessor) => { + const operation = datasource?.getOperationForColumnId(accessor); + if (operation?.label) { + columnToLabel[accessor] = operation.label; + } + }); + return columnToLabel; +}; + export const getSortedGroups = ( datasource: DatasourcePublicAPI | undefined, layer: PieLayerState, @@ -142,6 +156,9 @@ const generateCommonArguments = ( metrics: (layer.allowMultipleMetrics ? layer.metrics : [layer.metrics[0]]).map( prepareDimension ), + metricsToLabels: JSON.stringify( + getColumnToLabelMap(layer.metrics, datasourceLayers[layer.layerId]) + ), legendDisplay: (attributes.isPreview ? LegendDisplay.HIDE : layer.legendDisplay) as PartitionVisLegendDisplay, From 53c38889a5b1d8fc6285b1c90cd86c60a4ec8757 Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Wed, 9 Nov 2022 15:25:25 -0600 Subject: [PATCH 61/70] update visualization state in journey dashboard --- x-pack/performance/kbn_archives/flights_no_map_dashboard.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/performance/kbn_archives/flights_no_map_dashboard.json b/x-pack/performance/kbn_archives/flights_no_map_dashboard.json index f0afa9052ddae..cac545ba1d836 100644 --- a/x-pack/performance/kbn_archives/flights_no_map_dashboard.json +++ b/x-pack/performance/kbn_archives/flights_no_map_dashboard.json @@ -173,7 +173,7 @@ "searchSourceJSON": "{\"query\":{\"language\":\"kuery\",\"query\":\"\"},\"filter\":[]}" }, "optionsJSON": "{\"hidePanelTitles\":false,\"useMargins\":true}", - "panelsJSON": "[{\"version\":\"8.6.0\",\"type\":\"search\",\"gridData\":{\"x\":0,\"y\":47,\"w\":48,\"h\":15,\"i\":\"4\"},\"panelIndex\":\"4\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_4\"},{\"version\":\"8.6.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":16,\"w\":24,\"h\":9,\"i\":\"7\"},\"panelIndex\":\"7\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_7\"},{\"version\":\"8.6.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":36,\"w\":24,\"h\":11,\"i\":\"10\"},\"panelIndex\":\"10\",\"embeddableConfig\":{\"vis\":{\"colors\":{\"Count\":\"#1F78C1\"},\"legendOpen\":false},\"enhancements\":{}},\"panelRefName\":\"panel_10\"},{\"version\":\"8.6.0\",\"type\":\"visualization\",\"gridData\":{\"x\":36,\"y\":36,\"w\":12,\"h\":11,\"i\":\"21\"},\"panelIndex\":\"21\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_21\"},{\"version\":\"8.6.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":0,\"w\":24,\"h\":8,\"i\":\"6afc61f7-e2d5-45a3-9e7a-281160ad3eb9\"},\"panelIndex\":\"6afc61f7-e2d5-45a3-9e7a-281160ad3eb9\",\"embeddableConfig\":{\"savedVis\":{\"title\":\"[Flights] Markdown Instructions\",\"description\":\"\",\"type\":\"markdown\",\"params\":{\"fontSize\":10,\"openLinksInNewTab\":true,\"markdown\":\"## Sample Flight data\\nThis dashboard contains sample data for you to play with. You can view it, search it, and interact with the visualizations. For more information about Kibana, check our [docs](https://www.elastic.co/guide/en/kibana/current/index.html).\"},\"uiState\":{},\"data\":{\"aggs\":[],\"searchSource\":{}}},\"hidePanelTitles\":true,\"enhancements\":{}}},{\"version\":\"8.6.0\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":0,\"w\":8,\"h\":8,\"i\":\"392b4936-f753-47bc-a98d-a4e41a0a4cd4\"},\"panelIndex\":\"392b4936-f753-47bc-a98d-a4e41a0a4cd4\",\"embeddableConfig\":{\"enhancements\":{},\"attributes\":{\"title\":\"[Flights] Total Flights\",\"description\":\"\",\"visualizationType\":\"lnsLegacyMetric\",\"state\":{\"datasourceStates\":{\"formBased\":{\"layers\":{\"8fa993db-c147-4954-adf7-4ff264d42576\":{\"columns\":{\"81124c45-6ab6-42f4-8859-495d55eb8065\":{\"label\":\"Total flights\",\"dataType\":\"number\",\"operationType\":\"count\",\"isBucketed\":false,\"scale\":\"ratio\",\"sourceField\":\"___records___\",\"customLabel\":true}},\"columnOrder\":[\"81124c45-6ab6-42f4-8859-495d55eb8065\"],\"incompleteColumns\":{}}}}},\"visualization\":{\"layerId\":\"8fa993db-c147-4954-adf7-4ff264d42576\",\"accessor\":\"81124c45-6ab6-42f4-8859-495d55eb8065\",\"layerType\":\"data\",\"textAlign\":\"center\",\"titlePosition\":\"bottom\",\"size\":\"xl\"},\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filters\":[]},\"references\":[{\"id\":\"d3d7af60-4c81-11e8-b3d7-01146121b73d\",\"name\":\"indexpattern-datasource-current-indexpattern\",\"type\":\"index-pattern\"},{\"id\":\"d3d7af60-4c81-11e8-b3d7-01146121b73d\",\"name\":\"indexpattern-datasource-layer-8fa993db-c147-4954-adf7-4ff264d42576\",\"type\":\"index-pattern\"}]},\"hidePanelTitles\":true}},{\"version\":\"8.6.0\",\"type\":\"lens\",\"gridData\":{\"x\":32,\"y\":0,\"w\":8,\"h\":4,\"i\":\"9271deff-5a61-4665-83fc-f9fdc6bf0c0b\"},\"panelIndex\":\"9271deff-5a61-4665-83fc-f9fdc6bf0c0b\",\"embeddableConfig\":{\"attributes\":{\"title\":\"\",\"type\":\"lens\",\"visualizationType\":\"lnsLegacyMetric\",\"state\":{\"datasourceStates\":{\"formBased\":{\"layers\":{\"b4712d43-1e84-4f5b-878d-8e38ba748317\":{\"columns\":{\"7e8fe9b1-f45c-4f3d-9561-30febcd357ecX0\":{\"label\":\"Part of count(kql='FlightDelay : true') / count()\",\"dataType\":\"number\",\"operationType\":\"count\",\"isBucketed\":false,\"scale\":\"ratio\",\"sourceField\":\"___records___\",\"filter\":{\"query\":\"FlightDelay : true\",\"language\":\"kuery\"},\"customLabel\":true},\"7e8fe9b1-f45c-4f3d-9561-30febcd357ecX1\":{\"label\":\"Part of count(kql='FlightDelay : true') / count()\",\"dataType\":\"number\",\"operationType\":\"count\",\"isBucketed\":false,\"scale\":\"ratio\",\"sourceField\":\"___records___\",\"customLabel\":true},\"7e8fe9b1-f45c-4f3d-9561-30febcd357ecX2\":{\"label\":\"Part of count(kql='FlightDelay : true') / count()\",\"dataType\":\"number\",\"operationType\":\"math\",\"isBucketed\":false,\"scale\":\"ratio\",\"params\":{\"tinymathAst\":{\"type\":\"function\",\"name\":\"divide\",\"args\":[\"7e8fe9b1-f45c-4f3d-9561-30febcd357ecX0\",\"7e8fe9b1-f45c-4f3d-9561-30febcd357ecX1\"],\"location\":{\"min\":0,\"max\":41},\"text\":\"count(kql='FlightDelay : true') / count()\"}},\"references\":[\"7e8fe9b1-f45c-4f3d-9561-30febcd357ecX0\",\"7e8fe9b1-f45c-4f3d-9561-30febcd357ecX1\"],\"customLabel\":true},\"7e8fe9b1-f45c-4f3d-9561-30febcd357ec\":{\"label\":\"Delayed\",\"dataType\":\"number\",\"operationType\":\"formula\",\"isBucketed\":false,\"scale\":\"ratio\",\"params\":{\"formula\":\"count(kql='FlightDelay : true') / count()\",\"isFormulaBroken\":false,\"format\":{\"id\":\"percent\",\"params\":{\"decimals\":1}}},\"references\":[\"7e8fe9b1-f45c-4f3d-9561-30febcd357ecX2\"],\"customLabel\":true}},\"columnOrder\":[\"7e8fe9b1-f45c-4f3d-9561-30febcd357ec\",\"7e8fe9b1-f45c-4f3d-9561-30febcd357ecX0\",\"7e8fe9b1-f45c-4f3d-9561-30febcd357ecX1\",\"7e8fe9b1-f45c-4f3d-9561-30febcd357ecX2\"],\"incompleteColumns\":{}}}}},\"visualization\":{\"layerId\":\"b4712d43-1e84-4f5b-878d-8e38ba748317\",\"accessor\":\"7e8fe9b1-f45c-4f3d-9561-30febcd357ec\",\"layerType\":\"data\",\"textAlign\":\"center\",\"titlePosition\":\"bottom\",\"size\":\"xl\"},\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filters\":[]},\"references\":[{\"id\":\"d3d7af60-4c81-11e8-b3d7-01146121b73d\",\"name\":\"indexpattern-datasource-current-indexpattern\",\"type\":\"index-pattern\"},{\"id\":\"d3d7af60-4c81-11e8-b3d7-01146121b73d\",\"name\":\"indexpattern-datasource-layer-b4712d43-1e84-4f5b-878d-8e38ba748317\",\"type\":\"index-pattern\"}]},\"enhancements\":{}}},{\"version\":\"8.6.0\",\"type\":\"lens\",\"gridData\":{\"x\":40,\"y\":0,\"w\":8,\"h\":4,\"i\":\"aa591c29-1a31-4ee1-a71d-b829c06fd162\"},\"panelIndex\":\"aa591c29-1a31-4ee1-a71d-b829c06fd162\",\"embeddableConfig\":{\"attributes\":{\"title\":\"\",\"type\":\"lens\",\"visualizationType\":\"lnsLegacyMetric\",\"state\":{\"datasourceStates\":{\"formBased\":{\"layers\":{\"b4712d43-1e84-4f5b-878d-8e38ba748317\":{\"columns\":{\"c7851241-5526-499a-960b-357af8c2ce5bX0\":{\"label\":\"Part of Delayed\",\"dataType\":\"number\",\"operationType\":\"count\",\"isBucketed\":false,\"scale\":\"ratio\",\"sourceField\":\"___records___\",\"customLabel\":true},\"c7851241-5526-499a-960b-357af8c2ce5bX1\":{\"label\":\"Part of Delayed\",\"dataType\":\"number\",\"operationType\":\"count\",\"isBucketed\":false,\"scale\":\"ratio\",\"sourceField\":\"___records___\",\"timeShift\":\"1w\",\"customLabel\":true},\"c7851241-5526-499a-960b-357af8c2ce5bX2\":{\"label\":\"Part of Delayed\",\"dataType\":\"number\",\"operationType\":\"math\",\"isBucketed\":false,\"scale\":\"ratio\",\"params\":{\"tinymathAst\":{\"type\":\"function\",\"name\":\"subtract\",\"args\":[{\"type\":\"function\",\"name\":\"divide\",\"args\":[\"c7851241-5526-499a-960b-357af8c2ce5bX0\",\"c7851241-5526-499a-960b-357af8c2ce5bX1\"],\"location\":{\"min\":0,\"max\":28},\"text\":\"count() / count(shift='1w') \"},1],\"location\":{\"min\":0,\"max\":31},\"text\":\"count() / count(shift='1w') - 1\"}},\"references\":[\"c7851241-5526-499a-960b-357af8c2ce5bX0\",\"c7851241-5526-499a-960b-357af8c2ce5bX1\"],\"customLabel\":true},\"c7851241-5526-499a-960b-357af8c2ce5b\":{\"label\":\"Delayed vs 1 week earlier\",\"dataType\":\"number\",\"operationType\":\"formula\",\"isBucketed\":false,\"scale\":\"ratio\",\"params\":{\"formula\":\"count() / count(shift='1w') - 1\",\"isFormulaBroken\":false,\"format\":{\"id\":\"percent\",\"params\":{\"decimals\":1}}},\"references\":[\"c7851241-5526-499a-960b-357af8c2ce5bX2\"],\"customLabel\":true}},\"columnOrder\":[\"c7851241-5526-499a-960b-357af8c2ce5b\",\"c7851241-5526-499a-960b-357af8c2ce5bX2\",\"c7851241-5526-499a-960b-357af8c2ce5bX0\",\"c7851241-5526-499a-960b-357af8c2ce5bX1\"],\"incompleteColumns\":{}}}}},\"visualization\":{\"layerId\":\"b4712d43-1e84-4f5b-878d-8e38ba748317\",\"accessor\":\"c7851241-5526-499a-960b-357af8c2ce5b\",\"layerType\":\"data\",\"textAlign\":\"center\",\"titlePosition\":\"bottom\",\"size\":\"xl\"},\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filters\":[{\"meta\":{\"alias\":null,\"negate\":false,\"disabled\":false,\"type\":\"phrase\",\"key\":\"FlightDelay\",\"params\":{\"query\":true},\"index\":\"filter-index-pattern-0\"},\"query\":{\"match_phrase\":{\"FlightDelay\":true}},\"$state\":{\"store\":\"appState\"}}]},\"references\":[{\"id\":\"d3d7af60-4c81-11e8-b3d7-01146121b73d\",\"name\":\"indexpattern-datasource-current-indexpattern\",\"type\":\"index-pattern\"},{\"id\":\"d3d7af60-4c81-11e8-b3d7-01146121b73d\",\"name\":\"indexpattern-datasource-layer-b4712d43-1e84-4f5b-878d-8e38ba748317\",\"type\":\"index-pattern\"},{\"id\":\"d3d7af60-4c81-11e8-b3d7-01146121b73d\",\"name\":\"filter-index-pattern-0\",\"type\":\"index-pattern\"}]},\"enhancements\":{}}},{\"version\":\"8.6.0\",\"type\":\"lens\",\"gridData\":{\"x\":32,\"y\":4,\"w\":8,\"h\":4,\"i\":\"b766e3b8-4544-46ed-99e6-9ecc4847e2a2\"},\"panelIndex\":\"b766e3b8-4544-46ed-99e6-9ecc4847e2a2\",\"embeddableConfig\":{\"attributes\":{\"title\":\"\",\"type\":\"lens\",\"visualizationType\":\"lnsLegacyMetric\",\"state\":{\"datasourceStates\":{\"formBased\":{\"layers\":{\"b4712d43-1e84-4f5b-878d-8e38ba748317\":{\"columns\":{\"7e8fe9b1-f45c-4f3d-9561-30febcd357ecX0\":{\"label\":\"Part of Cancelled\",\"dataType\":\"number\",\"operationType\":\"count\",\"isBucketed\":false,\"scale\":\"ratio\",\"sourceField\":\"___records___\",\"filter\":{\"query\":\"Cancelled : true\",\"language\":\"kuery\"},\"customLabel\":true},\"7e8fe9b1-f45c-4f3d-9561-30febcd357ecX1\":{\"label\":\"Part of Cancelled\",\"dataType\":\"number\",\"operationType\":\"count\",\"isBucketed\":false,\"scale\":\"ratio\",\"sourceField\":\"___records___\",\"customLabel\":true},\"7e8fe9b1-f45c-4f3d-9561-30febcd357ecX2\":{\"label\":\"Part of Cancelled\",\"dataType\":\"number\",\"operationType\":\"math\",\"isBucketed\":false,\"scale\":\"ratio\",\"params\":{\"tinymathAst\":{\"type\":\"function\",\"name\":\"divide\",\"args\":[\"7e8fe9b1-f45c-4f3d-9561-30febcd357ecX0\",\"7e8fe9b1-f45c-4f3d-9561-30febcd357ecX1\"],\"location\":{\"min\":0,\"max\":39},\"text\":\"count(kql='Cancelled : true') / count()\"}},\"references\":[\"7e8fe9b1-f45c-4f3d-9561-30febcd357ecX0\",\"7e8fe9b1-f45c-4f3d-9561-30febcd357ecX1\"],\"customLabel\":true},\"7e8fe9b1-f45c-4f3d-9561-30febcd357ec\":{\"label\":\"Cancelled\",\"dataType\":\"number\",\"operationType\":\"formula\",\"isBucketed\":false,\"scale\":\"ratio\",\"params\":{\"formula\":\"count(kql='Cancelled : true') / count()\",\"isFormulaBroken\":false,\"format\":{\"id\":\"percent\",\"params\":{\"decimals\":1}}},\"references\":[\"7e8fe9b1-f45c-4f3d-9561-30febcd357ecX2\"],\"customLabel\":true}},\"columnOrder\":[\"7e8fe9b1-f45c-4f3d-9561-30febcd357ec\",\"7e8fe9b1-f45c-4f3d-9561-30febcd357ecX0\",\"7e8fe9b1-f45c-4f3d-9561-30febcd357ecX1\",\"7e8fe9b1-f45c-4f3d-9561-30febcd357ecX2\"],\"incompleteColumns\":{}}}}},\"visualization\":{\"layerId\":\"b4712d43-1e84-4f5b-878d-8e38ba748317\",\"accessor\":\"7e8fe9b1-f45c-4f3d-9561-30febcd357ec\",\"layerType\":\"data\",\"textAlign\":\"center\",\"titlePosition\":\"bottom\",\"size\":\"xl\"},\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filters\":[]},\"references\":[{\"id\":\"d3d7af60-4c81-11e8-b3d7-01146121b73d\",\"name\":\"indexpattern-datasource-current-indexpattern\",\"type\":\"index-pattern\"},{\"id\":\"d3d7af60-4c81-11e8-b3d7-01146121b73d\",\"name\":\"indexpattern-datasource-layer-b4712d43-1e84-4f5b-878d-8e38ba748317\",\"type\":\"index-pattern\"}]},\"enhancements\":{}}},{\"version\":\"8.6.0\",\"type\":\"lens\",\"gridData\":{\"x\":40,\"y\":4,\"w\":8,\"h\":4,\"i\":\"2e33ade5-96e5-40b4-b460-493e5d4fa834\"},\"panelIndex\":\"2e33ade5-96e5-40b4-b460-493e5d4fa834\",\"embeddableConfig\":{\"attributes\":{\"title\":\"\",\"type\":\"lens\",\"visualizationType\":\"lnsLegacyMetric\",\"state\":{\"datasourceStates\":{\"formBased\":{\"layers\":{\"b4712d43-1e84-4f5b-878d-8e38ba748317\":{\"columns\":{\"c7851241-5526-499a-960b-357af8c2ce5bX0\":{\"label\":\"Part of Delayed vs 1 week earlier\",\"dataType\":\"number\",\"operationType\":\"count\",\"isBucketed\":false,\"scale\":\"ratio\",\"sourceField\":\"___records___\",\"customLabel\":true},\"c7851241-5526-499a-960b-357af8c2ce5bX1\":{\"label\":\"Part of Delayed vs 1 week earlier\",\"dataType\":\"number\",\"operationType\":\"count\",\"isBucketed\":false,\"scale\":\"ratio\",\"sourceField\":\"___records___\",\"timeShift\":\"1w\",\"customLabel\":true},\"c7851241-5526-499a-960b-357af8c2ce5bX2\":{\"label\":\"Part of Delayed vs 1 week earlier\",\"dataType\":\"number\",\"operationType\":\"math\",\"isBucketed\":false,\"scale\":\"ratio\",\"params\":{\"tinymathAst\":{\"type\":\"function\",\"name\":\"subtract\",\"args\":[{\"type\":\"function\",\"name\":\"divide\",\"args\":[\"c7851241-5526-499a-960b-357af8c2ce5bX0\",\"c7851241-5526-499a-960b-357af8c2ce5bX1\"],\"location\":{\"min\":0,\"max\":28},\"text\":\"count() / count(shift='1w') \"},1],\"location\":{\"min\":0,\"max\":31},\"text\":\"count() / count(shift='1w') - 1\"}},\"references\":[\"c7851241-5526-499a-960b-357af8c2ce5bX0\",\"c7851241-5526-499a-960b-357af8c2ce5bX1\"],\"customLabel\":true},\"c7851241-5526-499a-960b-357af8c2ce5b\":{\"label\":\"Cancelled vs 1 week earlier\",\"dataType\":\"number\",\"operationType\":\"formula\",\"isBucketed\":false,\"scale\":\"ratio\",\"params\":{\"formula\":\"count() / count(shift='1w') - 1\",\"isFormulaBroken\":false,\"format\":{\"id\":\"percent\",\"params\":{\"decimals\":1}}},\"references\":[\"c7851241-5526-499a-960b-357af8c2ce5bX2\"],\"customLabel\":true}},\"columnOrder\":[\"c7851241-5526-499a-960b-357af8c2ce5b\",\"c7851241-5526-499a-960b-357af8c2ce5bX2\",\"c7851241-5526-499a-960b-357af8c2ce5bX0\",\"c7851241-5526-499a-960b-357af8c2ce5bX1\"],\"incompleteColumns\":{}}}}},\"visualization\":{\"layerId\":\"b4712d43-1e84-4f5b-878d-8e38ba748317\",\"accessor\":\"c7851241-5526-499a-960b-357af8c2ce5b\",\"layerType\":\"data\",\"textAlign\":\"center\",\"titlePosition\":\"bottom\",\"size\":\"xl\"},\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filters\":[{\"meta\":{\"alias\":null,\"negate\":false,\"disabled\":false,\"type\":\"phrase\",\"key\":\"Cancelled\",\"params\":{\"query\":true},\"index\":\"filter-index-pattern-0\"},\"query\":{\"match_phrase\":{\"Cancelled\":true}},\"$state\":{\"store\":\"appState\"}}]},\"references\":[{\"id\":\"d3d7af60-4c81-11e8-b3d7-01146121b73d\",\"name\":\"indexpattern-datasource-current-indexpattern\",\"type\":\"index-pattern\"},{\"id\":\"d3d7af60-4c81-11e8-b3d7-01146121b73d\",\"name\":\"indexpattern-datasource-layer-b4712d43-1e84-4f5b-878d-8e38ba748317\",\"type\":\"index-pattern\"},{\"id\":\"d3d7af60-4c81-11e8-b3d7-01146121b73d\",\"name\":\"filter-index-pattern-0\",\"type\":\"index-pattern\"}]},\"enhancements\":{}}},{\"version\":\"8.6.0\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":8,\"w\":24,\"h\":8,\"i\":\"086ac2e9-dd16-4b45-92b8-1e43ff7e3f65\"},\"panelIndex\":\"086ac2e9-dd16-4b45-92b8-1e43ff7e3f65\",\"embeddableConfig\":{\"attributes\":{\"title\":\"\",\"type\":\"lens\",\"visualizationType\":\"lnsXY\",\"state\":{\"datasourceStates\":{\"formBased\":{\"layers\":{\"03c34665-471c-49c7-acf1-5a11f517421c\":{\"columns\":{\"a5b94e30-4e77-4b0a-9187-1d8b13de1456\":{\"label\":\"timestamp\",\"dataType\":\"date\",\"operationType\":\"date_histogram\",\"sourceField\":\"timestamp\",\"isBucketed\":true,\"scale\":\"interval\",\"params\":{\"interval\":\"auto\",\"includeEmptyRows\":true}},\"3e267327-7317-4310-aee3-320e0f7c1e70\":{\"label\":\"Count of records\",\"dataType\":\"number\",\"operationType\":\"count\",\"isBucketed\":false,\"scale\":\"ratio\",\"sourceField\":\"___records___\"}},\"columnOrder\":[\"a5b94e30-4e77-4b0a-9187-1d8b13de1456\",\"3e267327-7317-4310-aee3-320e0f7c1e70\"],\"incompleteColumns\":{}}}}},\"visualization\":{\"legend\":{\"isVisible\":true,\"position\":\"right\",\"legendSize\":\"auto\"},\"valueLabels\":\"hide\",\"fittingFunction\":\"None\",\"yLeftExtent\":{\"mode\":\"full\"},\"yRightExtent\":{\"mode\":\"custom\",\"lowerBound\":0,\"upperBound\":1},\"axisTitlesVisibilitySettings\":{\"x\":false,\"yLeft\":false,\"yRight\":true},\"tickLabelsVisibilitySettings\":{\"x\":true,\"yLeft\":true,\"yRight\":true},\"gridlinesVisibilitySettings\":{\"x\":true,\"yLeft\":true,\"yRight\":true},\"preferredSeriesType\":\"bar_stacked\",\"layers\":[{\"layerId\":\"03c34665-471c-49c7-acf1-5a11f517421c\",\"accessors\":[\"3e267327-7317-4310-aee3-320e0f7c1e70\"],\"position\":\"top\",\"seriesType\":\"bar_stacked\",\"showGridlines\":false,\"xAccessor\":\"a5b94e30-4e77-4b0a-9187-1d8b13de1456\",\"layerType\":\"data\"}]},\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filters\":[]},\"references\":[{\"id\":\"d3d7af60-4c81-11e8-b3d7-01146121b73d\",\"name\":\"indexpattern-datasource-current-indexpattern\",\"type\":\"index-pattern\"},{\"id\":\"d3d7af60-4c81-11e8-b3d7-01146121b73d\",\"name\":\"indexpattern-datasource-layer-03c34665-471c-49c7-acf1-5a11f517421c\",\"type\":\"index-pattern\"}]},\"hidePanelTitles\":false,\"enhancements\":{}},\"title\":\"[Flights] Flight count\"},{\"version\":\"8.6.0\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":8,\"w\":24,\"h\":28,\"i\":\"fb86b32f-fb7a-45cf-9511-f366fef51bbd\"},\"panelIndex\":\"fb86b32f-fb7a-45cf-9511-f366fef51bbd\",\"embeddableConfig\":{\"attributes\":{\"title\":\"Cities by delay, cancellation\",\"type\":\"lens\",\"visualizationType\":\"lnsDatatable\",\"state\":{\"datasourceStates\":{\"formBased\":{\"layers\":{\"f26e8f7a-4118-4227-bea0-5c02d8b270f7\":{\"columns\":{\"3dd24cb4-45ef-4dd8-b22a-d7b802cb6da0\":{\"label\":\"Top values of OriginCityName\",\"dataType\":\"string\",\"operationType\":\"terms\",\"scale\":\"ordinal\",\"sourceField\":\"OriginCityName\",\"isBucketed\":true,\"params\":{\"size\":1000,\"orderBy\":{\"type\":\"alphabetical\",\"fallback\":true},\"orderDirection\":\"asc\",\"otherBucket\":true,\"missingBucket\":false}},\"52f6f2e9-6242-4c44-be63-b799150e7e60X0\":{\"label\":\"Part of Delay %\",\"dataType\":\"number\",\"operationType\":\"count\",\"isBucketed\":false,\"scale\":\"ratio\",\"sourceField\":\"___records___\",\"filter\":{\"query\":\"FlightDelay : true \",\"language\":\"kuery\"},\"customLabel\":true},\"52f6f2e9-6242-4c44-be63-b799150e7e60X1\":{\"label\":\"Part of Delay %\",\"dataType\":\"number\",\"operationType\":\"count\",\"isBucketed\":false,\"scale\":\"ratio\",\"sourceField\":\"___records___\",\"customLabel\":true},\"52f6f2e9-6242-4c44-be63-b799150e7e60X2\":{\"label\":\"Part of Delay %\",\"dataType\":\"number\",\"operationType\":\"math\",\"isBucketed\":false,\"scale\":\"ratio\",\"params\":{\"tinymathAst\":{\"type\":\"function\",\"name\":\"divide\",\"args\":[\"52f6f2e9-6242-4c44-be63-b799150e7e60X0\",\"52f6f2e9-6242-4c44-be63-b799150e7e60X1\"],\"location\":{\"min\":0,\"max\":42},\"text\":\"count(kql='FlightDelay : true ') / count()\"}},\"references\":[\"52f6f2e9-6242-4c44-be63-b799150e7e60X0\",\"52f6f2e9-6242-4c44-be63-b799150e7e60X1\"],\"customLabel\":true},\"52f6f2e9-6242-4c44-be63-b799150e7e60\":{\"label\":\"Delay %\",\"dataType\":\"number\",\"operationType\":\"formula\",\"isBucketed\":false,\"scale\":\"ratio\",\"params\":{\"formula\":\"count(kql='FlightDelay : true ') / count()\",\"isFormulaBroken\":false,\"format\":{\"id\":\"percent\",\"params\":{\"decimals\":0}}},\"references\":[\"52f6f2e9-6242-4c44-be63-b799150e7e60X2\"],\"customLabel\":true},\"7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6X0\":{\"label\":\"Part of Cancel %\",\"dataType\":\"number\",\"operationType\":\"count\",\"isBucketed\":false,\"scale\":\"ratio\",\"sourceField\":\"___records___\",\"filter\":{\"query\":\"Cancelled: true\",\"language\":\"kuery\"},\"customLabel\":true},\"7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6X1\":{\"label\":\"Part of Cancel %\",\"dataType\":\"number\",\"operationType\":\"count\",\"isBucketed\":false,\"scale\":\"ratio\",\"sourceField\":\"___records___\",\"customLabel\":true},\"7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6X2\":{\"label\":\"Part of Cancel %\",\"dataType\":\"number\",\"operationType\":\"math\",\"isBucketed\":false,\"scale\":\"ratio\",\"params\":{\"tinymathAst\":{\"type\":\"function\",\"name\":\"divide\",\"args\":[\"7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6X0\",\"7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6X1\"],\"location\":{\"min\":0,\"max\":38},\"text\":\"count(kql='Cancelled: true') / count()\"}},\"references\":[\"7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6X0\",\"7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6X1\"],\"customLabel\":true},\"7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6\":{\"label\":\"Cancel %\",\"dataType\":\"number\",\"operationType\":\"formula\",\"isBucketed\":false,\"scale\":\"ratio\",\"params\":{\"formula\":\"count(kql='Cancelled: true') / count()\",\"isFormulaBroken\":false,\"format\":{\"id\":\"percent\",\"params\":{\"decimals\":0}}},\"references\":[\"7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6X2\"],\"customLabel\":true}},\"columnOrder\":[\"3dd24cb4-45ef-4dd8-b22a-d7b802cb6da0\",\"52f6f2e9-6242-4c44-be63-b799150e7e60\",\"52f6f2e9-6242-4c44-be63-b799150e7e60X0\",\"52f6f2e9-6242-4c44-be63-b799150e7e60X1\",\"52f6f2e9-6242-4c44-be63-b799150e7e60X2\",\"7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6X0\",\"7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6X1\",\"7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6X2\",\"7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6\"],\"incompleteColumns\":{}}}}},\"visualization\":{\"columns\":[{\"isTransposed\":false,\"columnId\":\"3dd24cb4-45ef-4dd8-b22a-d7b802cb6da0\",\"width\":262.75},{\"columnId\":\"52f6f2e9-6242-4c44-be63-b799150e7e60\",\"isTransposed\":false,\"width\":302.5,\"colorMode\":\"cell\",\"palette\":{\"name\":\"custom\",\"type\":\"palette\",\"params\":{\"steps\":5,\"stops\":[{\"color\":\"#f7e0b8\",\"stop\":0.6},{\"color\":\"#e7664c\",\"stop\":1}],\"name\":\"custom\",\"colorStops\":[{\"color\":\"#f7e0b8\",\"stop\":0.2},{\"color\":\"#e7664c\",\"stop\":0.6}],\"rangeType\":\"number\",\"rangeMin\":0.2,\"rangeMax\":0.6}},\"alignment\":\"center\"},{\"columnId\":\"7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6\",\"isTransposed\":false,\"alignment\":\"center\",\"colorMode\":\"cell\",\"palette\":{\"name\":\"custom\",\"type\":\"palette\",\"params\":{\"steps\":5,\"stops\":[{\"color\":\"#f7e0b8\",\"stop\":0.6},{\"color\":\"#e7664c\",\"stop\":0.6666666666666666}],\"rangeType\":\"number\",\"name\":\"custom\",\"colorStops\":[{\"color\":\"#f7e0b8\",\"stop\":0.2},{\"color\":\"#e7664c\",\"stop\":0.6}],\"rangeMin\":0.2,\"rangeMax\":0.6}}}],\"layerId\":\"f26e8f7a-4118-4227-bea0-5c02d8b270f7\",\"sorting\":{\"columnId\":\"52f6f2e9-6242-4c44-be63-b799150e7e60\",\"direction\":\"desc\"},\"layerType\":\"data\",\"rowHeight\":\"single\",\"rowHeightLines\":1},\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filters\":[]},\"references\":[{\"id\":\"d3d7af60-4c81-11e8-b3d7-01146121b73d\",\"name\":\"indexpattern-datasource-current-indexpattern\",\"type\":\"index-pattern\"},{\"id\":\"d3d7af60-4c81-11e8-b3d7-01146121b73d\",\"name\":\"indexpattern-datasource-layer-f26e8f7a-4118-4227-bea0-5c02d8b270f7\",\"type\":\"index-pattern\"}]},\"enhancements\":{},\"hidePanelTitles\":false},\"title\":\"[Flights] Most delayed cities\"},{\"version\":\"8.6.0\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":25,\"w\":24,\"h\":11,\"i\":\"0cc42484-16f7-42ec-b38c-9bf8be69cde7\"},\"panelIndex\":\"0cc42484-16f7-42ec-b38c-9bf8be69cde7\",\"embeddableConfig\":{\"attributes\":{\"title\":\"\",\"type\":\"lens\",\"visualizationType\":\"lnsXY\",\"state\":{\"datasourceStates\":{\"formBased\":{\"layers\":{\"e80cc05e-c52a-4e5f-ac71-4b37274867f5\":{\"columns\":{\"caf7421e-93a3-439e-ab0a-fbdead93c21c\":{\"label\":\"Top values of FlightDelayType\",\"dataType\":\"string\",\"operationType\":\"terms\",\"scale\":\"ordinal\",\"sourceField\":\"FlightDelayType\",\"isBucketed\":true,\"params\":{\"size\":10,\"orderBy\":{\"type\":\"column\",\"columnId\":\"0233d302-ec81-4fbe-96cb-7fac84cf035c\"},\"orderDirection\":\"desc\",\"otherBucket\":true,\"missingBucket\":false}},\"13ec79e3-9d73-4536-9056-3d92802bb30a\":{\"label\":\"timestamp\",\"dataType\":\"date\",\"operationType\":\"date_histogram\",\"sourceField\":\"timestamp\",\"isBucketed\":true,\"scale\":\"interval\",\"params\":{\"interval\":\"auto\",\"includeEmptyRows\":true}},\"0233d302-ec81-4fbe-96cb-7fac84cf035c\":{\"label\":\"Count of records\",\"dataType\":\"number\",\"operationType\":\"count\",\"isBucketed\":false,\"scale\":\"ratio\",\"sourceField\":\"___records___\"}},\"columnOrder\":[\"caf7421e-93a3-439e-ab0a-fbdead93c21c\",\"13ec79e3-9d73-4536-9056-3d92802bb30a\",\"0233d302-ec81-4fbe-96cb-7fac84cf035c\"],\"incompleteColumns\":{}}}}},\"visualization\":{\"legend\":{\"isVisible\":true,\"position\":\"bottom\",\"legendSize\":\"auto\"},\"valueLabels\":\"hide\",\"fittingFunction\":\"None\",\"yLeftExtent\":{\"mode\":\"full\"},\"yRightExtent\":{\"mode\":\"full\"},\"axisTitlesVisibilitySettings\":{\"x\":true,\"yLeft\":false,\"yRight\":true},\"tickLabelsVisibilitySettings\":{\"x\":true,\"yLeft\":true,\"yRight\":true},\"gridlinesVisibilitySettings\":{\"x\":true,\"yLeft\":true,\"yRight\":true},\"preferredSeriesType\":\"bar_percentage_stacked\",\"layers\":[{\"layerId\":\"e80cc05e-c52a-4e5f-ac71-4b37274867f5\",\"accessors\":[\"0233d302-ec81-4fbe-96cb-7fac84cf035c\"],\"position\":\"top\",\"seriesType\":\"bar_percentage_stacked\",\"showGridlines\":false,\"palette\":{\"type\":\"palette\",\"name\":\"cool\"},\"xAccessor\":\"13ec79e3-9d73-4536-9056-3d92802bb30a\",\"splitAccessor\":\"caf7421e-93a3-439e-ab0a-fbdead93c21c\",\"layerType\":\"data\"}]},\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filters\":[]},\"references\":[{\"id\":\"d3d7af60-4c81-11e8-b3d7-01146121b73d\",\"name\":\"indexpattern-datasource-current-indexpattern\",\"type\":\"index-pattern\"},{\"id\":\"d3d7af60-4c81-11e8-b3d7-01146121b73d\",\"name\":\"indexpattern-datasource-layer-e80cc05e-c52a-4e5f-ac71-4b37274867f5\",\"type\":\"index-pattern\"}]},\"hidePanelTitles\":false,\"enhancements\":{}},\"title\":\"[Flights] Delay Type\"},{\"version\":\"8.6.0\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":36,\"w\":12,\"h\":11,\"i\":\"5d53db36-2d5a-4adc-af7b-cec4c1a294e0\"},\"panelIndex\":\"5d53db36-2d5a-4adc-af7b-cec4c1a294e0\",\"embeddableConfig\":{\"attributes\":{\"title\":\"\",\"type\":\"lens\",\"visualizationType\":\"lnsPie\",\"state\":{\"datasourceStates\":{\"formBased\":{\"layers\":{\"0c8e136b-a822-4fb3-836d-e06cbea4eea4\":{\"columns\":{\"d1cee8bf-34cf-4141-99d7-ff043ee77b56\":{\"label\":\"Top values of FlightDelayType\",\"dataType\":\"string\",\"operationType\":\"terms\",\"scale\":\"ordinal\",\"sourceField\":\"FlightDelayType\",\"isBucketed\":true,\"params\":{\"size\":10,\"orderBy\":{\"type\":\"column\",\"columnId\":\"aa152ace-ee2d-447b-b86d-459bef4d7880\"},\"orderDirection\":\"desc\",\"otherBucket\":true,\"missingBucket\":false}},\"aa152ace-ee2d-447b-b86d-459bef4d7880\":{\"label\":\"Count of records\",\"dataType\":\"number\",\"operationType\":\"count\",\"isBucketed\":false,\"scale\":\"ratio\",\"sourceField\":\"___records___\"}},\"columnOrder\":[\"d1cee8bf-34cf-4141-99d7-ff043ee77b56\",\"aa152ace-ee2d-447b-b86d-459bef4d7880\"],\"incompleteColumns\":{}}}}},\"visualization\":{\"shape\":\"pie\",\"palette\":{\"type\":\"palette\",\"name\":\"cool\"},\"layers\":[{\"layerId\":\"0c8e136b-a822-4fb3-836d-e06cbea4eea4\",\"metric\":\"aa152ace-ee2d-447b-b86d-459bef4d7880\",\"numberDisplay\":\"percent\",\"categoryDisplay\":\"default\",\"legendDisplay\":\"default\",\"nestedLegend\":false,\"layerType\":\"data\",\"legendSize\":\"auto\",\"primaryGroups\":[\"d1cee8bf-34cf-4141-99d7-ff043ee77b56\"]}]},\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filters\":[{\"meta\":{\"type\":\"phrase\",\"key\":\"FlightDelayType\",\"params\":{\"query\":\"No Delay\"},\"disabled\":false,\"negate\":true,\"alias\":null,\"index\":\"filter-index-pattern-0\"},\"query\":{\"match_phrase\":{\"FlightDelayType\":\"No Delay\"}},\"$state\":{\"store\":\"appState\"}}]},\"references\":[{\"id\":\"d3d7af60-4c81-11e8-b3d7-01146121b73d\",\"name\":\"indexpattern-datasource-current-indexpattern\",\"type\":\"index-pattern\"},{\"id\":\"d3d7af60-4c81-11e8-b3d7-01146121b73d\",\"name\":\"indexpattern-datasource-layer-0c8e136b-a822-4fb3-836d-e06cbea4eea4\",\"type\":\"index-pattern\"},{\"id\":\"d3d7af60-4c81-11e8-b3d7-01146121b73d\",\"name\":\"filter-index-pattern-0\",\"type\":\"index-pattern\"}]},\"enhancements\":{},\"hidePanelTitles\":false},\"title\":\"[Flights] Delay Type\"}]", + "panelsJSON": "[{\"version\":\"8.6.0\",\"type\":\"search\",\"gridData\":{\"x\":0,\"y\":47,\"w\":48,\"h\":15,\"i\":\"4\"},\"panelIndex\":\"4\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_4\"},{\"version\":\"8.6.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":16,\"w\":24,\"h\":9,\"i\":\"7\"},\"panelIndex\":\"7\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_7\"},{\"version\":\"8.6.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":36,\"w\":24,\"h\":11,\"i\":\"10\"},\"panelIndex\":\"10\",\"embeddableConfig\":{\"vis\":{\"colors\":{\"Count\":\"#1F78C1\"},\"legendOpen\":false},\"enhancements\":{}},\"panelRefName\":\"panel_10\"},{\"version\":\"8.6.0\",\"type\":\"visualization\",\"gridData\":{\"x\":36,\"y\":36,\"w\":12,\"h\":11,\"i\":\"21\"},\"panelIndex\":\"21\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_21\"},{\"version\":\"8.6.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":0,\"w\":24,\"h\":8,\"i\":\"6afc61f7-e2d5-45a3-9e7a-281160ad3eb9\"},\"panelIndex\":\"6afc61f7-e2d5-45a3-9e7a-281160ad3eb9\",\"embeddableConfig\":{\"savedVis\":{\"title\":\"[Flights] Markdown Instructions\",\"description\":\"\",\"type\":\"markdown\",\"params\":{\"fontSize\":10,\"openLinksInNewTab\":true,\"markdown\":\"## Sample Flight data\\nThis dashboard contains sample data for you to play with. You can view it, search it, and interact with the visualizations. For more information about Kibana, check our [docs](https://www.elastic.co/guide/en/kibana/current/index.html).\"},\"uiState\":{},\"data\":{\"aggs\":[],\"searchSource\":{}}},\"hidePanelTitles\":true,\"enhancements\":{}}},{\"version\":\"8.6.0\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":0,\"w\":8,\"h\":8,\"i\":\"392b4936-f753-47bc-a98d-a4e41a0a4cd4\"},\"panelIndex\":\"392b4936-f753-47bc-a98d-a4e41a0a4cd4\",\"embeddableConfig\":{\"enhancements\":{},\"attributes\":{\"title\":\"[Flights] Total Flights\",\"description\":\"\",\"visualizationType\":\"lnsLegacyMetric\",\"state\":{\"datasourceStates\":{\"formBased\":{\"layers\":{\"8fa993db-c147-4954-adf7-4ff264d42576\":{\"columns\":{\"81124c45-6ab6-42f4-8859-495d55eb8065\":{\"label\":\"Total flights\",\"dataType\":\"number\",\"operationType\":\"count\",\"isBucketed\":false,\"scale\":\"ratio\",\"sourceField\":\"___records___\",\"customLabel\":true}},\"columnOrder\":[\"81124c45-6ab6-42f4-8859-495d55eb8065\"],\"incompleteColumns\":{}}}}},\"visualization\":{\"layerId\":\"8fa993db-c147-4954-adf7-4ff264d42576\",\"accessor\":\"81124c45-6ab6-42f4-8859-495d55eb8065\",\"layerType\":\"data\",\"textAlign\":\"center\",\"titlePosition\":\"bottom\",\"size\":\"xl\"},\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filters\":[]},\"references\":[{\"id\":\"d3d7af60-4c81-11e8-b3d7-01146121b73d\",\"name\":\"indexpattern-datasource-current-indexpattern\",\"type\":\"index-pattern\"},{\"id\":\"d3d7af60-4c81-11e8-b3d7-01146121b73d\",\"name\":\"indexpattern-datasource-layer-8fa993db-c147-4954-adf7-4ff264d42576\",\"type\":\"index-pattern\"}]},\"hidePanelTitles\":true}},{\"version\":\"8.6.0\",\"type\":\"lens\",\"gridData\":{\"x\":32,\"y\":0,\"w\":8,\"h\":4,\"i\":\"9271deff-5a61-4665-83fc-f9fdc6bf0c0b\"},\"panelIndex\":\"9271deff-5a61-4665-83fc-f9fdc6bf0c0b\",\"embeddableConfig\":{\"attributes\":{\"title\":\"\",\"type\":\"lens\",\"visualizationType\":\"lnsLegacyMetric\",\"state\":{\"datasourceStates\":{\"formBased\":{\"layers\":{\"b4712d43-1e84-4f5b-878d-8e38ba748317\":{\"columns\":{\"7e8fe9b1-f45c-4f3d-9561-30febcd357ecX0\":{\"label\":\"Part of count(kql='FlightDelay : true') / count()\",\"dataType\":\"number\",\"operationType\":\"count\",\"isBucketed\":false,\"scale\":\"ratio\",\"sourceField\":\"___records___\",\"filter\":{\"query\":\"FlightDelay : true\",\"language\":\"kuery\"},\"customLabel\":true},\"7e8fe9b1-f45c-4f3d-9561-30febcd357ecX1\":{\"label\":\"Part of count(kql='FlightDelay : true') / count()\",\"dataType\":\"number\",\"operationType\":\"count\",\"isBucketed\":false,\"scale\":\"ratio\",\"sourceField\":\"___records___\",\"customLabel\":true},\"7e8fe9b1-f45c-4f3d-9561-30febcd357ecX2\":{\"label\":\"Part of count(kql='FlightDelay : true') / count()\",\"dataType\":\"number\",\"operationType\":\"math\",\"isBucketed\":false,\"scale\":\"ratio\",\"params\":{\"tinymathAst\":{\"type\":\"function\",\"name\":\"divide\",\"args\":[\"7e8fe9b1-f45c-4f3d-9561-30febcd357ecX0\",\"7e8fe9b1-f45c-4f3d-9561-30febcd357ecX1\"],\"location\":{\"min\":0,\"max\":41},\"text\":\"count(kql='FlightDelay : true') / count()\"}},\"references\":[\"7e8fe9b1-f45c-4f3d-9561-30febcd357ecX0\",\"7e8fe9b1-f45c-4f3d-9561-30febcd357ecX1\"],\"customLabel\":true},\"7e8fe9b1-f45c-4f3d-9561-30febcd357ec\":{\"label\":\"Delayed\",\"dataType\":\"number\",\"operationType\":\"formula\",\"isBucketed\":false,\"scale\":\"ratio\",\"params\":{\"formula\":\"count(kql='FlightDelay : true') / count()\",\"isFormulaBroken\":false,\"format\":{\"id\":\"percent\",\"params\":{\"decimals\":1}}},\"references\":[\"7e8fe9b1-f45c-4f3d-9561-30febcd357ecX2\"],\"customLabel\":true}},\"columnOrder\":[\"7e8fe9b1-f45c-4f3d-9561-30febcd357ec\",\"7e8fe9b1-f45c-4f3d-9561-30febcd357ecX0\",\"7e8fe9b1-f45c-4f3d-9561-30febcd357ecX1\",\"7e8fe9b1-f45c-4f3d-9561-30febcd357ecX2\"],\"incompleteColumns\":{}}}}},\"visualization\":{\"layerId\":\"b4712d43-1e84-4f5b-878d-8e38ba748317\",\"accessor\":\"7e8fe9b1-f45c-4f3d-9561-30febcd357ec\",\"layerType\":\"data\",\"textAlign\":\"center\",\"titlePosition\":\"bottom\",\"size\":\"xl\"},\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filters\":[]},\"references\":[{\"id\":\"d3d7af60-4c81-11e8-b3d7-01146121b73d\",\"name\":\"indexpattern-datasource-current-indexpattern\",\"type\":\"index-pattern\"},{\"id\":\"d3d7af60-4c81-11e8-b3d7-01146121b73d\",\"name\":\"indexpattern-datasource-layer-b4712d43-1e84-4f5b-878d-8e38ba748317\",\"type\":\"index-pattern\"}]},\"enhancements\":{}}},{\"version\":\"8.6.0\",\"type\":\"lens\",\"gridData\":{\"x\":40,\"y\":0,\"w\":8,\"h\":4,\"i\":\"aa591c29-1a31-4ee1-a71d-b829c06fd162\"},\"panelIndex\":\"aa591c29-1a31-4ee1-a71d-b829c06fd162\",\"embeddableConfig\":{\"attributes\":{\"title\":\"\",\"type\":\"lens\",\"visualizationType\":\"lnsLegacyMetric\",\"state\":{\"datasourceStates\":{\"formBased\":{\"layers\":{\"b4712d43-1e84-4f5b-878d-8e38ba748317\":{\"columns\":{\"c7851241-5526-499a-960b-357af8c2ce5bX0\":{\"label\":\"Part of Delayed\",\"dataType\":\"number\",\"operationType\":\"count\",\"isBucketed\":false,\"scale\":\"ratio\",\"sourceField\":\"___records___\",\"customLabel\":true},\"c7851241-5526-499a-960b-357af8c2ce5bX1\":{\"label\":\"Part of Delayed\",\"dataType\":\"number\",\"operationType\":\"count\",\"isBucketed\":false,\"scale\":\"ratio\",\"sourceField\":\"___records___\",\"timeShift\":\"1w\",\"customLabel\":true},\"c7851241-5526-499a-960b-357af8c2ce5bX2\":{\"label\":\"Part of Delayed\",\"dataType\":\"number\",\"operationType\":\"math\",\"isBucketed\":false,\"scale\":\"ratio\",\"params\":{\"tinymathAst\":{\"type\":\"function\",\"name\":\"subtract\",\"args\":[{\"type\":\"function\",\"name\":\"divide\",\"args\":[\"c7851241-5526-499a-960b-357af8c2ce5bX0\",\"c7851241-5526-499a-960b-357af8c2ce5bX1\"],\"location\":{\"min\":0,\"max\":28},\"text\":\"count() / count(shift='1w') \"},1],\"location\":{\"min\":0,\"max\":31},\"text\":\"count() / count(shift='1w') - 1\"}},\"references\":[\"c7851241-5526-499a-960b-357af8c2ce5bX0\",\"c7851241-5526-499a-960b-357af8c2ce5bX1\"],\"customLabel\":true},\"c7851241-5526-499a-960b-357af8c2ce5b\":{\"label\":\"Delayed vs 1 week earlier\",\"dataType\":\"number\",\"operationType\":\"formula\",\"isBucketed\":false,\"scale\":\"ratio\",\"params\":{\"formula\":\"count() / count(shift='1w') - 1\",\"isFormulaBroken\":false,\"format\":{\"id\":\"percent\",\"params\":{\"decimals\":1}}},\"references\":[\"c7851241-5526-499a-960b-357af8c2ce5bX2\"],\"customLabel\":true}},\"columnOrder\":[\"c7851241-5526-499a-960b-357af8c2ce5b\",\"c7851241-5526-499a-960b-357af8c2ce5bX2\",\"c7851241-5526-499a-960b-357af8c2ce5bX0\",\"c7851241-5526-499a-960b-357af8c2ce5bX1\"],\"incompleteColumns\":{}}}}},\"visualization\":{\"layerId\":\"b4712d43-1e84-4f5b-878d-8e38ba748317\",\"accessor\":\"c7851241-5526-499a-960b-357af8c2ce5b\",\"layerType\":\"data\",\"textAlign\":\"center\",\"titlePosition\":\"bottom\",\"size\":\"xl\"},\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filters\":[{\"meta\":{\"alias\":null,\"negate\":false,\"disabled\":false,\"type\":\"phrase\",\"key\":\"FlightDelay\",\"params\":{\"query\":true},\"index\":\"filter-index-pattern-0\"},\"query\":{\"match_phrase\":{\"FlightDelay\":true}},\"$state\":{\"store\":\"appState\"}}]},\"references\":[{\"id\":\"d3d7af60-4c81-11e8-b3d7-01146121b73d\",\"name\":\"indexpattern-datasource-current-indexpattern\",\"type\":\"index-pattern\"},{\"id\":\"d3d7af60-4c81-11e8-b3d7-01146121b73d\",\"name\":\"indexpattern-datasource-layer-b4712d43-1e84-4f5b-878d-8e38ba748317\",\"type\":\"index-pattern\"},{\"id\":\"d3d7af60-4c81-11e8-b3d7-01146121b73d\",\"name\":\"filter-index-pattern-0\",\"type\":\"index-pattern\"}]},\"enhancements\":{}}},{\"version\":\"8.6.0\",\"type\":\"lens\",\"gridData\":{\"x\":32,\"y\":4,\"w\":8,\"h\":4,\"i\":\"b766e3b8-4544-46ed-99e6-9ecc4847e2a2\"},\"panelIndex\":\"b766e3b8-4544-46ed-99e6-9ecc4847e2a2\",\"embeddableConfig\":{\"attributes\":{\"title\":\"\",\"type\":\"lens\",\"visualizationType\":\"lnsLegacyMetric\",\"state\":{\"datasourceStates\":{\"formBased\":{\"layers\":{\"b4712d43-1e84-4f5b-878d-8e38ba748317\":{\"columns\":{\"7e8fe9b1-f45c-4f3d-9561-30febcd357ecX0\":{\"label\":\"Part of Cancelled\",\"dataType\":\"number\",\"operationType\":\"count\",\"isBucketed\":false,\"scale\":\"ratio\",\"sourceField\":\"___records___\",\"filter\":{\"query\":\"Cancelled : true\",\"language\":\"kuery\"},\"customLabel\":true},\"7e8fe9b1-f45c-4f3d-9561-30febcd357ecX1\":{\"label\":\"Part of Cancelled\",\"dataType\":\"number\",\"operationType\":\"count\",\"isBucketed\":false,\"scale\":\"ratio\",\"sourceField\":\"___records___\",\"customLabel\":true},\"7e8fe9b1-f45c-4f3d-9561-30febcd357ecX2\":{\"label\":\"Part of Cancelled\",\"dataType\":\"number\",\"operationType\":\"math\",\"isBucketed\":false,\"scale\":\"ratio\",\"params\":{\"tinymathAst\":{\"type\":\"function\",\"name\":\"divide\",\"args\":[\"7e8fe9b1-f45c-4f3d-9561-30febcd357ecX0\",\"7e8fe9b1-f45c-4f3d-9561-30febcd357ecX1\"],\"location\":{\"min\":0,\"max\":39},\"text\":\"count(kql='Cancelled : true') / count()\"}},\"references\":[\"7e8fe9b1-f45c-4f3d-9561-30febcd357ecX0\",\"7e8fe9b1-f45c-4f3d-9561-30febcd357ecX1\"],\"customLabel\":true},\"7e8fe9b1-f45c-4f3d-9561-30febcd357ec\":{\"label\":\"Cancelled\",\"dataType\":\"number\",\"operationType\":\"formula\",\"isBucketed\":false,\"scale\":\"ratio\",\"params\":{\"formula\":\"count(kql='Cancelled : true') / count()\",\"isFormulaBroken\":false,\"format\":{\"id\":\"percent\",\"params\":{\"decimals\":1}}},\"references\":[\"7e8fe9b1-f45c-4f3d-9561-30febcd357ecX2\"],\"customLabel\":true}},\"columnOrder\":[\"7e8fe9b1-f45c-4f3d-9561-30febcd357ec\",\"7e8fe9b1-f45c-4f3d-9561-30febcd357ecX0\",\"7e8fe9b1-f45c-4f3d-9561-30febcd357ecX1\",\"7e8fe9b1-f45c-4f3d-9561-30febcd357ecX2\"],\"incompleteColumns\":{}}}}},\"visualization\":{\"layerId\":\"b4712d43-1e84-4f5b-878d-8e38ba748317\",\"accessor\":\"7e8fe9b1-f45c-4f3d-9561-30febcd357ec\",\"layerType\":\"data\",\"textAlign\":\"center\",\"titlePosition\":\"bottom\",\"size\":\"xl\"},\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filters\":[]},\"references\":[{\"id\":\"d3d7af60-4c81-11e8-b3d7-01146121b73d\",\"name\":\"indexpattern-datasource-current-indexpattern\",\"type\":\"index-pattern\"},{\"id\":\"d3d7af60-4c81-11e8-b3d7-01146121b73d\",\"name\":\"indexpattern-datasource-layer-b4712d43-1e84-4f5b-878d-8e38ba748317\",\"type\":\"index-pattern\"}]},\"enhancements\":{}}},{\"version\":\"8.6.0\",\"type\":\"lens\",\"gridData\":{\"x\":40,\"y\":4,\"w\":8,\"h\":4,\"i\":\"2e33ade5-96e5-40b4-b460-493e5d4fa834\"},\"panelIndex\":\"2e33ade5-96e5-40b4-b460-493e5d4fa834\",\"embeddableConfig\":{\"attributes\":{\"title\":\"\",\"type\":\"lens\",\"visualizationType\":\"lnsLegacyMetric\",\"state\":{\"datasourceStates\":{\"formBased\":{\"layers\":{\"b4712d43-1e84-4f5b-878d-8e38ba748317\":{\"columns\":{\"c7851241-5526-499a-960b-357af8c2ce5bX0\":{\"label\":\"Part of Delayed vs 1 week earlier\",\"dataType\":\"number\",\"operationType\":\"count\",\"isBucketed\":false,\"scale\":\"ratio\",\"sourceField\":\"___records___\",\"customLabel\":true},\"c7851241-5526-499a-960b-357af8c2ce5bX1\":{\"label\":\"Part of Delayed vs 1 week earlier\",\"dataType\":\"number\",\"operationType\":\"count\",\"isBucketed\":false,\"scale\":\"ratio\",\"sourceField\":\"___records___\",\"timeShift\":\"1w\",\"customLabel\":true},\"c7851241-5526-499a-960b-357af8c2ce5bX2\":{\"label\":\"Part of Delayed vs 1 week earlier\",\"dataType\":\"number\",\"operationType\":\"math\",\"isBucketed\":false,\"scale\":\"ratio\",\"params\":{\"tinymathAst\":{\"type\":\"function\",\"name\":\"subtract\",\"args\":[{\"type\":\"function\",\"name\":\"divide\",\"args\":[\"c7851241-5526-499a-960b-357af8c2ce5bX0\",\"c7851241-5526-499a-960b-357af8c2ce5bX1\"],\"location\":{\"min\":0,\"max\":28},\"text\":\"count() / count(shift='1w') \"},1],\"location\":{\"min\":0,\"max\":31},\"text\":\"count() / count(shift='1w') - 1\"}},\"references\":[\"c7851241-5526-499a-960b-357af8c2ce5bX0\",\"c7851241-5526-499a-960b-357af8c2ce5bX1\"],\"customLabel\":true},\"c7851241-5526-499a-960b-357af8c2ce5b\":{\"label\":\"Cancelled vs 1 week earlier\",\"dataType\":\"number\",\"operationType\":\"formula\",\"isBucketed\":false,\"scale\":\"ratio\",\"params\":{\"formula\":\"count() / count(shift='1w') - 1\",\"isFormulaBroken\":false,\"format\":{\"id\":\"percent\",\"params\":{\"decimals\":1}}},\"references\":[\"c7851241-5526-499a-960b-357af8c2ce5bX2\"],\"customLabel\":true}},\"columnOrder\":[\"c7851241-5526-499a-960b-357af8c2ce5b\",\"c7851241-5526-499a-960b-357af8c2ce5bX2\",\"c7851241-5526-499a-960b-357af8c2ce5bX0\",\"c7851241-5526-499a-960b-357af8c2ce5bX1\"],\"incompleteColumns\":{}}}}},\"visualization\":{\"layerId\":\"b4712d43-1e84-4f5b-878d-8e38ba748317\",\"accessor\":\"c7851241-5526-499a-960b-357af8c2ce5b\",\"layerType\":\"data\",\"textAlign\":\"center\",\"titlePosition\":\"bottom\",\"size\":\"xl\"},\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filters\":[{\"meta\":{\"alias\":null,\"negate\":false,\"disabled\":false,\"type\":\"phrase\",\"key\":\"Cancelled\",\"params\":{\"query\":true},\"index\":\"filter-index-pattern-0\"},\"query\":{\"match_phrase\":{\"Cancelled\":true}},\"$state\":{\"store\":\"appState\"}}]},\"references\":[{\"id\":\"d3d7af60-4c81-11e8-b3d7-01146121b73d\",\"name\":\"indexpattern-datasource-current-indexpattern\",\"type\":\"index-pattern\"},{\"id\":\"d3d7af60-4c81-11e8-b3d7-01146121b73d\",\"name\":\"indexpattern-datasource-layer-b4712d43-1e84-4f5b-878d-8e38ba748317\",\"type\":\"index-pattern\"},{\"id\":\"d3d7af60-4c81-11e8-b3d7-01146121b73d\",\"name\":\"filter-index-pattern-0\",\"type\":\"index-pattern\"}]},\"enhancements\":{}}},{\"version\":\"8.6.0\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":8,\"w\":24,\"h\":8,\"i\":\"086ac2e9-dd16-4b45-92b8-1e43ff7e3f65\"},\"panelIndex\":\"086ac2e9-dd16-4b45-92b8-1e43ff7e3f65\",\"embeddableConfig\":{\"attributes\":{\"title\":\"\",\"type\":\"lens\",\"visualizationType\":\"lnsXY\",\"state\":{\"datasourceStates\":{\"formBased\":{\"layers\":{\"03c34665-471c-49c7-acf1-5a11f517421c\":{\"columns\":{\"a5b94e30-4e77-4b0a-9187-1d8b13de1456\":{\"label\":\"timestamp\",\"dataType\":\"date\",\"operationType\":\"date_histogram\",\"sourceField\":\"timestamp\",\"isBucketed\":true,\"scale\":\"interval\",\"params\":{\"interval\":\"auto\",\"includeEmptyRows\":true}},\"3e267327-7317-4310-aee3-320e0f7c1e70\":{\"label\":\"Count of records\",\"dataType\":\"number\",\"operationType\":\"count\",\"isBucketed\":false,\"scale\":\"ratio\",\"sourceField\":\"___records___\"}},\"columnOrder\":[\"a5b94e30-4e77-4b0a-9187-1d8b13de1456\",\"3e267327-7317-4310-aee3-320e0f7c1e70\"],\"incompleteColumns\":{}}}}},\"visualization\":{\"legend\":{\"isVisible\":true,\"position\":\"right\",\"legendSize\":\"auto\"},\"valueLabels\":\"hide\",\"fittingFunction\":\"None\",\"yLeftExtent\":{\"mode\":\"full\"},\"yRightExtent\":{\"mode\":\"custom\",\"lowerBound\":0,\"upperBound\":1},\"axisTitlesVisibilitySettings\":{\"x\":false,\"yLeft\":false,\"yRight\":true},\"tickLabelsVisibilitySettings\":{\"x\":true,\"yLeft\":true,\"yRight\":true},\"gridlinesVisibilitySettings\":{\"x\":true,\"yLeft\":true,\"yRight\":true},\"preferredSeriesType\":\"bar_stacked\",\"layers\":[{\"layerId\":\"03c34665-471c-49c7-acf1-5a11f517421c\",\"accessors\":[\"3e267327-7317-4310-aee3-320e0f7c1e70\"],\"position\":\"top\",\"seriesType\":\"bar_stacked\",\"showGridlines\":false,\"xAccessor\":\"a5b94e30-4e77-4b0a-9187-1d8b13de1456\",\"layerType\":\"data\"}]},\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filters\":[]},\"references\":[{\"id\":\"d3d7af60-4c81-11e8-b3d7-01146121b73d\",\"name\":\"indexpattern-datasource-current-indexpattern\",\"type\":\"index-pattern\"},{\"id\":\"d3d7af60-4c81-11e8-b3d7-01146121b73d\",\"name\":\"indexpattern-datasource-layer-03c34665-471c-49c7-acf1-5a11f517421c\",\"type\":\"index-pattern\"}]},\"hidePanelTitles\":false,\"enhancements\":{}},\"title\":\"[Flights] Flight count\"},{\"version\":\"8.6.0\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":8,\"w\":24,\"h\":28,\"i\":\"fb86b32f-fb7a-45cf-9511-f366fef51bbd\"},\"panelIndex\":\"fb86b32f-fb7a-45cf-9511-f366fef51bbd\",\"embeddableConfig\":{\"attributes\":{\"title\":\"Cities by delay, cancellation\",\"type\":\"lens\",\"visualizationType\":\"lnsDatatable\",\"state\":{\"datasourceStates\":{\"formBased\":{\"layers\":{\"f26e8f7a-4118-4227-bea0-5c02d8b270f7\":{\"columns\":{\"3dd24cb4-45ef-4dd8-b22a-d7b802cb6da0\":{\"label\":\"Top values of OriginCityName\",\"dataType\":\"string\",\"operationType\":\"terms\",\"scale\":\"ordinal\",\"sourceField\":\"OriginCityName\",\"isBucketed\":true,\"params\":{\"size\":1000,\"orderBy\":{\"type\":\"alphabetical\",\"fallback\":true},\"orderDirection\":\"asc\",\"otherBucket\":true,\"missingBucket\":false}},\"52f6f2e9-6242-4c44-be63-b799150e7e60X0\":{\"label\":\"Part of Delay %\",\"dataType\":\"number\",\"operationType\":\"count\",\"isBucketed\":false,\"scale\":\"ratio\",\"sourceField\":\"___records___\",\"filter\":{\"query\":\"FlightDelay : true \",\"language\":\"kuery\"},\"customLabel\":true},\"52f6f2e9-6242-4c44-be63-b799150e7e60X1\":{\"label\":\"Part of Delay %\",\"dataType\":\"number\",\"operationType\":\"count\",\"isBucketed\":false,\"scale\":\"ratio\",\"sourceField\":\"___records___\",\"customLabel\":true},\"52f6f2e9-6242-4c44-be63-b799150e7e60X2\":{\"label\":\"Part of Delay %\",\"dataType\":\"number\",\"operationType\":\"math\",\"isBucketed\":false,\"scale\":\"ratio\",\"params\":{\"tinymathAst\":{\"type\":\"function\",\"name\":\"divide\",\"args\":[\"52f6f2e9-6242-4c44-be63-b799150e7e60X0\",\"52f6f2e9-6242-4c44-be63-b799150e7e60X1\"],\"location\":{\"min\":0,\"max\":42},\"text\":\"count(kql='FlightDelay : true ') / count()\"}},\"references\":[\"52f6f2e9-6242-4c44-be63-b799150e7e60X0\",\"52f6f2e9-6242-4c44-be63-b799150e7e60X1\"],\"customLabel\":true},\"52f6f2e9-6242-4c44-be63-b799150e7e60\":{\"label\":\"Delay %\",\"dataType\":\"number\",\"operationType\":\"formula\",\"isBucketed\":false,\"scale\":\"ratio\",\"params\":{\"formula\":\"count(kql='FlightDelay : true ') / count()\",\"isFormulaBroken\":false,\"format\":{\"id\":\"percent\",\"params\":{\"decimals\":0}}},\"references\":[\"52f6f2e9-6242-4c44-be63-b799150e7e60X2\"],\"customLabel\":true},\"7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6X0\":{\"label\":\"Part of Cancel %\",\"dataType\":\"number\",\"operationType\":\"count\",\"isBucketed\":false,\"scale\":\"ratio\",\"sourceField\":\"___records___\",\"filter\":{\"query\":\"Cancelled: true\",\"language\":\"kuery\"},\"customLabel\":true},\"7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6X1\":{\"label\":\"Part of Cancel %\",\"dataType\":\"number\",\"operationType\":\"count\",\"isBucketed\":false,\"scale\":\"ratio\",\"sourceField\":\"___records___\",\"customLabel\":true},\"7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6X2\":{\"label\":\"Part of Cancel %\",\"dataType\":\"number\",\"operationType\":\"math\",\"isBucketed\":false,\"scale\":\"ratio\",\"params\":{\"tinymathAst\":{\"type\":\"function\",\"name\":\"divide\",\"args\":[\"7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6X0\",\"7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6X1\"],\"location\":{\"min\":0,\"max\":38},\"text\":\"count(kql='Cancelled: true') / count()\"}},\"references\":[\"7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6X0\",\"7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6X1\"],\"customLabel\":true},\"7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6\":{\"label\":\"Cancel %\",\"dataType\":\"number\",\"operationType\":\"formula\",\"isBucketed\":false,\"scale\":\"ratio\",\"params\":{\"formula\":\"count(kql='Cancelled: true') / count()\",\"isFormulaBroken\":false,\"format\":{\"id\":\"percent\",\"params\":{\"decimals\":0}}},\"references\":[\"7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6X2\"],\"customLabel\":true}},\"columnOrder\":[\"3dd24cb4-45ef-4dd8-b22a-d7b802cb6da0\",\"52f6f2e9-6242-4c44-be63-b799150e7e60\",\"52f6f2e9-6242-4c44-be63-b799150e7e60X0\",\"52f6f2e9-6242-4c44-be63-b799150e7e60X1\",\"52f6f2e9-6242-4c44-be63-b799150e7e60X2\",\"7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6X0\",\"7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6X1\",\"7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6X2\",\"7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6\"],\"incompleteColumns\":{}}}}},\"visualization\":{\"columns\":[{\"isTransposed\":false,\"columnId\":\"3dd24cb4-45ef-4dd8-b22a-d7b802cb6da0\",\"width\":262.75},{\"columnId\":\"52f6f2e9-6242-4c44-be63-b799150e7e60\",\"isTransposed\":false,\"width\":302.5,\"colorMode\":\"cell\",\"palette\":{\"name\":\"custom\",\"type\":\"palette\",\"params\":{\"steps\":5,\"stops\":[{\"color\":\"#f7e0b8\",\"stop\":0.6},{\"color\":\"#e7664c\",\"stop\":1}],\"name\":\"custom\",\"colorStops\":[{\"color\":\"#f7e0b8\",\"stop\":0.2},{\"color\":\"#e7664c\",\"stop\":0.6}],\"rangeType\":\"number\",\"rangeMin\":0.2,\"rangeMax\":0.6}},\"alignment\":\"center\"},{\"columnId\":\"7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6\",\"isTransposed\":false,\"alignment\":\"center\",\"colorMode\":\"cell\",\"palette\":{\"name\":\"custom\",\"type\":\"palette\",\"params\":{\"steps\":5,\"stops\":[{\"color\":\"#f7e0b8\",\"stop\":0.6},{\"color\":\"#e7664c\",\"stop\":0.6666666666666666}],\"rangeType\":\"number\",\"name\":\"custom\",\"colorStops\":[{\"color\":\"#f7e0b8\",\"stop\":0.2},{\"color\":\"#e7664c\",\"stop\":0.6}],\"rangeMin\":0.2,\"rangeMax\":0.6}}}],\"layerId\":\"f26e8f7a-4118-4227-bea0-5c02d8b270f7\",\"sorting\":{\"columnId\":\"52f6f2e9-6242-4c44-be63-b799150e7e60\",\"direction\":\"desc\"},\"layerType\":\"data\",\"rowHeight\":\"single\",\"rowHeightLines\":1},\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filters\":[]},\"references\":[{\"id\":\"d3d7af60-4c81-11e8-b3d7-01146121b73d\",\"name\":\"indexpattern-datasource-current-indexpattern\",\"type\":\"index-pattern\"},{\"id\":\"d3d7af60-4c81-11e8-b3d7-01146121b73d\",\"name\":\"indexpattern-datasource-layer-f26e8f7a-4118-4227-bea0-5c02d8b270f7\",\"type\":\"index-pattern\"}]},\"enhancements\":{},\"hidePanelTitles\":false},\"title\":\"[Flights] Most delayed cities\"},{\"version\":\"8.6.0\",\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":25,\"w\":24,\"h\":11,\"i\":\"0cc42484-16f7-42ec-b38c-9bf8be69cde7\"},\"panelIndex\":\"0cc42484-16f7-42ec-b38c-9bf8be69cde7\",\"embeddableConfig\":{\"attributes\":{\"title\":\"\",\"type\":\"lens\",\"visualizationType\":\"lnsXY\",\"state\":{\"datasourceStates\":{\"formBased\":{\"layers\":{\"e80cc05e-c52a-4e5f-ac71-4b37274867f5\":{\"columns\":{\"caf7421e-93a3-439e-ab0a-fbdead93c21c\":{\"label\":\"Top values of FlightDelayType\",\"dataType\":\"string\",\"operationType\":\"terms\",\"scale\":\"ordinal\",\"sourceField\":\"FlightDelayType\",\"isBucketed\":true,\"params\":{\"size\":10,\"orderBy\":{\"type\":\"column\",\"columnId\":\"0233d302-ec81-4fbe-96cb-7fac84cf035c\"},\"orderDirection\":\"desc\",\"otherBucket\":true,\"missingBucket\":false}},\"13ec79e3-9d73-4536-9056-3d92802bb30a\":{\"label\":\"timestamp\",\"dataType\":\"date\",\"operationType\":\"date_histogram\",\"sourceField\":\"timestamp\",\"isBucketed\":true,\"scale\":\"interval\",\"params\":{\"interval\":\"auto\",\"includeEmptyRows\":true}},\"0233d302-ec81-4fbe-96cb-7fac84cf035c\":{\"label\":\"Count of records\",\"dataType\":\"number\",\"operationType\":\"count\",\"isBucketed\":false,\"scale\":\"ratio\",\"sourceField\":\"___records___\"}},\"columnOrder\":[\"caf7421e-93a3-439e-ab0a-fbdead93c21c\",\"13ec79e3-9d73-4536-9056-3d92802bb30a\",\"0233d302-ec81-4fbe-96cb-7fac84cf035c\"],\"incompleteColumns\":{}}}}},\"visualization\":{\"legend\":{\"isVisible\":true,\"position\":\"bottom\",\"legendSize\":\"auto\"},\"valueLabels\":\"hide\",\"fittingFunction\":\"None\",\"yLeftExtent\":{\"mode\":\"full\"},\"yRightExtent\":{\"mode\":\"full\"},\"axisTitlesVisibilitySettings\":{\"x\":true,\"yLeft\":false,\"yRight\":true},\"tickLabelsVisibilitySettings\":{\"x\":true,\"yLeft\":true,\"yRight\":true},\"gridlinesVisibilitySettings\":{\"x\":true,\"yLeft\":true,\"yRight\":true},\"preferredSeriesType\":\"bar_percentage_stacked\",\"layers\":[{\"layerId\":\"e80cc05e-c52a-4e5f-ac71-4b37274867f5\",\"accessors\":[\"0233d302-ec81-4fbe-96cb-7fac84cf035c\"],\"position\":\"top\",\"seriesType\":\"bar_percentage_stacked\",\"showGridlines\":false,\"palette\":{\"type\":\"palette\",\"name\":\"cool\"},\"xAccessor\":\"13ec79e3-9d73-4536-9056-3d92802bb30a\",\"splitAccessor\":\"caf7421e-93a3-439e-ab0a-fbdead93c21c\",\"layerType\":\"data\"}]},\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filters\":[]},\"references\":[{\"id\":\"d3d7af60-4c81-11e8-b3d7-01146121b73d\",\"name\":\"indexpattern-datasource-current-indexpattern\",\"type\":\"index-pattern\"},{\"id\":\"d3d7af60-4c81-11e8-b3d7-01146121b73d\",\"name\":\"indexpattern-datasource-layer-e80cc05e-c52a-4e5f-ac71-4b37274867f5\",\"type\":\"index-pattern\"}]},\"hidePanelTitles\":false,\"enhancements\":{}},\"title\":\"[Flights] Delay Type\"},{\"version\":\"8.6.0\",\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":36,\"w\":12,\"h\":11,\"i\":\"5d53db36-2d5a-4adc-af7b-cec4c1a294e0\"},\"panelIndex\":\"5d53db36-2d5a-4adc-af7b-cec4c1a294e0\",\"embeddableConfig\":{\"attributes\":{\"title\":\"\",\"type\":\"lens\",\"visualizationType\":\"lnsPie\",\"state\":{\"datasourceStates\":{\"formBased\":{\"layers\":{\"0c8e136b-a822-4fb3-836d-e06cbea4eea4\":{\"columns\":{\"d1cee8bf-34cf-4141-99d7-ff043ee77b56\":{\"label\":\"Top values of FlightDelayType\",\"dataType\":\"string\",\"operationType\":\"terms\",\"scale\":\"ordinal\",\"sourceField\":\"FlightDelayType\",\"isBucketed\":true,\"params\":{\"size\":10,\"orderBy\":{\"type\":\"column\",\"columnId\":\"aa152ace-ee2d-447b-b86d-459bef4d7880\"},\"orderDirection\":\"desc\",\"otherBucket\":true,\"missingBucket\":false}},\"aa152ace-ee2d-447b-b86d-459bef4d7880\":{\"label\":\"Count of records\",\"dataType\":\"number\",\"operationType\":\"count\",\"isBucketed\":false,\"scale\":\"ratio\",\"sourceField\":\"___records___\"}},\"columnOrder\":[\"d1cee8bf-34cf-4141-99d7-ff043ee77b56\",\"aa152ace-ee2d-447b-b86d-459bef4d7880\"],\"incompleteColumns\":{}}}}},\"visualization\":{\"shape\":\"pie\",\"palette\":{\"type\":\"palette\",\"name\":\"cool\"},\"layers\":[{\"layerId\":\"0c8e136b-a822-4fb3-836d-e06cbea4eea4\",\"metrics\":[\"aa152ace-ee2d-447b-b86d-459bef4d7880\"],\"numberDisplay\":\"percent\",\"categoryDisplay\":\"default\",\"legendDisplay\":\"default\",\"nestedLegend\":false,\"layerType\":\"data\",\"legendSize\":\"auto\",\"primaryGroups\":[\"d1cee8bf-34cf-4141-99d7-ff043ee77b56\"]}]},\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filters\":[{\"meta\":{\"type\":\"phrase\",\"key\":\"FlightDelayType\",\"params\":{\"query\":\"No Delay\"},\"disabled\":false,\"negate\":true,\"alias\":null,\"index\":\"filter-index-pattern-0\"},\"query\":{\"match_phrase\":{\"FlightDelayType\":\"No Delay\"}},\"$state\":{\"store\":\"appState\"}}]},\"references\":[{\"id\":\"d3d7af60-4c81-11e8-b3d7-01146121b73d\",\"name\":\"indexpattern-datasource-current-indexpattern\",\"type\":\"index-pattern\"},{\"id\":\"d3d7af60-4c81-11e8-b3d7-01146121b73d\",\"name\":\"indexpattern-datasource-layer-0c8e136b-a822-4fb3-836d-e06cbea4eea4\",\"type\":\"index-pattern\"},{\"id\":\"d3d7af60-4c81-11e8-b3d7-01146121b73d\",\"name\":\"filter-index-pattern-0\",\"type\":\"index-pattern\"}]},\"enhancements\":{},\"hidePanelTitles\":false},\"title\":\"[Flights] Delay Type\"}]", "refreshInterval": { "pause": true, "value": 0 From 28bcc7f24719b243e69a4e0c464120b34e7f998d Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Wed, 9 Nov 2022 15:30:29 -0600 Subject: [PATCH 62/70] update i18n string --- .../common/expression_functions/i18n.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/i18n.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/i18n.ts index 974e3d660e5d4..b312de7bf1583 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/i18n.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/i18n.ts @@ -18,7 +18,7 @@ export const strings = { defaultMessage: 'Metric dimensions config', }), getMetricToLabelHelp: () => - i18n.translate('expressionXY.layer.columnToLabel.help', { + i18n.translate('expressionPartitionVis.metricToLabel.help', { defaultMessage: 'JSON key-value pairs of column ID to label', }), getBucketsArgHelp: () => From ffd3a52082c92990f9d94b933ca3dbdc4e9d0680 Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Wed, 9 Nov 2022 15:40:57 -0600 Subject: [PATCH 63/70] update tests and types --- .../__snapshots__/mosaic_vis_function.test.ts.snap | 1 + .../__snapshots__/pie_vis_function.test.ts.snap | 2 ++ .../__snapshots__/treemap_vis_function.test.ts.snap | 1 + .../__snapshots__/waffle_vis_function.test.ts.snap | 1 + .../common/expression_functions/mosaic_vis_function.ts | 1 + .../common/expression_functions/pie_vis_function.test.ts | 1 + .../expression_functions/treemap_vis_function.test.ts | 1 + .../common/expression_functions/waffle_vis_function.test.ts | 1 + .../common/types/expression_renderers.ts | 2 +- .../public/__stories__/shared/config.ts | 1 + .../public/utils/filter_helpers.test.ts | 6 +++++- 11 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/mosaic_vis_function.test.ts.snap b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/mosaic_vis_function.test.ts.snap index 9fc8e917a46c3..c00de511b8afb 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/mosaic_vis_function.test.ts.snap +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/mosaic_vis_function.test.ts.snap @@ -124,6 +124,7 @@ Object { }, "type": "vis_dimension", }, + "metricsToLabels": Object {}, "nestedLegend": true, "palette": Object { "name": "kibana_palette", diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/pie_vis_function.test.ts.snap b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/pie_vis_function.test.ts.snap index 14d117e5d06b7..65cd755d51a07 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/pie_vis_function.test.ts.snap +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/pie_vis_function.test.ts.snap @@ -127,6 +127,7 @@ Object { "type": "vis_dimension", }, ], + "metricsToLabels": Object {}, "nestedLegend": true, "palette": Object { "name": "kibana_palette", @@ -266,6 +267,7 @@ Object { "type": "vis_dimension", }, ], + "metricsToLabels": Object {}, "nestedLegend": true, "palette": Object { "name": "kibana_palette", diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/treemap_vis_function.test.ts.snap b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/treemap_vis_function.test.ts.snap index 74f89a037df85..5388a47242fb4 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/treemap_vis_function.test.ts.snap +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/treemap_vis_function.test.ts.snap @@ -126,6 +126,7 @@ Object { "type": "vis_dimension", }, ], + "metricsToLabels": Object {}, "nestedLegend": true, "palette": Object { "name": "kibana_palette", diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/waffle_vis_function.test.ts.snap b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/waffle_vis_function.test.ts.snap index 336979eff662d..180c3221240ce 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/waffle_vis_function.test.ts.snap +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/waffle_vis_function.test.ts.snap @@ -100,6 +100,7 @@ Object { "type": "vis_dimension", }, ], + "metricsToLabels": Object {}, "palette": Object { "name": "kibana_palette", "type": "system_palette", diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/mosaic_vis_function.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/mosaic_vis_function.ts index 49ebb92c03684..75f2aa3c17dc1 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/mosaic_vis_function.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/mosaic_vis_function.ts @@ -130,6 +130,7 @@ export const mosaicVisFunction = (): MosaicVisExpressionFunctionDefinition => ({ const visConfig: PartitionVisParams = { ...args, + metricsToLabels: {}, ariaLabel: args.ariaLabel ?? (handlers.variables?.embeddableTitle as string) ?? diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.test.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.test.ts index 474a3b8b35e45..0c222758d912a 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.test.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.test.ts @@ -30,6 +30,7 @@ describe('interpreter/functions#pieVis', () => { const visConfig: PieVisConfig = { addTooltip: true, + metricsToLabels: JSON.stringify({}), legendDisplay: LegendDisplay.SHOW, legendPosition: 'right', legendSize: LegendSize.SMALL, diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/treemap_vis_function.test.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/treemap_vis_function.test.ts index 09b813ec805dd..e5bc4115c1461 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/treemap_vis_function.test.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/treemap_vis_function.test.ts @@ -34,6 +34,7 @@ describe('interpreter/functions#treemapVis', () => { const visConfig: TreemapVisConfig = { addTooltip: true, + metricsToLabels: JSON.stringify({}), legendDisplay: LegendDisplay.SHOW, legendPosition: 'right', nestedLegend: true, diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/waffle_vis_function.test.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/waffle_vis_function.test.ts index f36b492495a0c..4c81f64428a74 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/waffle_vis_function.test.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/waffle_vis_function.test.ts @@ -35,6 +35,7 @@ describe('interpreter/functions#waffleVis', () => { const visConfig: WaffleVisConfig = { addTooltip: true, showValuesInLegend: true, + metricsToLabels: JSON.stringify({}), legendDisplay: LegendDisplay.SHOW, legendPosition: 'right', truncateLegend: true, diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/types/expression_renderers.ts b/src/plugins/chart_expressions/expression_partition_vis/common/types/expression_renderers.ts index 1611d3565ded1..d01407cf0af72 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/types/expression_renderers.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/types/expression_renderers.ts @@ -95,7 +95,7 @@ export interface TreemapVisConfig extends VisCommonConfig { nestedLegend: boolean; } -export interface MosaicVisConfig extends Omit { +export interface MosaicVisConfig extends Omit { metric: ExpressionValueVisDimension | string; nestedLegend: boolean; } diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/__stories__/shared/config.ts b/src/plugins/chart_expressions/expression_partition_vis/public/__stories__/shared/config.ts index e0424a3249210..aa4023006d486 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/__stories__/shared/config.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/public/__stories__/shared/config.ts @@ -17,6 +17,7 @@ import { export const config: RenderValue['visConfig'] = { addTooltip: true, legendDisplay: LegendDisplay.HIDE, + metricsToLabels: { percent_uptime: 'percent_uptime' }, truncateLegend: true, respectSourceOrder: true, legendPosition: Position.Bottom, diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/utils/filter_helpers.test.ts b/src/plugins/chart_expressions/expression_partition_vis/public/utils/filter_helpers.test.ts index d8a766244dd8f..07646450a43a0 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/utils/filter_helpers.test.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/public/utils/filter_helpers.test.ts @@ -164,7 +164,11 @@ describe('getFilterClickData', () => { const { table: consolidatedTable } = consolidateMetricColumns( originalTable, ['0', '1'], - ['2', '3'] + ['2', '3'], + { + 2: 'metric1', + 3: 'metric2', + } ); it('generates the correct filters', () => { From 7060340da91e49b9617c6c29ea6e53133e4ee663 Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Wed, 9 Nov 2022 17:48:30 -0600 Subject: [PATCH 64/70] fix typings --- .../common/expression_functions/pie_vis_function.ts | 2 +- .../common/expression_functions/treemap_vis_function.ts | 2 +- .../common/expression_functions/waffle_vis_function.ts | 2 +- .../common/types/expression_renderers.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts index bfecd3bde8f6c..4bf2ead1b9c52 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts @@ -156,7 +156,7 @@ export const pieVisFunction = (): PieVisExpressionFunctionDefinition => ({ const visConfig: PartitionVisParams = { ...args, - metricsToLabels: JSON.parse(args.metricsToLabels), + metricsToLabels: args.metricsToLabels ? JSON.parse(args.metricsToLabels) : {}, ariaLabel: args.ariaLabel ?? (handlers.variables?.embeddableTitle as string) ?? diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/treemap_vis_function.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/treemap_vis_function.ts index 3e1dc9f5581cb..d5f91b1f0e1d3 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/treemap_vis_function.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/treemap_vis_function.ts @@ -136,7 +136,7 @@ export const treemapVisFunction = (): TreemapVisExpressionFunctionDefinition => const visConfig: PartitionVisParams = { ...args, - metricsToLabels: JSON.parse(args.metricsToLabels), + metricsToLabels: args.metricsToLabels ? JSON.parse(args.metricsToLabels) : {}, ariaLabel: args.ariaLabel ?? (handlers.variables?.embeddableTitle as string) ?? diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/waffle_vis_function.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/waffle_vis_function.ts index b522ffa017fa4..1568454b86eb2 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/waffle_vis_function.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/waffle_vis_function.ts @@ -131,7 +131,7 @@ export const waffleVisFunction = (): WaffleVisExpressionFunctionDefinition => ({ const buckets = args.bucket ? [args.bucket] : []; const visConfig: PartitionVisParams = { ...args, - metricsToLabels: JSON.parse(args.metricsToLabels), + metricsToLabels: args.metricsToLabels ? JSON.parse(args.metricsToLabels) : {}, ariaLabel: args.ariaLabel ?? (handlers.variables?.embeddableTitle as string) ?? diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/types/expression_renderers.ts b/src/plugins/chart_expressions/expression_partition_vis/common/types/expression_renderers.ts index d01407cf0af72..9584a810d7ca4 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/types/expression_renderers.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/types/expression_renderers.ts @@ -59,7 +59,7 @@ interface VisCommonParams { interface VisCommonConfig extends VisCommonParams { metrics: Array; - metricsToLabels: string; + metricsToLabels?: string; buckets?: Array; splitColumn?: Array; splitRow?: Array; From 51041e71d0385adddf7a01ead05e9557f7e0daae Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Thu, 10 Nov 2022 08:52:13 -0600 Subject: [PATCH 65/70] don't pass metricsToLabels to mosiac expression --- .../lens/public/visualizations/partition/to_expression.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/lens/public/visualizations/partition/to_expression.ts b/x-pack/plugins/lens/public/visualizations/partition/to_expression.ts index 2e02613a54bfd..2aa6651865d0c 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/to_expression.ts +++ b/x-pack/plugins/lens/public/visualizations/partition/to_expression.ts @@ -210,9 +210,10 @@ const generateTreemapVisAst: GenerateExpressionAstFunction = (...rest) => { const generateMosaicVisAst: GenerateExpressionAstFunction = (...rest) => { const { metrics, ...args } = generateCommonArguments(...rest); + return buildExpression([ buildExpressionFunction('mosaicVis', { - ...args, + ...{ ...args, metricsToLabels: undefined }, metric: metrics, // flip order of bucket dimensions so the rows are fetched before the columns to keep them stable buckets: rest[2] From ab4f82c518c210581848c55938d39914bc7df692 Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Thu, 10 Nov 2022 09:05:15 -0600 Subject: [PATCH 66/70] add section to docs --- docs/user/dashboard/lens.asciidoc | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/docs/user/dashboard/lens.asciidoc b/docs/user/dashboard/lens.asciidoc index 6c695cd3a74a9..de23c3a9962e0 100644 --- a/docs/user/dashboard/lens.asciidoc +++ b/docs/user/dashboard/lens.asciidoc @@ -172,6 +172,20 @@ Compare your real-time data to the results that are offset by a time increment. For a time shift example, refer to <>. +[float] +[[multi-metric-partition-chart]] +==== Build a partition chart from multiple metrics + +By default, partition charts (e.g. pie) are built from one or more "slice-by" dimensions to define the partitions and a single metric dimension to define their size. However, you can also build a partition chart from multiple metric dimensions. + +. Open the layer context menu at the top right of the layer panel. + +. Click *Layer settings*. + +. Click the switch labeled *Multiple metrics*. + +Note: this option is not available for mosaic charts. + [float] [[add-annotations]] ==== Add annotations From 097f55614365c1e37d0bdbf3d66c03bbff8ed0a3 Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Thu, 10 Nov 2022 11:08:54 -0600 Subject: [PATCH 67/70] show collapse setting --- x-pack/plugins/lens/public/visualizations/partition/toolbar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/lens/public/visualizations/partition/toolbar.tsx b/x-pack/plugins/lens/public/visualizations/partition/toolbar.tsx index dea4a30ca7e73..cab4781a6a319 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/toolbar.tsx +++ b/x-pack/plugins/lens/public/visualizations/partition/toolbar.tsx @@ -346,7 +346,7 @@ export function DimensionDataExtraEditor( return ( <> - {![...currentLayer.primaryGroups, ...(currentLayer.secondaryGroups ?? [])].includes( + {[...currentLayer.primaryGroups, ...(currentLayer.secondaryGroups ?? [])].includes( props.accessor ) && ( Date: Thu, 10 Nov 2022 13:32:48 -0600 Subject: [PATCH 68/70] resolve types --- .../partition/visualization.tsx | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx b/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx index 7d139aed225d3..0ba29502cf280 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx @@ -24,6 +24,7 @@ import type { VisualizationDimensionGroupConfig, Suggestion, VisualizeEditorContext, + VisualizationInfo, } from '../../types'; import { getSortedGroups, toExpression, toPreviewExpression } from './to_expression'; import { @@ -40,6 +41,10 @@ import { DimensionDataExtraEditor, DimensionEditor, PieToolbar } from './toolbar import { LayerSettings } from './layer_settings'; import { checkTableForContainsSmallValues } from './render_helpers'; +const metricLabel = i18n.translate('xpack.lens.pie.groupMetricLabelSingular', { + defaultMessage: 'Metric', +}); + function newLayerState(layerId: string): PieLayerState { return { layerId, @@ -299,9 +304,7 @@ export const getPieVisualization = ({ ? i18n.translate('xpack.lens.pie.groupMetricLabel', { defaultMessage: 'Metrics', }) - : i18n.translate('xpack.lens.pie.groupMetricLabelSingular', { - defaultMessage: 'Metric', - }); + : metricLabel; return { groupId: 'metric', @@ -581,15 +584,14 @@ export const getPieVisualization = ({ getVisualizationInfo(state: PieVisualizationState) { const layer = state.layers[0]; - const dimensions = []; - if (layer.metric) { + const dimensions: VisualizationInfo['layers'][number]['dimensions'] = []; + + layer.metrics.forEach((metric) => { dimensions.push({ - id: layer.metric, - name: i18n.translate('xpack.lens.pie.groupsizeLabel', { - defaultMessage: 'Size by', - }), + id: metric, + name: metricLabel, }); - } + }); if (state.shape === 'mosaic' && layer.secondaryGroups && layer.secondaryGroups.length) { layer.secondaryGroups.forEach((accessor) => { From ff7d7008503fd9893c31586f98b3bf1fc5467df2 Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Mon, 14 Nov 2022 09:27:36 -0600 Subject: [PATCH 69/70] change fleet server version to fix OSQuery tests --- x-pack/test/osquery_cypress/artifact_manager.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/test/osquery_cypress/artifact_manager.ts b/x-pack/test/osquery_cypress/artifact_manager.ts index 7ee2680e21f83..74097deb442cc 100644 --- a/x-pack/test/osquery_cypress/artifact_manager.ts +++ b/x-pack/test/osquery_cypress/artifact_manager.ts @@ -6,5 +6,5 @@ */ export async function getLatestVersion(): Promise { - return '8.6.0-SNAPSHOT'; + return '8.5.1-SNAPSHOT'; } From 3a326a48b3eb9e6a5c8c6117755b13c648a1f900 Mon Sep 17 00:00:00 2001 From: Andrew Tate Date: Mon, 14 Nov 2022 16:45:46 -0600 Subject: [PATCH 70/70] move layer settings action to layer panel --- .../datasources/form_based/form_based.tsx | 17 ------- .../layer_actions/layer_actions.tsx | 9 ++++ .../layer_actions/open_layer_settings.tsx | 27 +++++++++++ .../editor_frame/config_panel/layer_panel.tsx | 45 +++++++++---------- x-pack/plugins/lens/public/types.ts | 7 --- 5 files changed, 58 insertions(+), 47 deletions(-) create mode 100644 x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/open_layer_settings.tsx diff --git a/x-pack/plugins/lens/public/datasources/form_based/form_based.tsx b/x-pack/plugins/lens/public/datasources/form_based/form_based.tsx index ff3475df1f227..a464e12c80d1c 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/form_based.tsx +++ b/x-pack/plugins/lens/public/datasources/form_based/form_based.tsx @@ -618,23 +618,6 @@ export function getFormBasedDatasource({ getDropProps, onDrop, - getSupportedActionsForLayer(layerId, state, _, openLayerSettings) { - if (!openLayerSettings) { - return []; - } - return [ - { - id: 'openLayerSettings', - displayName: i18n.translate('xpack.lens.indexPattern.layerSettingsAction', { - defaultMessage: 'Layer settings', - }), - execute: openLayerSettings, - icon: 'gear', - isCompatible: Boolean(state.layers[layerId]), - 'data-test-subj': 'lnsLayerSettings', - }, - ]; - }, getCustomWorkspaceRenderer: ( state: FormBasedPrivateState, diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/layer_actions.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/layer_actions.tsx index 0ba94486ebba9..397325be3f4d7 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/layer_actions.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/layer_actions.tsx @@ -22,6 +22,7 @@ import type { LayerType } from '../../../..'; import type { LayerAction, Visualization } from '../../../../types'; import { getCloneLayerAction } from './clone_layer_action'; import { getRemoveLayerAction } from './remove_layer_action'; +import { getOpenLayerSettingsAction } from './open_layer_settings'; export interface LayerActionsProps { layerIndex: number; @@ -36,6 +37,8 @@ export const getSharedActions = ({ activeVisualization, isOnlyLayer, isTextBasedLanguage, + hasLayerSettings, + openLayerSettings, onCloneLayer, onRemoveLayer, }: { @@ -48,8 +51,14 @@ export const getSharedActions = ({ visualizationState: unknown; layerType?: LayerType; isTextBasedLanguage?: boolean; + hasLayerSettings: boolean; + openLayerSettings: () => void; core: Pick; }) => [ + getOpenLayerSettingsAction({ + hasLayerSettings, + openLayerSettings, + }), getCloneLayerAction({ execute: onCloneLayer, layerIndex, diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/open_layer_settings.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/open_layer_settings.tsx new file mode 100644 index 0000000000000..13570df68c581 --- /dev/null +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions/open_layer_settings.tsx @@ -0,0 +1,27 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import type { LayerAction } from '../../../../types'; + +export const getOpenLayerSettingsAction = (props: { + openLayerSettings: () => void; + hasLayerSettings: boolean; +}): LayerAction => { + const displayName = i18n.translate('xpack.lens.layerActions.layerSettingsAction', { + defaultMessage: 'Layer settings', + }); + + return { + id: 'openLayerSettings', + displayName, + execute: props.openLayerSettings, + icon: 'gear', + isCompatible: props.hasLayerSettings, + 'data-test-subj': 'lnsLayerSettings', + }; +}; diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx index aba39c2eb9e03..0e759db7fc9fc 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx @@ -51,6 +51,9 @@ import { onDropForVisualization, shouldRemoveSource } from './buttons/drop_targe import { getSharedActions } from './layer_actions/layer_actions'; import { FlyoutContainer } from './flyout_container'; +// hide the random sampling settings from the UI +const DISPLAY_RANDOM_SAMPLING_SETTINGS = false; + const initialActiveDimensionState = { isNew: false, }; @@ -330,12 +333,6 @@ export function LayerPanel( ); }, })) || []), - ...(layerDatasource?.getSupportedActionsForLayer?.( - layerId, - layerDatasourceState, - (newState) => updateDatasource(datasourceId, newState), - () => setPanelSettingsOpen(true) - ) || []), ...getSharedActions({ layerId, activeVisualization, @@ -345,6 +342,11 @@ export function LayerPanel( layerType: activeVisualization.getLayerType(layerId, visualizationState), isOnlyLayer, isTextBasedLanguage, + hasLayerSettings: Boolean( + activeVisualization.renderLayerSettings || + (layerDatasource?.renderLayerSettings && DISPLAY_RANDOM_SAMPLING_SETTINGS) + ), + openLayerSettings: () => setPanelSettingsOpen(true), onCloneLayer, onRemoveLayer: () => onRemoveLayer(layerId), }), @@ -352,16 +354,13 @@ export function LayerPanel( [ activeVisualization, core, - datasourceId, isOnlyLayer, isTextBasedLanguage, layerDatasource, - layerDatasourceState, layerId, layerIndex, onCloneLayer, onRemoveLayer, - updateDatasource, updateVisualization, visualizationState, ] @@ -688,25 +687,25 @@ export function LayerPanel( >
- {layerDatasource?.renderLayerSettings && ( - - )} - {activeVisualization?.renderLayerSettings && ( + {layerDatasource?.renderLayerSettings && DISPLAY_RANDOM_SAMPLING_SETTINGS && ( <> - + )} + {activeVisualization?.renderLayerSettings && ( + + )}
diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index 3871b2e04a728..8540f3a87b49c 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -489,13 +489,6 @@ export interface Datasource { */ getUsedDataViews: (state: T) => string[]; - getSupportedActionsForLayer?: ( - layerId: string, - state: T, - setState: StateSetter, - openLayerSettings?: () => void - ) => LayerAction[]; - getDatasourceInfo: ( state: T, references?: SavedObjectReference[],