From 9388bbae03f7607452ed3879b0725aea54655f2b Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Tue, 8 Mar 2022 16:08:39 +0200 Subject: [PATCH 001/133] added xy plugin. --- .github/CODEOWNERS | 1 + .i18nrc.json | 1 + docs/developer/plugin-list.asciidoc | 4 +++ .../chart_expressions/expression_xy/README.md | 9 +++++++ .../expression_xy/common/index.ts | 10 +++++++ .../expression_xy/jest.config.js | 19 +++++++++++++ .../expression_xy/kibana.json | 14 ++++++++++ .../expression_xy/public/index.ts | 17 ++++++++++++ .../expression_xy/public/plugin.ts | 24 +++++++++++++++++ .../expression_xy/public/types.ts | 13 +++++++++ .../expression_xy/server/index.ts | 16 +++++++++++ .../expression_xy/server/plugin.ts | 27 +++++++++++++++++++ .../expression_xy/server/types.ts | 13 +++++++++ .../expression_xy/tsconfig.json | 24 +++++++++++++++++ 14 files changed, 192 insertions(+) create mode 100755 src/plugins/chart_expressions/expression_xy/README.md create mode 100755 src/plugins/chart_expressions/expression_xy/common/index.ts create mode 100644 src/plugins/chart_expressions/expression_xy/jest.config.js create mode 100755 src/plugins/chart_expressions/expression_xy/kibana.json create mode 100755 src/plugins/chart_expressions/expression_xy/public/index.ts create mode 100755 src/plugins/chart_expressions/expression_xy/public/plugin.ts create mode 100755 src/plugins/chart_expressions/expression_xy/public/types.ts create mode 100755 src/plugins/chart_expressions/expression_xy/server/index.ts create mode 100755 src/plugins/chart_expressions/expression_xy/server/plugin.ts create mode 100755 src/plugins/chart_expressions/expression_xy/server/types.ts create mode 100644 src/plugins/chart_expressions/expression_xy/tsconfig.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 0a0aa994fb70b..0b645d281dd33 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -41,6 +41,7 @@ /src/plugins/chart_expressions/expression_heatmap/ @elastic/kibana-vis-editors /src/plugins/chart_expressions/expression_gauge/ @elastic/kibana-vis-editors /src/plugins/chart_expressions/expression_partition_vis/ @elastic/kibana-vis-editors +/src/plugins/chart_expressions/expression_xy/ @elastic/kibana-vis-editors /src/plugins/url_forwarding/ @elastic/kibana-vis-editors /packages/kbn-tinymath/ @elastic/kibana-vis-editors /x-pack/test/functional/apps/lens @elastic/kibana-vis-editors diff --git a/.i18nrc.json b/.i18nrc.json index eeb2578ef3472..ba78b5ac333fb 100644 --- a/.i18nrc.json +++ b/.i18nrc.json @@ -26,6 +26,7 @@ "expressionMetric": "src/plugins/expression_metric", "expressionMetricVis": "src/plugins/chart_expressions/expression_metric", "expressionPartitionVis": "src/plugins/chart_expressions/expression_partition_vis", + "expressionXY": "src/plugins/chart_expressions/expression_xy", "expressionRepeatImage": "src/plugins/expression_repeat_image", "expressionRevealImage": "src/plugins/expression_reveal_image", "expressions": "src/plugins/expressions", diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index c26a748839daf..f8cdedab388f1 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -160,6 +160,10 @@ for use in their own application. |Expression Tagcloud plugin adds a tagcloud renderer and function to the expression plugin. The renderer will display the Wordcloud chart. +|{kib-repo}blob/{branch}/src/plugins/chart_expressions/expression_xy/README.md[expressionXY] +|A Kibana plugin + + |{kib-repo}blob/{branch}/src/plugins/field_formats/README.md[fieldFormats] |Index pattern fields formatters diff --git a/src/plugins/chart_expressions/expression_xy/README.md b/src/plugins/chart_expressions/expression_xy/README.md new file mode 100755 index 0000000000000..3b99441811825 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/README.md @@ -0,0 +1,9 @@ +# expressionXY + +A Kibana plugin + +--- + +## Development + +See the [kibana contributing guide](https://github.com/elastic/kibana/blob/main/CONTRIBUTING.md) for instructions setting up your development environment. diff --git a/src/plugins/chart_expressions/expression_xy/common/index.ts b/src/plugins/chart_expressions/expression_xy/common/index.ts new file mode 100755 index 0000000000000..a1dedff32b6cb --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/index.ts @@ -0,0 +1,10 @@ +/* + * 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 const PLUGIN_ID = 'expressionXy'; +export const PLUGIN_NAME = 'expressionXy'; diff --git a/src/plugins/chart_expressions/expression_xy/jest.config.js b/src/plugins/chart_expressions/expression_xy/jest.config.js new file mode 100644 index 0000000000000..6d742af9a6f3d --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/jest.config.js @@ -0,0 +1,19 @@ +/* + * 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. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../../../', + roots: ['/src/plugins/chart_expressions/expression_xy'], + coverageDirectory: + '/target/kibana-coverage/jest/src/plugins/chart_expressions/expression_xy', + coverageReporters: ['text', 'html'], + collectCoverageFrom: [ + '/src/plugins/chart_expressions/expression_xy/{common,public,server}/**/*.{ts,tsx}', + ], +}; diff --git a/src/plugins/chart_expressions/expression_xy/kibana.json b/src/plugins/chart_expressions/expression_xy/kibana.json new file mode 100755 index 0000000000000..bb498969395be --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/kibana.json @@ -0,0 +1,14 @@ +{ + "id": "expressionXY", + "version": "1.0.0", + "kibanaVersion": "kibana", + "owner": { + "name": "Vis Editors", + "githubTeam": "kibana-vis-editors" + }, + "description": "Expression XY plugin adds a `xy` renderer and function to the expression plugin. The renderer will display the `xy` chart.", + "server": true, + "ui": true, + "requiredPlugins": [], + "optionalPlugins": [] +} diff --git a/src/plugins/chart_expressions/expression_xy/public/index.ts b/src/plugins/chart_expressions/expression_xy/public/index.ts new file mode 100755 index 0000000000000..d9447400aa266 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/public/index.ts @@ -0,0 +1,17 @@ +/* + * 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 { ExpressionXyPlugin } from './plugin'; + +// This exports static code and TypeScript types, +// as well as, Kibana Platform `plugin()` initializer. +export function plugin() { + return new ExpressionXyPlugin(); +} + +export type { ExpressionXyPluginSetup, ExpressionXyPluginStart } from './types'; diff --git a/src/plugins/chart_expressions/expression_xy/public/plugin.ts b/src/plugins/chart_expressions/expression_xy/public/plugin.ts new file mode 100755 index 0000000000000..bc830c3235d8e --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/public/plugin.ts @@ -0,0 +1,24 @@ +/* + * 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 { CoreSetup, CoreStart, Plugin } from '../../../../core/public'; +import { ExpressionXyPluginSetup, ExpressionXyPluginStart } from './types'; + +export class ExpressionXyPlugin + implements Plugin +{ + public setup(core: CoreSetup): ExpressionXyPluginSetup { + return {}; + } + + public start(core: CoreStart): ExpressionXyPluginStart { + return {}; + } + + public stop() {} +} diff --git a/src/plugins/chart_expressions/expression_xy/public/types.ts b/src/plugins/chart_expressions/expression_xy/public/types.ts new file mode 100755 index 0000000000000..361e68090dda2 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/public/types.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. + */ + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface ExpressionXyPluginSetup {} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface ExpressionXyPluginStart {} diff --git a/src/plugins/chart_expressions/expression_xy/server/index.ts b/src/plugins/chart_expressions/expression_xy/server/index.ts new file mode 100755 index 0000000000000..ecfb289bb608b --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/server/index.ts @@ -0,0 +1,16 @@ +/* + * 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 { PluginInitializerContext } from '../../../../core/server'; +import { ExpressionXyPlugin } from './plugin'; + +export function plugin(initializerContext: PluginInitializerContext) { + return new ExpressionXyPlugin(initializerContext); +} + +export type { ExpressionXyPluginSetup, ExpressionXyPluginStart } from './types'; diff --git a/src/plugins/chart_expressions/expression_xy/server/plugin.ts b/src/plugins/chart_expressions/expression_xy/server/plugin.ts new file mode 100755 index 0000000000000..52cf062b74864 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/server/plugin.ts @@ -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 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 { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from '../../../../core/server'; + +import { ExpressionXyPluginSetup, ExpressionXyPluginStart } from './types'; + +export class ExpressionXyPlugin + implements Plugin +{ + constructor(initializerContext: PluginInitializerContext) {} + + public setup(core: CoreSetup) { + return {}; + } + + public start(core: CoreStart) { + return {}; + } + + public stop() {} +} diff --git a/src/plugins/chart_expressions/expression_xy/server/types.ts b/src/plugins/chart_expressions/expression_xy/server/types.ts new file mode 100755 index 0000000000000..361e68090dda2 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/server/types.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. + */ + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface ExpressionXyPluginSetup {} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface ExpressionXyPluginStart {} diff --git a/src/plugins/chart_expressions/expression_xy/tsconfig.json b/src/plugins/chart_expressions/expression_xy/tsconfig.json new file mode 100644 index 0000000000000..97a0c8a9fc515 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/tsconfig.json @@ -0,0 +1,24 @@ +{ + "extends": "../../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./target/types", + "emitDeclarationOnly": true, + "declaration": true, + "declarationMap": true, + "isolatedModules": true + }, + "include": [ + "common/**/*", + "public/**/*", + "server/**/*", + ], + "references": [ + { "path": "../../../core/tsconfig.json" }, + { "path": "../../expressions/tsconfig.json" }, + { "path": "../../presentation_util/tsconfig.json" }, + { "path": "../../data/tsconfig.json" }, + { "path": "../../field_formats/tsconfig.json" }, + { "path": "../../charts/tsconfig.json" }, + { "path": "../../visualizations/tsconfig.json" } + ] +} From ab727283c3e2496b431d94e75365d53e6b484b62 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Wed, 9 Mar 2022 14:26:34 +0200 Subject: [PATCH 002/133] Added expressionXY limits. --- packages/kbn-optimizer/limits.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index afe7fcd9ddc86..82be0f701c816 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -123,4 +123,5 @@ pageLoadAssetSize: ux: 20784 sessionView: 77750 cloudSecurityPosture: 19109 - visTypeGauge: 24113 \ No newline at end of file + visTypeGauge: 24113 + expressionXY: 16241 From b4ee9c148b73eb73967a81464f4a39a96929c0ee Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Thu, 10 Mar 2022 14:23:50 +0200 Subject: [PATCH 003/133] Added xy expression functions to the expression_xy plugin. --- .../expression_xy/common/constants.ts | 153 +++++++++++++ .../axis_extent_config.ts | 52 +++++ .../axis_titles_visibility_config.ts | 51 +++++ .../expression_functions/data_layer_config.ts | 97 ++++++++ .../expression_functions/grid_lines_config.ts | 51 +++++ .../common/expression_functions/index.ts | 18 ++ .../labels_orientation_config.ts | 54 +++++ .../expression_functions/legend_config.ts | 94 ++++++++ .../reference_line_layer_config.ts | 51 +++++ .../tick_labels_config.ts | 51 +++++ .../common/expression_functions/xy_chart.ts | 178 +++++++++++++++ .../expression_functions/y_axis_config.ts | 72 ++++++ .../expression_xy/common/index.ts | 13 ++ .../common/types/expression_functions.ts | 215 ++++++++++++++++++ .../common/types/expression_renderers.ts | 21 ++ .../expression_xy/common/types/index.ts | 10 + .../expression_xy/kibana.json | 2 +- .../expression_xy/public/plugin.ts | 31 ++- .../expression_xy/public/types.ts | 15 +- .../expression_xy/server/plugin.ts | 32 ++- .../expression_xy/server/types.ts | 15 +- .../expression_xy/tsconfig.json | 6 +- 22 files changed, 1255 insertions(+), 27 deletions(-) create mode 100644 src/plugins/chart_expressions/expression_xy/common/constants.ts create mode 100644 src/plugins/chart_expressions/expression_xy/common/expression_functions/axis_extent_config.ts create mode 100644 src/plugins/chart_expressions/expression_xy/common/expression_functions/axis_titles_visibility_config.ts create mode 100644 src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer_config.ts create mode 100644 src/plugins/chart_expressions/expression_xy/common/expression_functions/grid_lines_config.ts create mode 100644 src/plugins/chart_expressions/expression_xy/common/expression_functions/index.ts create mode 100644 src/plugins/chart_expressions/expression_xy/common/expression_functions/labels_orientation_config.ts create mode 100644 src/plugins/chart_expressions/expression_xy/common/expression_functions/legend_config.ts create mode 100644 src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line_layer_config.ts create mode 100644 src/plugins/chart_expressions/expression_xy/common/expression_functions/tick_labels_config.ts create mode 100644 src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_chart.ts create mode 100644 src/plugins/chart_expressions/expression_xy/common/expression_functions/y_axis_config.ts create mode 100644 src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts create mode 100644 src/plugins/chart_expressions/expression_xy/common/types/expression_renderers.ts create mode 100644 src/plugins/chart_expressions/expression_xy/common/types/index.ts diff --git a/src/plugins/chart_expressions/expression_xy/common/constants.ts b/src/plugins/chart_expressions/expression_xy/common/constants.ts new file mode 100644 index 0000000000000..bc27bdaeb72c8 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/constants.ts @@ -0,0 +1,153 @@ +/* + * 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 { i18n } from '@kbn/i18n'; + +export const XY_CHART = 'lens_xy_chart'; +export const Y_CONFIG = 'lens_xy_yConfig'; +export const MULTITABLE = 'lens_multitable'; +export const DATA_LAYER = 'lens_xy_data_layer'; +export const LEGEND_CONFIG = 'lens_xy_legendConfig'; +export const XY_CHART_RENDERER = 'lens_xy_chart_renderer'; +export const GRID_LINES_CONFIG = 'lens_xy_gridlinesConfig'; +export const TICK_LABELS_CONFIG = 'lens_xy_tickLabelsConfig'; +export const AXIS_EXTENT_CONFIG = 'lens_xy_axisExtentConfig'; +export const REFERENCE_LINE_LAYER = 'lens_xy_referenceLine_layer'; +export const LABELS_ORIENTATION_CONFIG = 'lens_xy_labelsOrientationConfig'; +export const AXIS_TITLES_VISIBILITY_CONFIG = 'lens_xy_axisTitlesVisibilityConfig'; + +export const LayerTypes = { + DATA: 'data', + REFERENCELINE: 'referenceLine', +} as const; + +export const FittingFunctions = { + NONE: 'None', + ZERO: 'Zero', + LINEAR: 'Linear', + CARRY: 'Carry', + LOOKAHEAD: 'Lookahead', +} as const; + +export const YAxisModes = { + AUTO: 'auto', + LEFT: 'left', + RIGHT: 'right', + BOTTOM: 'bottom', +} as const; + +export const AxisExtentModes = { + FULL: 'full', + CUSTOM: 'custom', + DATA_BOUNDS: 'dataBounds', +} as const; + +export const LineStyles = { + SOLID: 'solid', + DASHED: 'dashed', + DOTTED: 'dotted', +} as const; + +export const FillStyles = { + NONE: 'none', + ABOVE: 'above', + BELOW: 'below', +} as const; + +export const IconPositions = { + AUTO: 'auto', + LEFT: 'left', + RIGHT: 'right', + ABOVE: 'above', + BELOW: 'below', +} as const; + +export const SeriesTypes = { + BAR: 'bar', + LINE: 'line', + AREA: 'area', + BAR_STACKED: 'bar_stacked', + AREA_STACKED: 'area_stacked', + BAR_HORIZONTAL: 'bar_horizontal', + BAR_PERCENTAGE_STACKED: 'bar_percentage_stacked', + BAR_HORIZONTAL_STACKED: 'bar_horizontal_stacked', + AREA_PERCENTAGE_STACKED: 'area_percentage_stacked', + BAR_HORIZONTAL_PERCENTAGE_STACKED: 'bar_horizontal_percentage_stacked', +} as const; + +export const YScaleTypes = { + TIME: 'time', + LINEAR: 'linear', + LOG: 'log', + SQRT: 'sqrt', +} as const; + +export const XScaleTypes = { + TIME: 'time', + LINEAR: 'linear', + ORDINAL: 'ordinal', +} as const; + +export const XYCurveTypes = { + LINEAR: 'LINEAR', + CURVE_MONOTONE_X: 'CURVE_MONOTONE_X', +} as const; + +export const ValueLabelModes = { + HIDE: 'hide', + INSIDE: 'inside', + OUTSIDE: 'outside', +} as const; + +export const fittingFunctionDefinitions = [ + { + id: FittingFunctions.NONE, + title: i18n.translate('xpack.lens.fittingFunctionsTitle.none', { + defaultMessage: 'Hide', + }), + description: i18n.translate('xpack.lens.fittingFunctionsDescription.none', { + defaultMessage: 'Do not fill gaps', + }), + }, + { + id: FittingFunctions.ZERO, + title: i18n.translate('xpack.lens.fittingFunctionsTitle.zero', { + defaultMessage: 'Zero', + }), + description: i18n.translate('xpack.lens.fittingFunctionsDescription.zero', { + defaultMessage: 'Fill gaps with zeros', + }), + }, + { + id: FittingFunctions.LINEAR, + title: i18n.translate('xpack.lens.fittingFunctionsTitle.linear', { + defaultMessage: 'Linear', + }), + description: i18n.translate('xpack.lens.fittingFunctionsDescription.linear', { + defaultMessage: 'Fill gaps with a line', + }), + }, + { + id: FittingFunctions.CARRY, + title: i18n.translate('xpack.lens.fittingFunctionsTitle.carry', { + defaultMessage: 'Last', + }), + description: i18n.translate('xpack.lens.fittingFunctionsDescription.carry', { + defaultMessage: 'Fill gaps with the last value', + }), + }, + { + id: FittingFunctions.LOOKAHEAD, + title: i18n.translate('xpack.lens.fittingFunctionsTitle.lookahead', { + defaultMessage: 'Next', + }), + description: i18n.translate('xpack.lens.fittingFunctionsDescription.lookahead', { + defaultMessage: 'Fill gaps with the next value', + }), + }, +]; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/axis_extent_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/axis_extent_config.ts new file mode 100644 index 0000000000000..deba7f9f541e6 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/axis_extent_config.ts @@ -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 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 { i18n } from '@kbn/i18n'; +import type { ExpressionFunctionDefinition } from '../../../../../../src/plugins/expressions/common'; +import { AxisExtentConfig, AxisExtentConfigResult } from '../types'; +import { AxisExtentModes, AXIS_EXTENT_CONFIG } from '../constants'; + +export const axisExtentConfigFunction: ExpressionFunctionDefinition< + typeof AXIS_EXTENT_CONFIG, + null, + AxisExtentConfig, + AxisExtentConfigResult +> = { + name: AXIS_EXTENT_CONFIG, + aliases: [], + type: AXIS_EXTENT_CONFIG, + help: `Configure the xy chart's axis extents`, + inputTypes: ['null'], + args: { + mode: { + types: ['string'], + options: [...Object.values(AxisExtentModes)], + help: i18n.translate('xpack.lens.xyChart.extentMode.help', { + defaultMessage: 'The extent mode', + }), + }, + lowerBound: { + types: ['number'], + help: i18n.translate('xpack.lens.xyChart.extentMode.help', { + defaultMessage: 'The extent mode', + }), + }, + upperBound: { + types: ['number'], + help: i18n.translate('xpack.lens.xyChart.extentMode.help', { + defaultMessage: 'The extent mode', + }), + }, + }, + fn(input, args) { + return { + type: AXIS_EXTENT_CONFIG, + ...args, + }; + }, +}; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/axis_titles_visibility_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/axis_titles_visibility_config.ts new file mode 100644 index 0000000000000..9cdf3128afa3d --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/axis_titles_visibility_config.ts @@ -0,0 +1,51 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import type { ExpressionFunctionDefinition } from '../../../../../../src/plugins/expressions/common'; +import { AXIS_TITLES_VISIBILITY_CONFIG } from '../constants'; +import { AxesSettingsConfig, AxisTitlesVisibilityConfigResult } from '../types'; + +export const axisTitlesVisibilityConfigFunction: ExpressionFunctionDefinition< + typeof AXIS_TITLES_VISIBILITY_CONFIG, + null, + AxesSettingsConfig, + AxisTitlesVisibilityConfigResult +> = { + name: AXIS_TITLES_VISIBILITY_CONFIG, + aliases: [], + type: AXIS_TITLES_VISIBILITY_CONFIG, + help: `Configure the xy chart's axis titles appearance`, + inputTypes: ['null'], + args: { + x: { + types: ['boolean'], + help: i18n.translate('xpack.lens.xyChart.xAxisTitle.help', { + defaultMessage: 'Specifies whether or not the title of the x-axis are visible.', + }), + }, + yLeft: { + types: ['boolean'], + help: i18n.translate('xpack.lens.xyChart.yLeftAxisTitle.help', { + defaultMessage: 'Specifies whether or not the title of the left y-axis are visible.', + }), + }, + yRight: { + types: ['boolean'], + help: i18n.translate('xpack.lens.xyChart.yRightAxisTitle.help', { + defaultMessage: 'Specifies whether or not the title of the right y-axis are visible.', + }), + }, + }, + fn(inputn, args) { + return { + type: AXIS_TITLES_VISIBILITY_CONFIG, + ...args, + }; + }, +}; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer_config.ts new file mode 100644 index 0000000000000..fd5bfb471c0f6 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer_config.ts @@ -0,0 +1,97 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { ExpressionFunctionDefinition } from '../../../../../../src/plugins/expressions/common'; +import { DataLayerArgs, DataLayerConfigResult } from '../types'; +import { + DATA_LAYER, + LayerTypes, + SeriesTypes, + XScaleTypes, + YScaleTypes, + Y_CONFIG, +} from '../constants'; + +export const dataLayerConfigFunction: ExpressionFunctionDefinition< + typeof DATA_LAYER, + null, + DataLayerArgs, + DataLayerConfigResult +> = { + name: DATA_LAYER, + aliases: [], + type: DATA_LAYER, + help: `Configure a layer in the xy chart`, + inputTypes: ['null'], + args: { + hide: { + types: ['boolean'], + default: false, + help: 'Show / hide axis', + }, + layerId: { + types: ['string'], + help: '', + }, + xAccessor: { + types: ['string'], + help: '', + }, + seriesType: { + types: ['string'], + options: [...Object.values(SeriesTypes)], + help: 'The type of chart to display.', + }, + xScaleType: { + options: [...Object.values(XScaleTypes)], + help: 'The scale type of the x axis', + default: XScaleTypes.ORDINAL, + }, + isHistogram: { + types: ['boolean'], + default: false, + help: 'Whether to layout the chart as a histogram', + }, + yScaleType: { + options: [...Object.values(YScaleTypes)], + help: 'The scale type of the y axes', + default: YScaleTypes.LINEAR, + }, + splitAccessor: { + types: ['string'], + help: 'The column to split by', + multi: false, + }, + accessors: { + types: ['string'], + help: 'The columns to display on the y axis.', + multi: true, + }, + yConfig: { + types: [Y_CONFIG], + help: 'Additional configuration for y axes', + multi: true, + }, + columnToLabel: { + types: ['string'], + help: 'JSON key-value pairs of column ID to label', + }, + palette: { + default: `{theme "palette" default={system_palette name="default"} }`, + help: '', + types: ['palette'], + }, + }, + fn(input, args) { + return { + type: DATA_LAYER, + ...args, + layerType: LayerTypes.DATA, + }; + }, +}; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/grid_lines_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/grid_lines_config.ts new file mode 100644 index 0000000000000..2a3a749489a7f --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/grid_lines_config.ts @@ -0,0 +1,51 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import type { ExpressionFunctionDefinition } from '../../../../../../src/plugins/expressions/common'; +import { GRID_LINES_CONFIG } from '../constants'; +import { AxesSettingsConfig, GridlinesConfigResult } from '../types'; + +export const gridlinesConfigFunction: ExpressionFunctionDefinition< + typeof GRID_LINES_CONFIG, + null, + AxesSettingsConfig, + GridlinesConfigResult +> = { + name: GRID_LINES_CONFIG, + aliases: [], + type: GRID_LINES_CONFIG, + help: `Configure the xy chart's gridlines appearance`, + inputTypes: ['null'], + args: { + x: { + types: ['boolean'], + help: i18n.translate('xpack.lens.xyChart.xAxisGridlines.help', { + defaultMessage: 'Specifies whether or not the gridlines of the x-axis are visible.', + }), + }, + yLeft: { + types: ['boolean'], + help: i18n.translate('xpack.lens.xyChart.yLeftAxisgridlines.help', { + defaultMessage: 'Specifies whether or not the gridlines of the left y-axis are visible.', + }), + }, + yRight: { + types: ['boolean'], + help: i18n.translate('xpack.lens.xyChart.yRightAxisgridlines.help', { + defaultMessage: 'Specifies whether or not the gridlines of the right y-axis are visible.', + }), + }, + }, + fn(input, args) { + return { + type: GRID_LINES_CONFIG, + ...args, + }; + }, +}; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/index.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/index.ts new file mode 100644 index 0000000000000..736a49487c06d --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/index.ts @@ -0,0 +1,18 @@ +/* + * 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 './xy_chart'; +export * from './legend_config'; +export * from './y_axis_config'; +export * from './data_layer_config'; +export * from './grid_lines_config'; +export * from './axis_extent_config'; +export * from './tick_labels_config'; +export * from './labels_orientation_config'; +export * from './reference_line_layer_config'; +export * from './axis_titles_visibility_config'; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/labels_orientation_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/labels_orientation_config.ts new file mode 100644 index 0000000000000..383c7a564e7c9 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/labels_orientation_config.ts @@ -0,0 +1,54 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import type { ExpressionFunctionDefinition } from '../../../../../../src/plugins/expressions/common'; +import { LABELS_ORIENTATION_CONFIG } from '../constants'; +import { LabelsOrientationConfig, LabelsOrientationConfigResult } from '../types'; + +export const labelsOrientationConfigFunction: ExpressionFunctionDefinition< + typeof LABELS_ORIENTATION_CONFIG, + null, + LabelsOrientationConfig, + LabelsOrientationConfigResult +> = { + name: LABELS_ORIENTATION_CONFIG, + aliases: [], + type: LABELS_ORIENTATION_CONFIG, + help: `Configure the xy chart's tick labels orientation`, + inputTypes: ['null'], + args: { + x: { + types: ['number'], + options: [0, -90, -45], + help: i18n.translate('xpack.lens.xyChart.xAxisLabelsOrientation.help', { + defaultMessage: 'Specifies the labels orientation of the x-axis.', + }), + }, + yLeft: { + types: ['number'], + options: [0, -90, -45], + help: i18n.translate('xpack.lens.xyChart.yLeftAxisLabelsOrientation.help', { + defaultMessage: 'Specifies the labels orientation of the left y-axis.', + }), + }, + yRight: { + types: ['number'], + options: [0, -90, -45], + help: i18n.translate('xpack.lens.xyChart.yRightAxisLabelsOrientation.help', { + defaultMessage: 'Specifies the labels orientation of the right y-axis.', + }), + }, + }, + fn(input, args) { + return { + type: LABELS_ORIENTATION_CONFIG, + ...args, + }; + }, +}; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/legend_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/legend_config.ts new file mode 100644 index 0000000000000..2c849b5d82487 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/legend_config.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 { HorizontalAlignment, Position, VerticalAlignment } from '@elastic/charts'; +import { i18n } from '@kbn/i18n'; +import type { ExpressionFunctionDefinition } from '../../../../../../src/plugins/expressions/common'; +import { LEGEND_CONFIG } from '../constants'; +import { LegendConfig, LegendConfigResult } from '../types'; + +export const legendConfigFunction: ExpressionFunctionDefinition< + typeof LEGEND_CONFIG, + null, + LegendConfig, + LegendConfigResult +> = { + name: LEGEND_CONFIG, + aliases: [], + type: LEGEND_CONFIG, + help: `Configure the xy chart's legend`, + inputTypes: ['null'], + args: { + isVisible: { + types: ['boolean'], + help: i18n.translate('xpack.lens.xyChart.isVisible.help', { + defaultMessage: 'Specifies whether or not the legend is visible.', + }), + }, + position: { + types: ['string'], + options: [Position.Top, Position.Right, Position.Bottom, Position.Left], + help: i18n.translate('xpack.lens.xyChart.position.help', { + defaultMessage: 'Specifies the legend position.', + }), + }, + showSingleSeries: { + types: ['boolean'], + help: i18n.translate('xpack.lens.xyChart.showSingleSeries.help', { + defaultMessage: 'Specifies whether a legend with just a single entry should be shown', + }), + }, + isInside: { + types: ['boolean'], + help: i18n.translate('xpack.lens.xyChart.isInside.help', { + defaultMessage: 'Specifies whether a legend is inside the chart', + }), + }, + horizontalAlignment: { + types: ['string'], + options: [HorizontalAlignment.Right, HorizontalAlignment.Left], + help: i18n.translate('xpack.lens.xyChart.horizontalAlignment.help', { + defaultMessage: + 'Specifies the horizontal alignment of the legend when it is displayed inside chart.', + }), + }, + verticalAlignment: { + types: ['string'], + options: [VerticalAlignment.Top, VerticalAlignment.Bottom], + help: i18n.translate('xpack.lens.xyChart.verticalAlignment.help', { + defaultMessage: + 'Specifies the vertical alignment of the legend when it is displayed inside chart.', + }), + }, + floatingColumns: { + types: ['number'], + help: i18n.translate('xpack.lens.xyChart.floatingColumns.help', { + defaultMessage: 'Specifies the number of columns when legend is displayed inside chart.', + }), + }, + maxLines: { + types: ['number'], + help: i18n.translate('xpack.lens.xyChart.maxLines.help', { + defaultMessage: 'Specifies the number of lines per legend item.', + }), + }, + shouldTruncate: { + types: ['boolean'], + default: true, + help: i18n.translate('xpack.lens.xyChart.shouldTruncate.help', { + defaultMessage: 'Specifies whether the legend items will be truncated or not', + }), + }, + }, + fn(input, args) { + return { + type: LEGEND_CONFIG, + ...args, + }; + }, +}; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line_layer_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line_layer_config.ts new file mode 100644 index 0000000000000..6f06e7e0f0839 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line_layer_config.ts @@ -0,0 +1,51 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { ExpressionFunctionDefinition } from '../../../../expressions/common'; +import { LayerTypes, REFERENCE_LINE_LAYER, Y_CONFIG } from '../constants'; +import { ReferenceLineLayerArgs, ReferenceLineLayerConfigResult } from '../types'; + +export const referenceLineLayerConfigFunction: ExpressionFunctionDefinition< + typeof REFERENCE_LINE_LAYER, + null, + ReferenceLineLayerArgs, + ReferenceLineLayerConfigResult +> = { + name: REFERENCE_LINE_LAYER, + aliases: [], + type: REFERENCE_LINE_LAYER, + help: `Configure a layer in the xy chart`, + inputTypes: ['null'], + args: { + layerId: { + types: ['string'], + help: '', + }, + accessors: { + types: ['string'], + help: 'The columns to display on the y axis.', + multi: true, + }, + yConfig: { + types: [Y_CONFIG], + help: 'Additional configuration for y axes', + multi: true, + }, + columnToLabel: { + types: ['string'], + help: 'JSON key-value pairs of column ID to label', + }, + }, + fn(input, args) { + return { + type: REFERENCE_LINE_LAYER, + ...args, + layerType: LayerTypes.REFERENCELINE, + }; + }, +}; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/tick_labels_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/tick_labels_config.ts new file mode 100644 index 0000000000000..3b39c1992d273 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/tick_labels_config.ts @@ -0,0 +1,51 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import type { ExpressionFunctionDefinition } from '../../../../../../src/plugins/expressions/common'; +import { TICK_LABELS_CONFIG } from '../constants'; +import { AxesSettingsConfig, TickLabelsConfigResult } from '../types'; + +export const tickLabelsConfigFunction: ExpressionFunctionDefinition< + typeof TICK_LABELS_CONFIG, + null, + AxesSettingsConfig, + TickLabelsConfigResult +> = { + name: TICK_LABELS_CONFIG, + aliases: [], + type: TICK_LABELS_CONFIG, + help: `Configure the xy chart's tick labels appearance`, + inputTypes: ['null'], + args: { + x: { + types: ['boolean'], + help: i18n.translate('xpack.lens.xyChart.xAxisTickLabels.help', { + defaultMessage: 'Specifies whether or not the tick labels of the x-axis are visible.', + }), + }, + yLeft: { + types: ['boolean'], + help: i18n.translate('xpack.lens.xyChart.yLeftAxisTickLabels.help', { + defaultMessage: 'Specifies whether or not the tick labels of the left y-axis are visible.', + }), + }, + yRight: { + types: ['boolean'], + help: i18n.translate('xpack.lens.xyChart.yRightAxisTickLabels.help', { + defaultMessage: 'Specifies whether or not the tick labels of the right y-axis are visible.', + }), + }, + }, + fn(input, args) { + return { + type: TICK_LABELS_CONFIG, + ...args, + }; + }, +}; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_chart.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_chart.ts new file mode 100644 index 0000000000000..599f03c5cb216 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_chart.ts @@ -0,0 +1,178 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import type { ExpressionFunctionDefinition } from '../../../../../../src/plugins/expressions/common'; +import { LensMultiTable, XYArgs, XYRender } from '../types'; +import { + XY_CHART, + DATA_LAYER, + MULTITABLE, + XYCurveTypes, + LEGEND_CONFIG, + ValueLabelModes, + FittingFunctions, + GRID_LINES_CONFIG, + XY_CHART_RENDERER, + AXIS_EXTENT_CONFIG, + TICK_LABELS_CONFIG, + REFERENCE_LINE_LAYER, + LABELS_ORIENTATION_CONFIG, + AXIS_TITLES_VISIBILITY_CONFIG, +} from '../constants'; + +export const xyChartFunction: ExpressionFunctionDefinition< + typeof XY_CHART, + LensMultiTable, + XYArgs, + XYRender +> = { + name: XY_CHART, + type: 'render', + inputTypes: [MULTITABLE], + help: i18n.translate('xpack.lens.xyChart.help', { + defaultMessage: 'An X/Y chart', + }), + args: { + title: { + types: ['string'], + help: 'The chart title.', + }, + description: { + types: ['string'], + help: '', + }, + xTitle: { + types: ['string'], + help: i18n.translate('xpack.lens.xyChart.xTitle.help', { + defaultMessage: 'X axis title', + }), + }, + yTitle: { + types: ['string'], + help: i18n.translate('xpack.lens.xyChart.yLeftTitle.help', { + defaultMessage: 'Y left axis title', + }), + }, + yRightTitle: { + types: ['string'], + help: i18n.translate('xpack.lens.xyChart.yRightTitle.help', { + defaultMessage: 'Y right axis title', + }), + }, + yLeftExtent: { + types: [AXIS_EXTENT_CONFIG], + help: i18n.translate('xpack.lens.xyChart.yLeftExtent.help', { + defaultMessage: 'Y left axis extents', + }), + }, + yRightExtent: { + types: [AXIS_EXTENT_CONFIG], + help: i18n.translate('xpack.lens.xyChart.yRightExtent.help', { + defaultMessage: 'Y right axis extents', + }), + }, + legend: { + types: [LEGEND_CONFIG], + help: i18n.translate('xpack.lens.xyChart.legend.help', { + defaultMessage: 'Configure the chart legend.', + }), + }, + fittingFunction: { + types: ['string'], + options: [...Object.values(FittingFunctions)], + help: i18n.translate('xpack.lens.xyChart.fittingFunction.help', { + defaultMessage: 'Define how missing values are treated', + }), + }, + valueLabels: { + types: ['string'], + options: [...Object.values(ValueLabelModes)], + help: '', + }, + tickLabelsVisibilitySettings: { + types: [TICK_LABELS_CONFIG], + help: i18n.translate('xpack.lens.xyChart.tickLabelsSettings.help', { + defaultMessage: 'Show x and y axes tick labels', + }), + }, + labelsOrientation: { + types: [LABELS_ORIENTATION_CONFIG], + help: i18n.translate('xpack.lens.xyChart.labelsOrientation.help', { + defaultMessage: 'Defines the rotation of the axis labels', + }), + }, + gridlinesVisibilitySettings: { + types: [GRID_LINES_CONFIG], + help: i18n.translate('xpack.lens.xyChart.gridlinesSettings.help', { + defaultMessage: 'Show x and y axes gridlines', + }), + }, + axisTitlesVisibilitySettings: { + types: [AXIS_TITLES_VISIBILITY_CONFIG], + help: i18n.translate('xpack.lens.xyChart.axisTitlesSettings.help', { + defaultMessage: 'Show x and y axes titles', + }), + }, + layers: { + types: [DATA_LAYER, REFERENCE_LINE_LAYER], + help: 'Layers of visual series', + multi: true, + }, + curveType: { + types: ['string'], + options: [...Object.values(XYCurveTypes)], + help: i18n.translate('xpack.lens.xyChart.curveType.help', { + defaultMessage: 'Define how curve type is rendered for a line chart', + }), + }, + fillOpacity: { + types: ['number'], + help: i18n.translate('xpack.lens.xyChart.fillOpacity.help', { + defaultMessage: 'Define the area chart fill opacity', + }), + }, + hideEndzones: { + types: ['boolean'], + default: false, + help: i18n.translate('xpack.lens.xyChart.hideEndzones.help', { + defaultMessage: 'Hide endzone markers for partial data', + }), + }, + valuesInLegend: { + types: ['boolean'], + default: false, + help: i18n.translate('xpack.lens.xyChart.valuesInLegend.help', { + defaultMessage: 'Show values in legend', + }), + }, + ariaLabel: { + types: ['string'], + help: i18n.translate('xpack.lens.xyChart.ariaLabel.help', { + defaultMessage: 'Specifies the aria label of the xy chart', + }), + required: false, + }, + }, + fn(data, args, handlers) { + return { + type: 'render', + as: XY_CHART_RENDERER, + value: { + data, + args: { + ...args, + ariaLabel: + args.ariaLabel ?? + (handlers.variables?.embeddableTitle as string) ?? + handlers.getExecutionContext?.()?.description, + }, + }, + }; + }, +}; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/y_axis_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/y_axis_config.ts new file mode 100644 index 0000000000000..f6b30ac7120d6 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/y_axis_config.ts @@ -0,0 +1,72 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { ExpressionFunctionDefinition } from '../../../../expressions/common'; +import { FillStyles, IconPositions, LineStyles, YAxisModes, Y_CONFIG } from '../constants'; +import { YConfig, YConfigResult } from '../types'; + +export const yAxisConfigFunction: ExpressionFunctionDefinition< + typeof Y_CONFIG, + null, + YConfig, + YConfigResult +> = { + name: Y_CONFIG, + aliases: [], + type: Y_CONFIG, + help: `Configure the behavior of a xy chart's y axis metric`, + inputTypes: ['null'], + args: { + forAccessor: { + types: ['string'], + help: 'The accessor this configuration is for', + }, + axisMode: { + types: ['string'], + options: [...Object.values(YAxisModes)], + help: 'The axis mode of the metric', + }, + color: { + types: ['string'], + help: 'The color of the series', + }, + lineStyle: { + types: ['string'], + options: [...Object.values(LineStyles)], + help: 'The style of the reference line', + }, + lineWidth: { + types: ['number'], + help: 'The width of the reference line', + }, + icon: { + types: ['string'], + help: 'An optional icon used for reference lines', + }, + iconPosition: { + types: ['string'], + options: [...Object.values(IconPositions)], + help: 'The placement of the icon for the reference line', + }, + textVisibility: { + types: ['boolean'], + help: 'Visibility of the label on the reference line', + }, + fill: { + types: ['string'], + options: [...Object.values(FillStyles)], + help: '', + }, + }, + fn(input, args) { + return { + type: Y_CONFIG, + ...args, + }; + }, +}; diff --git a/src/plugins/chart_expressions/expression_xy/common/index.ts b/src/plugins/chart_expressions/expression_xy/common/index.ts index a1dedff32b6cb..0434699ccac1e 100755 --- a/src/plugins/chart_expressions/expression_xy/common/index.ts +++ b/src/plugins/chart_expressions/expression_xy/common/index.ts @@ -8,3 +8,16 @@ export const PLUGIN_ID = 'expressionXy'; export const PLUGIN_NAME = 'expressionXy'; + +export { + xyChartFunction, + yAxisConfigFunction, + legendConfigFunction, + gridlinesConfigFunction, + dataLayerConfigFunction, + axisExtentConfigFunction, + tickLabelsConfigFunction, + labelsOrientationConfigFunction, + referenceLineLayerConfigFunction, + axisTitlesVisibilityConfigFunction, +} from './expression_functions'; diff --git a/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts b/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts new file mode 100644 index 0000000000000..3870d0a13bc7f --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts @@ -0,0 +1,215 @@ +/* + * 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 { HorizontalAlignment, Position, VerticalAlignment } from '@elastic/charts'; +import { $Values } from '@kbn/utility-types'; +import { Datatable } from '../../../../expressions'; +import { PaletteOutput } from '../../../charts/common'; +import { + AxisExtentModes, + FillStyles, + FittingFunctions, + IconPositions, + LayerTypes, + MULTITABLE, + LineStyles, + SeriesTypes, + ValueLabelModes, + XScaleTypes, + XYCurveTypes, + YAxisModes, + YScaleTypes, + REFERENCE_LINE_LAYER, + Y_CONFIG, + AXIS_TITLES_VISIBILITY_CONFIG, + LABELS_ORIENTATION_CONFIG, + TICK_LABELS_CONFIG, + GRID_LINES_CONFIG, + LEGEND_CONFIG, + DATA_LAYER, + AXIS_EXTENT_CONFIG, +} from '../constants'; + +export type LayerType = $Values; +export type YAxisMode = $Values; +export type LineStyle = $Values; +export type FillStyle = $Values; +export type SeriesType = $Values; +export type YScaleType = $Values; +export type XScaleType = $Values; +export type XYCurveType = $Values; +export type IconPosition = $Values; +export type ValueLabelMode = $Values; +export type AxisExtentMode = $Values; +export type FittingFunction = $Values; + +export interface AxesSettingsConfig { + x: boolean; + yLeft: boolean; + yRight: boolean; +} + +export interface AxisExtentConfig { + mode: AxisExtentMode; + lowerBound?: number; + upperBound?: number; +} + +export interface AxisConfig { + title: string; + hide?: boolean; +} + +export interface YConfig { + forAccessor: string; + axisMode?: YAxisMode; + color?: string; + icon?: string; + lineWidth?: number; + lineStyle?: LineStyle; + fill?: FillStyle; + iconPosition?: IconPosition; + textVisibility?: boolean; +} + +export interface XYDataLayerConfig { + layerId: string; + accessors: string[]; + seriesType: SeriesType; + xAccessor?: string; + hide?: boolean; + yConfig?: YConfigResult[]; + splitAccessor?: string; + palette?: PaletteOutput; +} +export interface ValidLayer extends XYDataLayerConfig { + xAccessor: NonNullable; +} + +export type DataLayerArgs = XYDataLayerConfig & { + columnToLabel?: string; // Actually a JSON key-value pair + yScaleType: YScaleType; + xScaleType: XScaleType; + isHistogram: boolean; + // palette will always be set on the expression + palette: PaletteOutput; +}; + +export interface LegendConfig { + /** + * Flag whether the legend should be shown. If there is just a single series, it will be hidden + */ + isVisible: boolean; + /** + * Position of the legend relative to the chart + */ + position: Position; + /** + * Flag whether the legend should be shown even with just a single series + */ + showSingleSeries?: boolean; + /** + * Flag whether the legend is inside the chart + */ + isInside?: boolean; + /** + * Horizontal Alignment of the legend when it is set inside chart + */ + horizontalAlignment?: HorizontalAlignment; + /** + * Vertical Alignment of the legend when it is set inside chart + */ + verticalAlignment?: VerticalAlignment; + /** + * Number of columns when legend is set inside chart + */ + floatingColumns?: number; + /** + * Maximum number of lines per legend item + */ + maxLines?: number; + /** + * Flag whether the legend items are truncated or not + */ + shouldTruncate?: boolean; +} + +export interface LabelsOrientationConfig { + x: number; + yLeft: number; + yRight: number; +} + +// Arguments to XY chart expression, with computed properties +export interface XYArgs { + title?: string; + description?: string; + xTitle: string; + yTitle: string; + yRightTitle: string; + yLeftExtent: AxisExtentConfigResult; + yRightExtent: AxisExtentConfigResult; + legend: LegendConfigResult; + valueLabels: ValueLabelMode; + layers: DataLayerConfigResult[] | ReferenceLineLayerConfigResult[]; + fittingFunction?: FittingFunction; + axisTitlesVisibilitySettings?: AxisTitlesVisibilityConfigResult; + tickLabelsVisibilitySettings?: TickLabelsConfigResult; + gridlinesVisibilitySettings?: GridlinesConfigResult; + labelsOrientation?: LabelsOrientationConfigResult; + curveType?: XYCurveType; + fillOpacity?: number; + hideEndzones?: boolean; + valuesInLegend?: boolean; + ariaLabel?: string; +} + +export interface XYReferenceLineLayerConfig { + layerId: string; + accessors: string[]; + yConfig?: YConfigResult[]; +} +export type ReferenceLineLayerArgs = XYReferenceLineLayerConfig & { + columnToLabel?: string; +}; + +export type XYLayerConfig = XYDataLayerConfig | XYReferenceLineLayerConfig; + +export interface LensMultiTable { + type: typeof MULTITABLE; + tables: Record; + dateRange?: { + fromDate: Date; + toDate: Date; + }; +} + +export type ReferenceLineLayerConfigResult = ReferenceLineLayerArgs & { + type: typeof REFERENCE_LINE_LAYER; + layerType: typeof LayerTypes.REFERENCELINE; +}; + +export type DataLayerConfigResult = DataLayerArgs & { + type: typeof DATA_LAYER; + layerType: typeof LayerTypes.DATA; +}; + +export type YConfigResult = YConfig & { type: typeof Y_CONFIG }; + +export type AxisTitlesVisibilityConfigResult = AxesSettingsConfig & { + type: typeof AXIS_TITLES_VISIBILITY_CONFIG; +}; + +export type LabelsOrientationConfigResult = LabelsOrientationConfig & { + type: typeof LABELS_ORIENTATION_CONFIG; +}; + +export type LegendConfigResult = LegendConfig & { type: typeof LEGEND_CONFIG }; +export type AxisExtentConfigResult = AxisExtentConfig & { type: typeof AXIS_EXTENT_CONFIG }; +export type GridlinesConfigResult = AxesSettingsConfig & { type: typeof GRID_LINES_CONFIG }; +export type TickLabelsConfigResult = AxesSettingsConfig & { type: typeof TICK_LABELS_CONFIG }; diff --git a/src/plugins/chart_expressions/expression_xy/common/types/expression_renderers.ts b/src/plugins/chart_expressions/expression_xy/common/types/expression_renderers.ts new file mode 100644 index 0000000000000..74edf916c7584 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/types/expression_renderers.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { XY_CHART_RENDERER } from '../constants'; +import { LensMultiTable, XYArgs } from './expression_functions'; + +export interface XYChartProps { + data: LensMultiTable; + args: XYArgs; +} + +export interface XYRender { + type: 'render'; + as: typeof XY_CHART_RENDERER; + value: XYChartProps; +} diff --git a/src/plugins/chart_expressions/expression_xy/common/types/index.ts b/src/plugins/chart_expressions/expression_xy/common/types/index.ts new file mode 100644 index 0000000000000..9c50bfab1305d --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/types/index.ts @@ -0,0 +1,10 @@ +/* + * 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 './expression_functions'; +export * from './expression_renderers'; diff --git a/src/plugins/chart_expressions/expression_xy/kibana.json b/src/plugins/chart_expressions/expression_xy/kibana.json index bb498969395be..9cde1fc3117a1 100755 --- a/src/plugins/chart_expressions/expression_xy/kibana.json +++ b/src/plugins/chart_expressions/expression_xy/kibana.json @@ -9,6 +9,6 @@ "description": "Expression XY plugin adds a `xy` renderer and function to the expression plugin. The renderer will display the `xy` chart.", "server": true, "ui": true, - "requiredPlugins": [], + "requiredPlugins": ["expressions", "charts"], "optionalPlugins": [] } diff --git a/src/plugins/chart_expressions/expression_xy/public/plugin.ts b/src/plugins/chart_expressions/expression_xy/public/plugin.ts index bc830c3235d8e..d2222684cffbd 100755 --- a/src/plugins/chart_expressions/expression_xy/public/plugin.ts +++ b/src/plugins/chart_expressions/expression_xy/public/plugin.ts @@ -7,18 +7,37 @@ */ import { CoreSetup, CoreStart, Plugin } from '../../../../core/public'; -import { ExpressionXyPluginSetup, ExpressionXyPluginStart } from './types'; +import { ExpressionXyPluginSetup, ExpressionXyPluginStart, SetupDeps } from './types'; +import { + xyChartFunction, + yAxisConfigFunction, + legendConfigFunction, + gridlinesConfigFunction, + dataLayerConfigFunction, + axisExtentConfigFunction, + tickLabelsConfigFunction, + labelsOrientationConfigFunction, + referenceLineLayerConfigFunction, + axisTitlesVisibilityConfigFunction, +} from '../common'; export class ExpressionXyPlugin implements Plugin { - public setup(core: CoreSetup): ExpressionXyPluginSetup { - return {}; + public setup(core: CoreSetup, { expressions }: SetupDeps): ExpressionXyPluginSetup { + expressions.registerFunction(yAxisConfigFunction); + expressions.registerFunction(legendConfigFunction); + expressions.registerFunction(gridlinesConfigFunction); + expressions.registerFunction(dataLayerConfigFunction); + expressions.registerFunction(axisExtentConfigFunction); + expressions.registerFunction(tickLabelsConfigFunction); + expressions.registerFunction(labelsOrientationConfigFunction); + expressions.registerFunction(referenceLineLayerConfigFunction); + expressions.registerFunction(axisTitlesVisibilityConfigFunction); + expressions.registerFunction(xyChartFunction); } - public start(core: CoreStart): ExpressionXyPluginStart { - return {}; - } + public start(core: CoreStart): ExpressionXyPluginStart {} public stop() {} } diff --git a/src/plugins/chart_expressions/expression_xy/public/types.ts b/src/plugins/chart_expressions/expression_xy/public/types.ts index 361e68090dda2..2025e95fe890c 100755 --- a/src/plugins/chart_expressions/expression_xy/public/types.ts +++ b/src/plugins/chart_expressions/expression_xy/public/types.ts @@ -6,8 +6,15 @@ * Side Public License, v 1. */ -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface ExpressionXyPluginSetup {} +import { ExpressionsPublicPlugin, ExpressionsServiceStart } from '../../../expressions/public'; -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface ExpressionXyPluginStart {} +export interface SetupDeps { + expressions: ReturnType; +} + +export interface StartDeps { + expression: ExpressionsServiceStart; +} + +export type ExpressionXyPluginSetup = void; +export type ExpressionXyPluginStart = void; diff --git a/src/plugins/chart_expressions/expression_xy/server/plugin.ts b/src/plugins/chart_expressions/expression_xy/server/plugin.ts index 52cf062b74864..e943277f8a119 100755 --- a/src/plugins/chart_expressions/expression_xy/server/plugin.ts +++ b/src/plugins/chart_expressions/expression_xy/server/plugin.ts @@ -9,19 +9,37 @@ import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from '../../../../core/server'; import { ExpressionXyPluginSetup, ExpressionXyPluginStart } from './types'; +import { + xyChartFunction, + yAxisConfigFunction, + legendConfigFunction, + gridlinesConfigFunction, + dataLayerConfigFunction, + axisExtentConfigFunction, + tickLabelsConfigFunction, + labelsOrientationConfigFunction, + referenceLineLayerConfigFunction, + axisTitlesVisibilityConfigFunction, +} from '../common'; +import { SetupDeps } from './types'; export class ExpressionXyPlugin implements Plugin { - constructor(initializerContext: PluginInitializerContext) {} - - public setup(core: CoreSetup) { - return {}; + public setup(core: CoreSetup, { expressions }: SetupDeps) { + expressions.registerFunction(yAxisConfigFunction); + expressions.registerFunction(legendConfigFunction); + expressions.registerFunction(gridlinesConfigFunction); + expressions.registerFunction(dataLayerConfigFunction); + expressions.registerFunction(axisExtentConfigFunction); + expressions.registerFunction(tickLabelsConfigFunction); + expressions.registerFunction(labelsOrientationConfigFunction); + expressions.registerFunction(referenceLineLayerConfigFunction); + expressions.registerFunction(axisTitlesVisibilityConfigFunction); + expressions.registerFunction(xyChartFunction); } - public start(core: CoreStart) { - return {}; - } + public start(core: CoreStart) {} public stop() {} } diff --git a/src/plugins/chart_expressions/expression_xy/server/types.ts b/src/plugins/chart_expressions/expression_xy/server/types.ts index 361e68090dda2..738f52b739229 100755 --- a/src/plugins/chart_expressions/expression_xy/server/types.ts +++ b/src/plugins/chart_expressions/expression_xy/server/types.ts @@ -6,8 +6,15 @@ * Side Public License, v 1. */ -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface ExpressionXyPluginSetup {} +import { ExpressionsServerStart, ExpressionsServerSetup } from '../../../expressions/server'; -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface ExpressionXyPluginStart {} +export type ExpressionXyPluginSetup = void; +export type ExpressionXyPluginStart = void; + +export interface SetupDeps { + expressions: ExpressionsServerSetup; +} + +export interface StartDeps { + expression: ExpressionsServerStart; +} diff --git a/src/plugins/chart_expressions/expression_xy/tsconfig.json b/src/plugins/chart_expressions/expression_xy/tsconfig.json index 97a0c8a9fc515..34350093a8989 100644 --- a/src/plugins/chart_expressions/expression_xy/tsconfig.json +++ b/src/plugins/chart_expressions/expression_xy/tsconfig.json @@ -13,12 +13,8 @@ "server/**/*", ], "references": [ + { "path": "../../charts/tsconfig.json" }, { "path": "../../../core/tsconfig.json" }, { "path": "../../expressions/tsconfig.json" }, - { "path": "../../presentation_util/tsconfig.json" }, - { "path": "../../data/tsconfig.json" }, - { "path": "../../field_formats/tsconfig.json" }, - { "path": "../../charts/tsconfig.json" }, - { "path": "../../visualizations/tsconfig.json" } ] } From 400845dc3e0b671a8b93c169bed50f2191f907e3 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Fri, 11 Mar 2022 18:07:47 +0200 Subject: [PATCH 004/133] Moved xy to a separate plugin. --- .../expression_xy/common/__mocks__/index.ts | 128 + .../expression_xy/common/constants.ts | 50 - .../expression_functions/expression.test.tsx | 117 + .../expression_xy/common/index.ts | 40 + .../common/types/expression_functions.ts | 8 +- .../expression_xy/kibana.json | 2 +- .../expression_xy/public/__mocks__/index.tsx | 224 ++ .../expression_xy/public/components/index.ts | 13 + .../public/components/legend_action.test.tsx | 17 +- .../public/components/legend_action.tsx | 17 +- .../components/legend_action_popover.tsx | 108 + .../public/components/reference_lines.scss | 0 .../public/components/reference_lines.tsx | 16 +- .../public/components}/x_domain.tsx | 12 +- .../public/components/xy_chart.scss | 0 .../public/components/xy_chart.test.tsx | 2726 +++++++++++++++ .../public/components/xy_chart.tsx | 188 +- .../public/definitions/fitting_functions.ts | 10 + .../expression_xy/public/definitions/index.ts | 10 + .../public/definitions/visualizations.ts | 128 + .../public/expression_renderers/index.ts | 10 + .../xy_chart_renderer.tsx | 80 + .../public/helpers/axes_configuration.test.ts | 334 ++ .../public/helpers/axes_configuration.ts | 146 + .../public/helpers/color_assignment.test.ts | 239 ++ .../public/helpers/color_assignment.ts | 157 + .../public/helpers}/fitting_functions.ts | 7 +- .../expression_xy/public/helpers/icon.ts | 11 + .../expression_xy/public/helpers/index.ts | 17 + .../public/helpers/interval.test.ts | 80 + .../expression_xy/public/helpers/interval.ts | 36 + .../expression_xy/public/helpers/layers.ts | 32 + .../public/helpers/reference_lines.ts | 454 +++ .../expression_xy/public/helpers/state.ts | 89 + .../public/helpers/visualization.ts | 317 ++ .../expression_xy/public/icons/area.tsx | 32 + .../public/icons/area_percentage.tsx | 32 + .../public/icons/area_stacked.tsx | 32 + .../expression_xy/public/icons/bar.tsx | 32 + .../public/icons/bar_horizontal.tsx | 32 + .../icons/bar_horizontal_percentage.tsx | 36 + .../public/icons/bar_horizontal_stacked.tsx | 36 + .../public/icons/bar_percentage.tsx | 32 + .../public/icons/bar_reference_line.tsx | 37 + .../public/icons/bar_stacked.tsx | 32 + .../expression_xy/public/icons/index.ts | 20 + .../expression_xy/public/icons/line.tsx | 32 + .../expression_xy/public/icons/mixed_xy.tsx | 36 + .../expression_xy/public/plugin.ts | 63 +- .../expression_xy/public/types.ts | 170 +- .../expression_xy/tsconfig.json | 3 + .../plugins/lens/common/expressions/index.ts | 1 - .../expressions/xy_chart/axis_config.ts | 207 -- .../expressions/xy_chart/grid_lines_config.ts | 51 - .../lens/common/expressions/xy_chart/index.ts | 17 - .../xy_chart/labels_orientation_config.ts | 60 - .../layer_config/data_layer_config.ts | 122 - .../xy_chart/layer_config/index.ts | 12 - .../reference_line_layer_config.ts | 64 - .../expressions/xy_chart/legend_config.ts | 131 - .../expressions/xy_chart/series_type.ts | 18 - .../xy_chart/tick_labels_config.ts | 51 - .../common/expressions/xy_chart/xy_args.ts | 41 - .../common/expressions/xy_chart/xy_chart.ts | 175 - x-pack/plugins/lens/kibana.json | 1 + .../shared_components/axis_title_settings.tsx | 2 +- .../axes_configuration.test.ts | 16 +- .../xy_visualization/axes_configuration.ts | 12 +- .../xy_visualization/color_assignment.test.ts | 6 +- .../xy_visualization/color_assignment.ts | 28 +- .../xy_visualization/expression.test.tsx | 2993 ----------------- .../expression_reference_lines.test.tsx | 374 -- .../expression_thresholds.scss | 18 - .../lens/public/xy_visualization/index.ts | 16 +- .../reference_line_helpers.test.ts | 64 +- .../reference_line_helpers.tsx | 44 +- .../public/xy_visualization/state_helpers.ts | 7 +- .../xy_visualization/to_expression.test.ts | 60 +- .../public/xy_visualization/to_expression.ts | 7 +- .../lens/public/xy_visualization/types.ts | 2 +- .../xy_visualization/visualization.test.ts | 185 +- .../public/xy_visualization/visualization.tsx | 16 +- .../visualization_helpers.tsx | 35 +- .../xy_config_panel/axis_settings_popover.tsx | 6 +- .../xy_config_panel/color_picker.tsx | 1 + .../xy_config_panel/dimension_editor.tsx | 3 +- .../xy_config_panel/index.tsx | 5 +- .../xy_config_panel/layer_header.tsx | 10 +- .../xy_config_panel/reference_line_panel.tsx | 8 +- .../shared/line_style_settings.tsx | 7 +- .../shared/marker_decoration_settings.tsx | 6 +- .../fitting_function_definitions.ts} | 7 +- .../visual_options_popover/index.tsx | 2 +- .../line_curve_option.tsx | 2 +- .../missing_values_option.tsx | 6 +- .../visual_options_popover.test.tsx | 38 +- .../xy_config_panel/xy_config_panel.test.tsx | 42 +- .../xy_visualization/xy_suggestions.test.ts | 72 +- .../public/xy_visualization/xy_suggestions.ts | 30 +- .../xy_visualization/xy_visualization.ts | 1 - x-pack/plugins/lens/tsconfig.json | 3 +- 101 files changed, 6818 insertions(+), 4747 deletions(-) create mode 100644 src/plugins/chart_expressions/expression_xy/common/__mocks__/index.ts create mode 100644 src/plugins/chart_expressions/expression_xy/common/expression_functions/expression.test.tsx create mode 100644 src/plugins/chart_expressions/expression_xy/public/__mocks__/index.tsx create mode 100644 src/plugins/chart_expressions/expression_xy/public/components/index.ts rename x-pack/plugins/lens/public/xy_visualization/get_legend_action.test.tsx => src/plugins/chart_expressions/expression_xy/public/components/legend_action.test.tsx (92%) rename x-pack/plugins/lens/public/xy_visualization/get_legend_action.tsx => src/plugins/chart_expressions/expression_xy/public/components/legend_action.tsx (79%) create mode 100644 src/plugins/chart_expressions/expression_xy/public/components/legend_action_popover.tsx rename x-pack/plugins/lens/public/xy_visualization/expression_reference_lines.scss => src/plugins/chart_expressions/expression_xy/public/components/reference_lines.scss (100%) rename x-pack/plugins/lens/public/xy_visualization/expression_reference_lines.tsx => src/plugins/chart_expressions/expression_xy/public/components/reference_lines.tsx (96%) rename {x-pack/plugins/lens/public/xy_visualization => src/plugins/chart_expressions/expression_xy/public/components}/x_domain.tsx (89%) rename x-pack/plugins/lens/public/xy_visualization/expression.scss => src/plugins/chart_expressions/expression_xy/public/components/xy_chart.scss (100%) create mode 100644 src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx rename x-pack/plugins/lens/public/xy_visualization/expression.tsx => src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx (82%) create mode 100644 src/plugins/chart_expressions/expression_xy/public/definitions/fitting_functions.ts create mode 100644 src/plugins/chart_expressions/expression_xy/public/definitions/index.ts create mode 100644 src/plugins/chart_expressions/expression_xy/public/definitions/visualizations.ts create mode 100644 src/plugins/chart_expressions/expression_xy/public/expression_renderers/index.ts create mode 100644 src/plugins/chart_expressions/expression_xy/public/expression_renderers/xy_chart_renderer.tsx create mode 100644 src/plugins/chart_expressions/expression_xy/public/helpers/axes_configuration.test.ts create mode 100644 src/plugins/chart_expressions/expression_xy/public/helpers/axes_configuration.ts create mode 100644 src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.test.ts create mode 100644 src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.ts rename {x-pack/plugins/lens/public/xy_visualization => src/plugins/chart_expressions/expression_xy/public/helpers}/fitting_functions.ts (66%) create mode 100644 src/plugins/chart_expressions/expression_xy/public/helpers/icon.ts create mode 100644 src/plugins/chart_expressions/expression_xy/public/helpers/index.ts create mode 100644 src/plugins/chart_expressions/expression_xy/public/helpers/interval.test.ts create mode 100644 src/plugins/chart_expressions/expression_xy/public/helpers/interval.ts create mode 100644 src/plugins/chart_expressions/expression_xy/public/helpers/layers.ts create mode 100644 src/plugins/chart_expressions/expression_xy/public/helpers/reference_lines.ts create mode 100644 src/plugins/chart_expressions/expression_xy/public/helpers/state.ts create mode 100644 src/plugins/chart_expressions/expression_xy/public/helpers/visualization.ts create mode 100644 src/plugins/chart_expressions/expression_xy/public/icons/area.tsx create mode 100644 src/plugins/chart_expressions/expression_xy/public/icons/area_percentage.tsx create mode 100644 src/plugins/chart_expressions/expression_xy/public/icons/area_stacked.tsx create mode 100644 src/plugins/chart_expressions/expression_xy/public/icons/bar.tsx create mode 100644 src/plugins/chart_expressions/expression_xy/public/icons/bar_horizontal.tsx create mode 100644 src/plugins/chart_expressions/expression_xy/public/icons/bar_horizontal_percentage.tsx create mode 100644 src/plugins/chart_expressions/expression_xy/public/icons/bar_horizontal_stacked.tsx create mode 100644 src/plugins/chart_expressions/expression_xy/public/icons/bar_percentage.tsx create mode 100644 src/plugins/chart_expressions/expression_xy/public/icons/bar_reference_line.tsx create mode 100644 src/plugins/chart_expressions/expression_xy/public/icons/bar_stacked.tsx create mode 100644 src/plugins/chart_expressions/expression_xy/public/icons/index.ts create mode 100644 src/plugins/chart_expressions/expression_xy/public/icons/line.tsx create mode 100644 src/plugins/chart_expressions/expression_xy/public/icons/mixed_xy.tsx delete mode 100644 x-pack/plugins/lens/common/expressions/xy_chart/axis_config.ts delete mode 100644 x-pack/plugins/lens/common/expressions/xy_chart/grid_lines_config.ts delete mode 100644 x-pack/plugins/lens/common/expressions/xy_chart/index.ts delete mode 100644 x-pack/plugins/lens/common/expressions/xy_chart/labels_orientation_config.ts delete mode 100644 x-pack/plugins/lens/common/expressions/xy_chart/layer_config/data_layer_config.ts delete mode 100644 x-pack/plugins/lens/common/expressions/xy_chart/layer_config/index.ts delete mode 100644 x-pack/plugins/lens/common/expressions/xy_chart/layer_config/reference_line_layer_config.ts delete mode 100644 x-pack/plugins/lens/common/expressions/xy_chart/legend_config.ts delete mode 100644 x-pack/plugins/lens/common/expressions/xy_chart/series_type.ts delete mode 100644 x-pack/plugins/lens/common/expressions/xy_chart/tick_labels_config.ts delete mode 100644 x-pack/plugins/lens/common/expressions/xy_chart/xy_args.ts delete mode 100644 x-pack/plugins/lens/common/expressions/xy_chart/xy_chart.ts delete mode 100644 x-pack/plugins/lens/public/xy_visualization/expression.test.tsx delete mode 100644 x-pack/plugins/lens/public/xy_visualization/expression_reference_lines.test.tsx delete mode 100644 x-pack/plugins/lens/public/xy_visualization/expression_thresholds.scss rename x-pack/plugins/lens/{common/expressions/xy_chart/fitting_function.ts => public/xy_visualization/xy_config_panel/visual_options_popover/fitting_function_definitions.ts} (88%) diff --git a/src/plugins/chart_expressions/expression_xy/common/__mocks__/index.ts b/src/plugins/chart_expressions/expression_xy/common/__mocks__/index.ts new file mode 100644 index 0000000000000..605ab3c2d2728 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/__mocks__/index.ts @@ -0,0 +1,128 @@ +/* + * 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 { Position } from '@elastic/charts'; +import { PaletteOutput } from 'src/plugins/charts/common'; +import { Datatable, DatatableRow } from 'src/plugins/expressions'; +import { LayerTypes } from '../constants'; +import { DataLayerConfigResult, LensMultiTable, XYArgs } from '../types'; + +export const mockPaletteOutput: PaletteOutput = { + type: 'palette', + name: 'mock', + params: {}, +}; + +export const createSampleDatatableWithRows = (rows: DatatableRow[]): Datatable => ({ + type: 'datatable', + columns: [ + { + id: 'a', + name: 'a', + meta: { type: 'number', params: { id: 'number', params: { pattern: '0,0.000' } } }, + }, + { + id: 'b', + name: 'b', + meta: { type: 'number', params: { id: 'number', params: { pattern: '000,0' } } }, + }, + { + id: 'c', + name: 'c', + meta: { + type: 'date', + field: 'order_date', + sourceParams: { type: 'date-histogram', params: { interval: 'auto' } }, + params: { id: 'string' }, + }, + }, + { id: 'd', name: 'ColD', meta: { type: 'string' } }, + ], + rows, +}); + +export const sampleLayer: DataLayerConfigResult = { + type: 'lens_xy_data_layer', + layerId: 'first', + layerType: LayerTypes.DATA, + seriesType: 'line', + xAccessor: 'c', + accessors: ['a', 'b'], + splitAccessor: 'd', + columnToLabel: '{"a": "Label A", "b": "Label B", "d": "Label D"}', + xScaleType: 'ordinal', + yScaleType: 'linear', + isHistogram: false, + palette: mockPaletteOutput, +}; + +export const createArgsWithLayers = (layers: DataLayerConfigResult[] = [sampleLayer]): XYArgs => ({ + xTitle: '', + yTitle: '', + yRightTitle: '', + legend: { + type: 'lens_xy_legendConfig', + isVisible: false, + position: Position.Top, + }, + valueLabels: 'hide', + valuesInLegend: false, + axisTitlesVisibilitySettings: { + type: 'lens_xy_axisTitlesVisibilityConfig', + x: true, + yLeft: true, + yRight: true, + }, + tickLabelsVisibilitySettings: { + type: 'lens_xy_tickLabelsConfig', + x: true, + yLeft: false, + yRight: false, + }, + labelsOrientation: { + type: 'lens_xy_labelsOrientationConfig', + x: 0, + yLeft: -90, + yRight: -45, + }, + gridlinesVisibilitySettings: { + type: 'lens_xy_gridlinesConfig', + x: true, + yLeft: false, + yRight: false, + }, + yLeftExtent: { + mode: 'full', + type: 'lens_xy_axisExtentConfig', + }, + yRightExtent: { + mode: 'full', + type: 'lens_xy_axisExtentConfig', + }, + layers, +}); + +export function sampleArgs() { + const data: LensMultiTable = { + type: 'lens_multitable', + tables: { + first: createSampleDatatableWithRows([ + { a: 1, b: 2, c: 'I', d: 'Foo' }, + { a: 1, b: 5, c: 'J', d: 'Bar' }, + ]), + }, + dateRange: { + fromDate: new Date('2019-01-02T05:00:00.000Z'), + toDate: new Date('2019-01-03T05:00:00.000Z'), + }, + }; + + const args: XYArgs = createArgsWithLayers(); + + return { data, args }; +} diff --git a/src/plugins/chart_expressions/expression_xy/common/constants.ts b/src/plugins/chart_expressions/expression_xy/common/constants.ts index bc27bdaeb72c8..0bf0f61959bfa 100644 --- a/src/plugins/chart_expressions/expression_xy/common/constants.ts +++ b/src/plugins/chart_expressions/expression_xy/common/constants.ts @@ -6,8 +6,6 @@ * Side Public License, v 1. */ -import { i18n } from '@kbn/i18n'; - export const XY_CHART = 'lens_xy_chart'; export const Y_CONFIG = 'lens_xy_yConfig'; export const MULTITABLE = 'lens_multitable'; @@ -103,51 +101,3 @@ export const ValueLabelModes = { INSIDE: 'inside', OUTSIDE: 'outside', } as const; - -export const fittingFunctionDefinitions = [ - { - id: FittingFunctions.NONE, - title: i18n.translate('xpack.lens.fittingFunctionsTitle.none', { - defaultMessage: 'Hide', - }), - description: i18n.translate('xpack.lens.fittingFunctionsDescription.none', { - defaultMessage: 'Do not fill gaps', - }), - }, - { - id: FittingFunctions.ZERO, - title: i18n.translate('xpack.lens.fittingFunctionsTitle.zero', { - defaultMessage: 'Zero', - }), - description: i18n.translate('xpack.lens.fittingFunctionsDescription.zero', { - defaultMessage: 'Fill gaps with zeros', - }), - }, - { - id: FittingFunctions.LINEAR, - title: i18n.translate('xpack.lens.fittingFunctionsTitle.linear', { - defaultMessage: 'Linear', - }), - description: i18n.translate('xpack.lens.fittingFunctionsDescription.linear', { - defaultMessage: 'Fill gaps with a line', - }), - }, - { - id: FittingFunctions.CARRY, - title: i18n.translate('xpack.lens.fittingFunctionsTitle.carry', { - defaultMessage: 'Last', - }), - description: i18n.translate('xpack.lens.fittingFunctionsDescription.carry', { - defaultMessage: 'Fill gaps with the last value', - }), - }, - { - id: FittingFunctions.LOOKAHEAD, - title: i18n.translate('xpack.lens.fittingFunctionsTitle.lookahead', { - defaultMessage: 'Next', - }), - description: i18n.translate('xpack.lens.fittingFunctionsDescription.lookahead', { - defaultMessage: 'Fill gaps with the next value', - }), - }, -]; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/expression.test.tsx b/src/plugins/chart_expressions/expression_xy/common/expression_functions/expression.test.tsx new file mode 100644 index 0000000000000..9ec9e5416ab62 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/expression.test.tsx @@ -0,0 +1,117 @@ +/* + * 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 { Position } from '@elastic/charts'; +import { LegendConfig, AxesSettingsConfig, LabelsOrientationConfig, DataLayerArgs } from '../types'; +import { + xyChartFunction, + dataLayerConfigFunction, + legendConfigFunction, + tickLabelsConfigFunction, + gridlinesConfigFunction, + labelsOrientationConfigFunction, +} from '../expression_functions'; +import { createMockExecutionContext } from '../../../../../plugins/expressions/common/mocks'; +import { mockPaletteOutput, sampleArgs } from '../__mocks__'; + +describe('xy_expression', () => { + describe('configs', () => { + test('legendConfigFunction produces the correct arguments', () => { + const args: LegendConfig = { + isVisible: true, + position: Position.Left, + }; + + const result = legendConfigFunction.fn(null, args, createMockExecutionContext()); + + expect(result).toEqual({ + type: 'lens_xy_legendConfigFunction', + ...args, + }); + }); + + test('dataLayerConfigFunction produces the correct arguments', () => { + const args: DataLayerArgs = { + layerId: 'first', + seriesType: 'line', + xAccessor: 'c', + accessors: ['a', 'b'], + splitAccessor: 'd', + xScaleType: 'linear', + yScaleType: 'linear', + isHistogram: false, + palette: mockPaletteOutput, + }; + + const result = dataLayerConfigFunction.fn(null, args, createMockExecutionContext()); + + expect(result).toEqual({ + type: 'lens_xy_data_layer', + ...args, + }); + }); + }); + + test('tickLabelsConfigFunction produces the correct arguments', () => { + const args: AxesSettingsConfig = { + x: true, + yLeft: false, + yRight: false, + }; + + const result = tickLabelsConfigFunction.fn(null, args, createMockExecutionContext()); + + expect(result).toEqual({ + type: 'lens_xy_tickLabelsConfigFunction', + ...args, + }); + }); + + test('gridlinesConfigFunction produces the correct arguments', () => { + const args: AxesSettingsConfig = { + x: true, + yLeft: false, + yRight: false, + }; + + const result = gridlinesConfigFunction.fn(null, args, createMockExecutionContext()); + + expect(result).toEqual({ + type: 'lens_xy_gridlinesConfigFunction', + ...args, + }); + }); + + test('labelsOrientationConfigFunction produces the correct arguments', () => { + const args: LabelsOrientationConfig = { + x: 0, + yLeft: -90, + yRight: -45, + }; + + const result = labelsOrientationConfigFunction.fn(null, args, createMockExecutionContext()); + + expect(result).toEqual({ + type: 'lens_xy_labelsOrientationConfigFunction', + ...args, + }); + }); + + describe('xyChartFunction', () => { + test('it renders with the specified data and args', () => { + const { data, args } = sampleArgs(); + const result = xyChartFunction.fn(data, args, createMockExecutionContext()); + + expect(result).toEqual({ + type: 'render', + as: 'lens_xy_chart_renderer', + value: { data, args }, + }); + }); + }); +}); diff --git a/src/plugins/chart_expressions/expression_xy/common/index.ts b/src/plugins/chart_expressions/expression_xy/common/index.ts index 0434699ccac1e..493d37c7d59b4 100755 --- a/src/plugins/chart_expressions/expression_xy/common/index.ts +++ b/src/plugins/chart_expressions/expression_xy/common/index.ts @@ -21,3 +21,43 @@ export { referenceLineLayerConfigFunction, axisTitlesVisibilityConfigFunction, } from './expression_functions'; + +export type { + XYArgs, + YConfig, + XYRender, + LayerType, + YAxisMode, + LineStyle, + FillStyle, + SeriesType, + YScaleType, + XScaleType, + AxisConfig, + ValidLayer, + XYCurveType, + XYChartProps, + LegendConfig, + IconPosition, + YConfigResult, + DataLayerArgs, + XYLayerConfig, + LensMultiTable, + ValueLabelMode, + AxisExtentMode, + FittingFunction, + AxisExtentConfig, + XYDataLayerConfig, + LegendConfigResult, + AxesSettingsConfig, + GridlinesConfigResult, + DataLayerConfigResult, + TickLabelsConfigResult, + AxisExtentConfigResult, + ReferenceLineLayerArgs, + LabelsOrientationConfig, + XYReferenceLineLayerConfig, + LabelsOrientationConfigResult, + ReferenceLineLayerConfigResult, + AxisTitlesVisibilityConfigResult, +} from './types'; diff --git a/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts b/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts index 3870d0a13bc7f..377219c3cc21a 100644 --- a/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts +++ b/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts @@ -9,7 +9,7 @@ import { HorizontalAlignment, Position, VerticalAlignment } from '@elastic/charts'; import { $Values } from '@kbn/utility-types'; import { Datatable } from '../../../../expressions'; -import { PaletteOutput } from '../../../charts/common'; +import { PaletteOutput } from '../../../../charts/common'; import { AxisExtentModes, FillStyles, @@ -87,7 +87,7 @@ export interface XYDataLayerConfig { splitAccessor?: string; palette?: PaletteOutput; } -export interface ValidLayer extends XYDataLayerConfig { +export interface ValidLayer extends DataLayerConfigResult { xAccessor: NonNullable; } @@ -156,7 +156,7 @@ export interface XYArgs { yRightExtent: AxisExtentConfigResult; legend: LegendConfigResult; valueLabels: ValueLabelMode; - layers: DataLayerConfigResult[] | ReferenceLineLayerConfigResult[]; + layers: Array; fittingFunction?: FittingFunction; axisTitlesVisibilitySettings?: AxisTitlesVisibilityConfigResult; tickLabelsVisibilitySettings?: TickLabelsConfigResult; @@ -178,7 +178,7 @@ export type ReferenceLineLayerArgs = XYReferenceLineLayerConfig & { columnToLabel?: string; }; -export type XYLayerConfig = XYDataLayerConfig | XYReferenceLineLayerConfig; +export type XYLayerConfig = DataLayerConfigResult | ReferenceLineLayerConfigResult; export interface LensMultiTable { type: typeof MULTITABLE; diff --git a/src/plugins/chart_expressions/expression_xy/kibana.json b/src/plugins/chart_expressions/expression_xy/kibana.json index 9cde1fc3117a1..eb34097f9c1f6 100755 --- a/src/plugins/chart_expressions/expression_xy/kibana.json +++ b/src/plugins/chart_expressions/expression_xy/kibana.json @@ -9,6 +9,6 @@ "description": "Expression XY plugin adds a `xy` renderer and function to the expression plugin. The renderer will display the `xy` chart.", "server": true, "ui": true, - "requiredPlugins": ["expressions", "charts"], + "requiredPlugins": ["expressions", "charts", "data", "fieldFormats", "uiActions"], "optionalPlugins": [] } diff --git a/src/plugins/chart_expressions/expression_xy/public/__mocks__/index.tsx b/src/plugins/chart_expressions/expression_xy/public/__mocks__/index.tsx new file mode 100644 index 0000000000000..073126c3f7ba7 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/public/__mocks__/index.tsx @@ -0,0 +1,224 @@ +/* + * 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 { chartPluginMock } from '../../../../../plugins/charts/public/mocks'; +import { DataLayerConfigResult, LensMultiTable, XYArgs } from '../../common'; +import { LayerTypes } from '../../common/constants'; +import { mockPaletteOutput, sampleArgs } from '../../common/__mocks__'; + +const chartSetupContract = chartPluginMock.createSetupContract(); +const chartStartContract = chartPluginMock.createStartContract(); + +export const chartsThemeService = chartSetupContract.theme; +export const chartsActiveCursorService = chartStartContract.activeCursor; + +export const paletteService = chartPluginMock.createPaletteRegistry(); + +export const dateHistogramData: LensMultiTable = { + type: 'lens_multitable', + tables: { + timeLayer: { + type: 'datatable', + rows: [ + { + xAccessorId: 1585758120000, + splitAccessorId: "Men's Clothing", + yAccessorId: 1, + }, + { + xAccessorId: 1585758360000, + splitAccessorId: "Women's Accessories", + yAccessorId: 1, + }, + { + xAccessorId: 1585758360000, + splitAccessorId: "Women's Clothing", + yAccessorId: 1, + }, + { + xAccessorId: 1585759380000, + splitAccessorId: "Men's Clothing", + yAccessorId: 1, + }, + { + xAccessorId: 1585759380000, + splitAccessorId: "Men's Shoes", + yAccessorId: 1, + }, + { + xAccessorId: 1585759380000, + splitAccessorId: "Women's Clothing", + yAccessorId: 1, + }, + { + xAccessorId: 1585760700000, + splitAccessorId: "Men's Clothing", + yAccessorId: 1, + }, + { + xAccessorId: 1585760760000, + splitAccessorId: "Men's Clothing", + yAccessorId: 1, + }, + { + xAccessorId: 1585760760000, + splitAccessorId: "Men's Shoes", + yAccessorId: 1, + }, + { + xAccessorId: 1585761120000, + splitAccessorId: "Men's Shoes", + yAccessorId: 1, + }, + ], + columns: [ + { + id: 'xAccessorId', + name: 'order_date per minute', + meta: { + type: 'date', + field: 'order_date', + source: 'esaggs', + index: 'indexPatternId', + sourceParams: { + indexPatternId: 'indexPatternId', + type: 'date_histogram', + appliedTimeRange: { + from: '2020-04-01T16:14:16.246Z', + to: '2020-04-01T17:15:41.263Z', + }, + params: { + field: 'order_date', + timeRange: { from: '2020-04-01T16:14:16.246Z', to: '2020-04-01T17:15:41.263Z' }, + useNormalizedEsInterval: true, + scaleMetricValues: false, + interval: '1m', + drop_partials: false, + min_doc_count: 0, + extended_bounds: {}, + }, + }, + params: { id: 'date', params: { pattern: 'HH:mm' } }, + }, + }, + { + id: 'splitAccessorId', + name: 'Top values of category.keyword', + meta: { + type: 'string', + field: 'category.keyword', + source: 'esaggs', + index: 'indexPatternId', + sourceParams: { + indexPatternId: 'indexPatternId', + type: 'terms', + params: { + field: 'category.keyword', + orderBy: 'yAccessorId', + order: 'desc', + size: 3, + otherBucket: false, + otherBucketLabel: 'Other', + missingBucket: false, + missingBucketLabel: 'Missing', + }, + }, + params: { + id: 'terms', + params: { + id: 'string', + otherBucketLabel: 'Other', + missingBucketLabel: 'Missing', + parsedUrl: { + origin: 'http://localhost:5601', + pathname: '/jiy/app/kibana', + basePath: '/jiy', + }, + }, + }, + }, + }, + { + id: 'yAccessorId', + name: 'Count of records', + meta: { + type: 'number', + source: 'esaggs', + index: 'indexPatternId', + sourceParams: { + indexPatternId: 'indexPatternId', + params: {}, + }, + params: { id: 'number' }, + }, + }, + ], + }, + }, + dateRange: { + fromDate: new Date('2020-04-01T16:14:16.246Z'), + toDate: new Date('2020-04-01T17:15:41.263Z'), + }, +}; + +export const dateHistogramLayer: DataLayerConfigResult = { + type: 'lens_xy_data_layer', + layerId: 'timeLayer', + layerType: LayerTypes.DATA, + hide: false, + xAccessor: 'xAccessorId', + yScaleType: 'linear', + xScaleType: 'time', + isHistogram: true, + splitAccessor: 'splitAccessorId', + seriesType: 'bar_stacked', + accessors: ['yAccessorId'], + palette: mockPaletteOutput, +}; + +export function sampleArgsWithReferenceLine(value: number = 150) { + const { data, args } = sampleArgs(); + + return { + data: { + ...data, + tables: { + ...data.tables, + referenceLine: { + type: 'datatable', + columns: [ + { + id: 'referenceLine-a', + meta: { params: { id: 'number' }, type: 'number' }, + name: 'Static value', + }, + ], + rows: [{ 'referenceLine-a': value }], + }, + }, + } as LensMultiTable, + args: { + ...args, + layers: [ + ...args.layers, + { + layerType: LayerTypes.REFERENCELINE, + accessors: ['referenceLine-a'], + layerId: 'referenceLine', + seriesType: 'line', + xScaleType: 'linear', + yScaleType: 'linear', + palette: mockPaletteOutput, + isHistogram: false, + hide: true, + yConfig: [{ axisMode: 'left', forAccessor: 'referenceLine-a', type: 'lens_xy_yConfig' }], + }, + ], + } as XYArgs, + }; +} diff --git a/src/plugins/chart_expressions/expression_xy/public/components/index.ts b/src/plugins/chart_expressions/expression_xy/public/components/index.ts new file mode 100644 index 0000000000000..be06610fa8922 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/public/components/index.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. + */ + +export * from './legend_action_popover'; +export * from './reference_lines'; +export * from './legend_action'; +export * from './x_domain'; +export * from './xy_chart'; diff --git a/x-pack/plugins/lens/public/xy_visualization/get_legend_action.test.tsx b/src/plugins/chart_expressions/expression_xy/public/components/legend_action.test.tsx similarity index 92% rename from x-pack/plugins/lens/public/xy_visualization/get_legend_action.test.tsx rename to src/plugins/chart_expressions/expression_xy/public/components/legend_action.test.tsx index faa3ecc976d9b..0f1cdebc5bf59 100644 --- a/x-pack/plugins/lens/public/xy_visualization/get_legend_action.test.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/legend_action.test.tsx @@ -1,8 +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; you may not use this file except in compliance with the Elastic License - * 2.0. + * 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 React from 'react'; @@ -11,14 +12,15 @@ import { EuiPopover } from '@elastic/eui'; import { mountWithIntl } from '@kbn/test-jest-helpers'; import { ComponentType, ReactWrapper } from 'enzyme'; import type { LensMultiTable } from '../../common'; -import { layerTypes } from '../../common'; -import type { DataLayerArgs } from '../../common/expressions'; -import { getLegendAction } from './get_legend_action'; -import { LegendActionPopover } from '../shared_components'; +import { LayerTypes } from '../../common/constants'; +import type { DataLayerArgs } from '../../common'; +import { getLegendAction } from './legend_action'; +import { LegendActionPopover } from './legend_action_popover'; +import { mockPaletteOutput } from '../../common/__mocks__'; const sampleLayer = { layerId: 'first', - layerType: layerTypes.DATA, + layerType: LayerTypes.DATA, seriesType: 'line', xAccessor: 'c', accessors: ['a', 'b'], @@ -27,6 +29,7 @@ const sampleLayer = { xScaleType: 'ordinal', yScaleType: 'linear', isHistogram: false, + palette: mockPaletteOutput, } as DataLayerArgs; const tables = { diff --git a/x-pack/plugins/lens/public/xy_visualization/get_legend_action.tsx b/src/plugins/chart_expressions/expression_xy/public/components/legend_action.tsx similarity index 79% rename from x-pack/plugins/lens/public/xy_visualization/get_legend_action.tsx rename to src/plugins/chart_expressions/expression_xy/public/components/legend_action.tsx index 00532314e045b..9bbdec3635fa8 100644 --- a/x-pack/plugins/lens/public/xy_visualization/get_legend_action.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/legend_action.tsx @@ -1,21 +1,22 @@ /* * 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. + * 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 React from 'react'; import type { LegendAction, XYChartSeriesIdentifier } from '@elastic/charts'; -import type { LensFilterEvent } from '../types'; -import type { LensMultiTable, FormatFactory } from '../../common'; -import type { DataLayerArgs } from '../../common/expressions'; -import { LegendActionPopover } from '../shared_components'; +import type { FilterEvent } from '../types'; +import type { LensMultiTable, DataLayerArgs } from '../../common'; +import type { FormatFactory } from '../types'; +import { LegendActionPopover } from './legend_action_popover'; export const getLegendAction = ( filteredLayers: DataLayerArgs[], tables: LensMultiTable['tables'], - onFilter: (data: LensFilterEvent['data']) => void, + onFilter: (data: FilterEvent['data']) => void, formatFactory: FormatFactory, layersAlreadyFormatted: Record ): LegendAction => @@ -55,7 +56,7 @@ export const getLegendAction = ( }, ]; - const context: LensFilterEvent['data'] = { + const context: FilterEvent['data'] = { data, }; diff --git a/src/plugins/chart_expressions/expression_xy/public/components/legend_action_popover.tsx b/src/plugins/chart_expressions/expression_xy/public/components/legend_action_popover.tsx new file mode 100644 index 0000000000000..8adc2895fe734 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/public/components/legend_action_popover.tsx @@ -0,0 +1,108 @@ +/* + * 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 React, { useState } from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiContextMenuPanelDescriptor, EuiIcon, EuiPopover, EuiContextMenu } from '@elastic/eui'; +import { useLegendAction } from '@elastic/charts'; +import type { FilterEvent } from '../types'; + +export interface LegendActionPopoverProps { + /** + * Determines the panels label + */ + label: string; + /** + * Callback on filter value + */ + onFilter: (data: FilterEvent['data']) => void; + /** + * Determines the filter event data + */ + context: FilterEvent['data']; +} + +export const LegendActionPopover: React.FunctionComponent = ({ + label, + onFilter, + context, +}) => { + const [popoverOpen, setPopoverOpen] = useState(false); + const [ref, onClose] = useLegendAction(); + const panels: EuiContextMenuPanelDescriptor[] = [ + { + id: 'main', + title: label, + items: [ + { + name: i18n.translate('xpack.lens.shared.legend.filterForValueButtonAriaLabel', { + defaultMessage: 'Filter for value', + }), + 'data-test-subj': `legend-${label}-filterIn`, + icon: , + onClick: () => { + setPopoverOpen(false); + onFilter(context); + }, + }, + { + name: i18n.translate('xpack.lens.shared.legend.filterOutValueButtonAriaLabel', { + defaultMessage: 'Filter out value', + }), + 'data-test-subj': `legend-${label}-filterOut`, + icon: , + onClick: () => { + setPopoverOpen(false); + onFilter({ ...context, negate: true }); + }, + }, + ], + }, + ]; + + const Button = ( +
setPopoverOpen(!popoverOpen)} + onClick={() => setPopoverOpen(!popoverOpen)} + > + +
+ ); + return ( + { + setPopoverOpen(false); + onClose(); + }} + panelPaddingSize="none" + anchorPosition="upLeft" + title={i18n.translate('xpack.lens.shared.legend.filterOptionsLegend', { + defaultMessage: '{legendDataLabel}, filter options', + values: { legendDataLabel: label }, + })} + > + + + ); +}; diff --git a/x-pack/plugins/lens/public/xy_visualization/expression_reference_lines.scss b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.scss similarity index 100% rename from x-pack/plugins/lens/public/xy_visualization/expression_reference_lines.scss rename to src/plugins/chart_expressions/expression_xy/public/components/reference_lines.scss diff --git a/x-pack/plugins/lens/public/xy_visualization/expression_reference_lines.tsx b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.tsx similarity index 96% rename from x-pack/plugins/lens/public/xy_visualization/expression_reference_lines.tsx rename to src/plugins/chart_expressions/expression_xy/public/components/reference_lines.tsx index d9a6a84bb5383..566c71782ef2b 100644 --- a/x-pack/plugins/lens/public/xy_visualization/expression_reference_lines.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.tsx @@ -1,11 +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; you may not use this file except in compliance with the Elastic License - * 2.0. + * 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 './expression_reference_lines.scss'; +import './reference_lines.scss'; + import React from 'react'; import { groupBy } from 'lodash'; import { EuiIcon } from '@elastic/eui'; @@ -13,9 +15,9 @@ import { RectAnnotation, AnnotationDomainType, LineAnnotation, Position } from ' import type { PaletteRegistry } from 'src/plugins/charts/public'; import type { FieldFormat } from 'src/plugins/field_formats/common'; import { euiLightVars } from '@kbn/ui-theme'; -import type { ReferenceLineLayerArgs, YConfig } from '../../common/expressions'; +import type { ReferenceLineLayerConfigResult, YConfig } from '../../common'; import type { LensMultiTable } from '../../common/types'; -import { hasIcon } from './xy_config_panel/shared/icon_select'; +import { hasIcon } from '../helpers'; export const REFERENCE_LINE_MARKER_SIZE = 20; @@ -55,7 +57,7 @@ export const computeChartMargins = ( // Note: it does not take into consideration whether the reference line is in view or not export const getReferenceLineRequiredPaddings = ( - referenceLineLayers: ReferenceLineLayerArgs[], + referenceLineLayers: ReferenceLineLayerConfigResult[], axesMap: Record<'left' | 'right', unknown> ) => { // collect all paddings for the 4 axis: if any text is detected double it. @@ -181,7 +183,7 @@ function getMarkerToShow( } export interface ReferenceLineAnnotationsProps { - layers: ReferenceLineLayerArgs[]; + layers: ReferenceLineLayerConfigResult[]; data: LensMultiTable; formatters: Record<'left' | 'right' | 'bottom', FieldFormat | undefined>; paletteService: PaletteRegistry; diff --git a/x-pack/plugins/lens/public/xy_visualization/x_domain.tsx b/src/plugins/chart_expressions/expression_xy/public/components/x_domain.tsx similarity index 89% rename from x-pack/plugins/lens/public/xy_visualization/x_domain.tsx rename to src/plugins/chart_expressions/expression_xy/public/components/x_domain.tsx index eb9de9a2993b4..dbc4c348cb891 100644 --- a/x-pack/plugins/lens/public/xy_visualization/x_domain.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/x_domain.tsx @@ -1,17 +1,17 @@ /* * 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. + * 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 { uniq } from 'lodash'; import React from 'react'; import moment from 'moment'; -import { Endzones } from '../../../../../src/plugins/charts/public'; -import type { LensMultiTable } from '../../common'; -import type { DataLayerArgs } from '../../common/expressions'; -import { search } from '../../../../../src/plugins/data/public'; +import { Endzones } from '../../../../../plugins/charts/public'; +import type { LensMultiTable, DataLayerArgs } from '../../common'; +import { search } from '../../../../../plugins/data/public'; export interface XDomain { min?: number; diff --git a/x-pack/plugins/lens/public/xy_visualization/expression.scss b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.scss similarity index 100% rename from x-pack/plugins/lens/public/xy_visualization/expression.scss rename to src/plugins/chart_expressions/expression_xy/public/components/xy_chart.scss diff --git a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx new file mode 100644 index 0000000000000..067e0a5503d34 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx @@ -0,0 +1,2726 @@ +/* + * 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 React from 'react'; +import { shallow } from 'enzyme'; +import { mountWithIntl } from '@kbn/test-jest-helpers'; +import { DataLayerConfigResult, LensMultiTable, XYArgs } from '../../common'; +import { LayerTypes } from '../../common/constants'; +import { + AreaSeries, + Axis, + BarSeries, + Fit, + GeometryValue, + HorizontalAlignment, + LayoutDirection, + LineSeries, + Position, + ScaleType, + SeriesNameFn, + Settings, + VerticalAlignment, + XYChartSeriesIdentifier, +} from '@elastic/charts'; +import { EmptyPlaceholder } from '../../../../../plugins/charts/public'; +import { XyEndzones } from './x_domain'; +import { + chartsActiveCursorService, + chartsThemeService, + dateHistogramData, + dateHistogramLayer, + paletteService, + sampleArgsWithReferenceLine, +} from '../__mocks__'; +import { + mockPaletteOutput, + sampleArgs, + createArgsWithLayers, + createSampleDatatableWithRows, + sampleLayer, +} from '../../common/__mocks__'; +import { XYChart, XYChartRenderProps } from './xy_chart'; + +const onClickValue = jest.fn(); +const onSelectRange = jest.fn(); + +describe('XYChart component', () => { + let getFormatSpy: jest.Mock; + let convertSpy: jest.Mock; + let defaultProps: Omit; + + const dataWithoutFormats: LensMultiTable = { + type: 'lens_multitable', + tables: { + first: { + type: 'datatable', + columns: [ + { id: 'a', name: 'a', meta: { type: 'number' } }, + { id: 'b', name: 'b', meta: { type: 'number' } }, + { id: 'c', name: 'c', meta: { type: 'string' } }, + { id: 'd', name: 'd', meta: { type: 'string' } }, + ], + rows: [ + { a: 1, b: 2, c: 'I', d: 'Row 1' }, + { a: 1, b: 5, c: 'J', d: 'Row 2' }, + ], + }, + }, + }; + const dataWithFormats: LensMultiTable = { + type: 'lens_multitable', + tables: { + first: { + type: 'datatable', + columns: [ + { id: 'a', name: 'a', meta: { type: 'number' } }, + { id: 'b', name: 'b', meta: { type: 'number' } }, + { id: 'c', name: 'c', meta: { type: 'string' } }, + { id: 'd', name: 'd', meta: { type: 'string', params: { id: 'custom' } } }, + ], + rows: [ + { a: 1, b: 2, c: 'I', d: 'Row 1' }, + { a: 1, b: 5, c: 'J', d: 'Row 2' }, + ], + }, + }, + }; + + const getRenderedComponent = (data: LensMultiTable, args: XYArgs) => { + return shallow(); + }; + + beforeEach(() => { + convertSpy = jest.fn((x) => x); + getFormatSpy = jest.fn(); + getFormatSpy.mockReturnValue({ convert: convertSpy }); + + defaultProps = { + formatFactory: getFormatSpy, + timeZone: 'UTC', + renderMode: 'view', + chartsThemeService, + chartsActiveCursorService, + paletteService, + minInterval: 50, + onClickValue, + onSelectRange, + syncColors: false, + useLegacyTimeAxis: false, + }; + }); + + test('it renders line', () => { + const { data, args } = sampleArgs(); + + const component = shallow( + + ); + expect(component).toMatchSnapshot(); + expect(component.find(LineSeries)).toHaveLength(2); + expect(component.find(LineSeries).at(0).prop('yAccessors')).toEqual(['a']); + expect(component.find(LineSeries).at(1).prop('yAccessors')).toEqual(['b']); + }); + + describe('date range', () => { + const timeSampleLayer: DataLayerConfigResult = { + type: 'lens_xy_data_layer', + layerId: 'first', + layerType: LayerTypes.DATA, + seriesType: 'line', + xAccessor: 'c', + accessors: ['a', 'b'], + splitAccessor: 'd', + columnToLabel: '{"a": "Label A", "b": "Label B", "d": "Label D"}', + xScaleType: 'time', + yScaleType: 'linear', + isHistogram: false, + palette: mockPaletteOutput, + }; + const multiLayerArgs = createArgsWithLayers([ + timeSampleLayer, + { + ...timeSampleLayer, + layerId: 'second', + seriesType: 'bar', + xScaleType: 'time', + }, + ]); + test('it uses the full date range', () => { + const { data, args } = sampleArgs(); + + const component = shallow( + + c.id !== 'c' + ? c + : { + ...c, + meta: { + type: 'date', + source: 'esaggs', + sourceParams: { + type: 'date_histogram', + params: {}, + appliedTimeRange: { + from: '2019-01-02T05:00:00.000Z', + to: '2019-01-03T05:00:00.000Z', + }, + }, + }, + } + ), + }, + }, + }} + args={{ + ...args, + layers: [ + { + ...args.layers[0], + seriesType: 'line', + xScaleType: 'time', + type: 'lens_xy_data_layer', + } as DataLayerConfigResult, + ], + }} + minInterval={undefined} + /> + ); + expect(component.find(Settings).prop('xDomain')).toMatchInlineSnapshot(` + Object { + "max": 1546491600000, + "min": 1546405200000, + "minInterval": undefined, + } + `); + }); + + test('it uses passed in minInterval', () => { + const data: LensMultiTable = { + type: 'lens_multitable', + tables: { + first: createSampleDatatableWithRows([{ a: 1, b: 2, c: 'I', d: 'Foo' }]), + second: createSampleDatatableWithRows([]), + }, + }; + + const component = shallow(); + + // real auto interval is 30mins = 1800000 + expect(component.find(Settings).prop('xDomain')).toMatchInlineSnapshot(` + Object { + "max": NaN, + "min": NaN, + "minInterval": 50, + } + `); + }); + + describe('axis time', () => { + const defaultTimeLayer: DataLayerConfigResult = { + type: 'lens_xy_data_layer', + layerId: 'first', + layerType: LayerTypes.DATA, + seriesType: 'line', + xAccessor: 'c', + accessors: ['a', 'b'], + splitAccessor: 'd', + columnToLabel: '{"a": "Label A", "b": "Label B", "d": "Label D"}', + xScaleType: 'time', + yScaleType: 'linear', + isHistogram: true, + palette: mockPaletteOutput, + }; + test('it should disable the new time axis for a line time layer when isHistogram is set to false', () => { + const { data } = sampleArgs(); + + const instance = shallow( + + ); + + const axisStyle = instance.find(Axis).first().prop('timeAxisLayerCount'); + + expect(axisStyle).toBe(0); + }); + test('it should enable the new time axis for a line time layer when isHistogram is set to true', () => { + const { data } = sampleArgs(); + const timeLayerArgs = createArgsWithLayers([defaultTimeLayer]); + + const instance = shallow( + + ); + + const axisStyle = instance.find(Axis).first().prop('timeAxisLayerCount'); + + expect(axisStyle).toBe(3); + }); + test('it should disable the new time axis for a vertical bar with break down dimension', () => { + const { data } = sampleArgs(); + const timeLayer: DataLayerConfigResult = { + ...defaultTimeLayer, + seriesType: 'bar', + }; + const timeLayerArgs = createArgsWithLayers([timeLayer]); + + const instance = shallow( + + ); + + const axisStyle = instance.find(Axis).first().prop('timeAxisLayerCount'); + + expect(axisStyle).toBe(0); + }); + + test('it should enable the new time axis for a stacked vertical bar with break down dimension', () => { + const { data } = sampleArgs(); + const timeLayer: DataLayerConfigResult = { + ...defaultTimeLayer, + seriesType: 'bar_stacked', + }; + const timeLayerArgs = createArgsWithLayers([timeLayer]); + + const instance = shallow( + + ); + + const axisStyle = instance.find(Axis).first().prop('timeAxisLayerCount'); + + expect(axisStyle).toBe(3); + }); + }); + describe('endzones', () => { + const { args } = sampleArgs(); + const table = createSampleDatatableWithRows([ + { a: 1, b: 2, c: new Date('2021-04-22').valueOf(), d: 'Foo' }, + { a: 1, b: 2, c: new Date('2021-04-23').valueOf(), d: 'Foo' }, + { a: 1, b: 2, c: new Date('2021-04-24').valueOf(), d: 'Foo' }, + ]); + const data: LensMultiTable = { + type: 'lens_multitable', + tables: { + first: { + ...table, + columns: table.columns.map((c) => + c.id !== 'c' + ? c + : { + ...c, + meta: { + type: 'date', + source: 'esaggs', + sourceParams: { + type: 'date_histogram', + params: {}, + appliedTimeRange: { + from: '2021-04-22T12:00:00.000Z', + to: '2021-04-24T12:00:00.000Z', + }, + }, + }, + } + ), + }, + }, + dateRange: { + // first and last bucket are partial + fromDate: new Date('2021-04-22T12:00:00.000Z'), + toDate: new Date('2021-04-24T12:00:00.000Z'), + }, + }; + const timeArgs: XYArgs = { + ...args, + layers: [ + { + ...args.layers[0], + type: 'lens_xy_data_layer', + seriesType: 'line', + xScaleType: 'time', + isHistogram: true, + splitAccessor: undefined, + } as DataLayerConfigResult, + ], + }; + + test('it extends interval if data is exceeding it', () => { + const component = shallow( + + ); + + expect(component.find(Settings).prop('xDomain')).toEqual({ + // shortened to 24th midnight (elastic-charts automatically adds one min interval) + max: new Date('2021-04-24').valueOf(), + // extended to 22nd midnight because of first bucket + min: new Date('2021-04-22').valueOf(), + minInterval: 24 * 60 * 60 * 1000, + }); + }); + + test('it renders endzone component bridging gap between domain and extended domain', () => { + const component = shallow( + + ); + + expect(component.find(XyEndzones).dive().find('Endzones').props()).toEqual( + expect.objectContaining({ + domainStart: new Date('2021-04-22T12:00:00.000Z').valueOf(), + domainEnd: new Date('2021-04-24T12:00:00.000Z').valueOf(), + domainMin: new Date('2021-04-22').valueOf(), + domainMax: new Date('2021-04-24').valueOf(), + }) + ); + }); + + test('should pass enabled histogram mode and min interval to endzones component', () => { + const component = shallow( + + ); + + expect(component.find(XyEndzones).dive().find('Endzones').props()).toEqual( + expect.objectContaining({ + interval: 24 * 60 * 60 * 1000, + isFullBin: false, + }) + ); + }); + + test('should pass disabled histogram mode and min interval to endzones component', () => { + const component = shallow( + + ); + + expect(component.find(XyEndzones).dive().find('Endzones').props()).toEqual( + expect.objectContaining({ + interval: 24 * 60 * 60 * 1000, + isFullBin: true, + }) + ); + }); + + test('it does not render endzones if disabled via settings', () => { + const component = shallow( + + ); + + expect(component.find(XyEndzones).length).toEqual(0); + }); + }); + }); + + describe('y axis extents', () => { + test('it passes custom y axis extents to elastic-charts axis spec', () => { + const { data, args } = sampleArgs(); + + const component = shallow( + + ); + expect(component.find(Axis).find('[id="left"]').prop('domain')).toEqual({ + fit: false, + min: 123, + max: 456, + }); + }); + + test('it passes fit to bounds y axis extents to elastic-charts axis spec', () => { + const { data, args } = sampleArgs(); + + const component = shallow( + + ); + expect(component.find(Axis).find('[id="left"]').prop('domain')).toEqual({ + fit: true, + min: NaN, + max: NaN, + }); + }); + + test('it does not allow fit for area chart', () => { + const { data, args } = sampleArgs(); + + const component = shallow( + + ); + expect(component.find(Axis).find('[id="left"]').prop('domain')).toEqual({ + fit: false, + min: NaN, + max: NaN, + }); + }); + + test('it does not allow positive lower bound for bar', () => { + const { data, args } = sampleArgs(); + + const component = shallow( + + ); + expect(component.find(Axis).find('[id="left"]').prop('domain')).toEqual({ + fit: false, + min: NaN, + max: NaN, + }); + }); + + test('it does include referenceLine values when in full extent mode', () => { + const { data, args } = sampleArgsWithReferenceLine(); + + const component = shallow(); + expect(component.find(Axis).find('[id="left"]').prop('domain')).toEqual({ + fit: false, + min: 0, + max: 150, + }); + }); + + test('it should ignore referenceLine values when set to custom extents', () => { + const { data, args } = sampleArgsWithReferenceLine(); + + const component = shallow( + + ); + expect(component.find(Axis).find('[id="left"]').prop('domain')).toEqual({ + fit: false, + min: 123, + max: 456, + }); + }); + + test('it should work for negative values in referenceLines', () => { + const { data, args } = sampleArgsWithReferenceLine(-150); + + const component = shallow(); + expect(component.find(Axis).find('[id="left"]').prop('domain')).toEqual({ + fit: false, + min: -150, + max: 5, + }); + }); + }); + + test('it has xDomain undefined if the x is not a time scale or a histogram', () => { + const { data, args } = sampleArgs(); + + const component = shallow( + + ); + const xDomain = component.find(Settings).prop('xDomain'); + expect(xDomain).toEqual(undefined); + }); + + test('it uses min interval if interval is passed in and visualization is histogram', () => { + const { data, args } = sampleArgs(); + + const component = shallow( + + ); + expect(component.find(Settings).prop('xDomain')).toEqual({ + minInterval: 101, + min: NaN, + max: NaN, + }); + }); + + test('disabled legend extra by default', () => { + const { data, args } = sampleArgs(); + const component = shallow(); + expect(component.find(Settings).at(0).prop('showLegendExtra')).toEqual(false); + }); + + test('ignores legend extra for ordinal chart', () => { + const { data, args } = sampleArgs(); + const component = shallow( + + ); + expect(component.find(Settings).at(0).prop('showLegendExtra')).toEqual(false); + }); + + test('shows legend extra for histogram chart', () => { + const { args } = sampleArgs(); + const component = shallow( + + ); + expect(component.find(Settings).at(0).prop('showLegendExtra')).toEqual(true); + }); + + test('it renders bar', () => { + const { data, args } = sampleArgs(); + const component = shallow( + + ); + expect(component).toMatchSnapshot(); + expect(component.find(BarSeries)).toHaveLength(2); + expect(component.find(BarSeries).at(0).prop('yAccessors')).toEqual(['a']); + expect(component.find(BarSeries).at(1).prop('yAccessors')).toEqual(['b']); + }); + + test('it renders area', () => { + const { data, args } = sampleArgs(); + const component = shallow( + + ); + expect(component).toMatchSnapshot(); + expect(component.find(AreaSeries)).toHaveLength(2); + expect(component.find(AreaSeries).at(0).prop('yAccessors')).toEqual(['a']); + expect(component.find(AreaSeries).at(1).prop('yAccessors')).toEqual(['b']); + }); + + test('it renders horizontal bar', () => { + const { data, args } = sampleArgs(); + const component = shallow( + + ); + expect(component).toMatchSnapshot(); + expect(component.find(BarSeries)).toHaveLength(2); + expect(component.find(BarSeries).at(0).prop('yAccessors')).toEqual(['a']); + expect(component.find(BarSeries).at(1).prop('yAccessors')).toEqual(['b']); + expect(component.find(Settings).prop('rotation')).toEqual(90); + }); + + test('it renders regular bar empty placeholder for no results', () => { + const { data, args } = sampleArgs(); + + // send empty data to the chart + data.tables.first.rows = []; + + const component = shallow(); + + expect(component.find(BarSeries)).toHaveLength(0); + expect(component.find(EmptyPlaceholder).prop('icon')).toBeDefined(); + }); + + test('onBrushEnd returns correct context data for date histogram data', () => { + const { args } = sampleArgs(); + + const wrapper = mountWithIntl( + + ); + wrapper.find(Settings).first().prop('onBrushEnd')!({ x: [1585757732783, 1585758880838] }); + + expect(onSelectRange).toHaveBeenCalledWith({ + column: 0, + table: dateHistogramData.tables.timeLayer, + range: [1585757732783, 1585758880838], + }); + }); + + test('onBrushEnd returns correct context data for number histogram data', () => { + const { args } = sampleArgs(); + + const numberLayer: DataLayerConfigResult = { + layerId: 'numberLayer', + type: 'lens_xy_data_layer', + layerType: LayerTypes.DATA, + hide: false, + xAccessor: 'xAccessorId', + yScaleType: 'linear', + xScaleType: 'linear', + isHistogram: true, + seriesType: 'bar_stacked', + accessors: ['yAccessorId'], + palette: mockPaletteOutput, + }; + + const numberHistogramData: LensMultiTable = { + type: 'lens_multitable', + tables: { + numberLayer: { + type: 'datatable', + rows: [ + { + xAccessorId: 5, + yAccessorId: 1, + }, + { + xAccessorId: 7, + yAccessorId: 1, + }, + { + xAccessorId: 8, + yAccessorId: 1, + }, + { + xAccessorId: 10, + yAccessorId: 1, + }, + ], + columns: [ + { + id: 'xAccessorId', + name: 'bytes', + meta: { type: 'number' }, + }, + { + id: 'yAccessorId', + name: 'Count of records', + meta: { type: 'number' }, + }, + ], + }, + }, + dateRange: { + fromDate: new Date('2020-04-01T16:14:16.246Z'), + toDate: new Date('2020-04-01T17:15:41.263Z'), + }, + }; + + const wrapper = mountWithIntl( + + ); + + wrapper.find(Settings).first().prop('onBrushEnd')!({ x: [5, 8] }); + + expect(onSelectRange).toHaveBeenCalledWith({ + column: 0, + table: numberHistogramData.tables.numberLayer, + range: [5, 8], + }); + }); + + test('onBrushEnd is not set on non-interactive mode', () => { + const { args, data } = sampleArgs(); + + const wrapper = mountWithIntl( + + ); + + expect(wrapper.find(Settings).first().prop('onBrushEnd')).toBeUndefined(); + }); + + test('allowBrushingLastHistogramBin is true for date histogram data', () => { + const { args } = sampleArgs(); + + const wrapper = mountWithIntl( + + ); + expect(wrapper.find(Settings).at(0).prop('allowBrushingLastHistogramBin')).toEqual(true); + }); + + test('onElementClick returns correct context data', () => { + const geometry: GeometryValue = { x: 5, y: 1, accessor: 'y1', mark: null, datum: {} }; + const series = { + key: 'spec{d}yAccessor{d}splitAccessors{b-2}', + specId: 'd', + yAccessor: 'd', + splitAccessors: {}, + seriesKeys: [2, 'd'], + }; + + const { args, data } = sampleArgs(); + + const wrapper = mountWithIntl( + + ); + + wrapper.find(Settings).first().prop('onElementClick')!([ + [geometry, series as XYChartSeriesIdentifier], + ]); + + expect(onClickValue).toHaveBeenCalledWith({ + data: [ + { + column: 1, + row: 1, + table: data.tables.first, + value: 5, + }, + { + column: 1, + row: 0, + table: data.tables.first, + value: 2, + }, + ], + }); + }); + + test('onElementClick returns correct context data for date histogram', () => { + const geometry: GeometryValue = { + x: 1585758120000, + y: 1, + accessor: 'y1', + mark: null, + datum: {}, + }; + const series = { + key: 'spec{d}yAccessor{d}splitAccessors{b-2}', + specId: 'd', + yAccessor: 'yAccessorId', + splitAccessors: {}, + seriesKeys: ['yAccessorId'], + }; + + const { args } = sampleArgs(); + + const wrapper = mountWithIntl( + + ); + + wrapper.find(Settings).first().prop('onElementClick')!([ + [geometry, series as XYChartSeriesIdentifier], + ]); + + expect(onClickValue).toHaveBeenCalledWith({ + data: [ + { + column: 0, + row: 0, + table: dateHistogramData.tables.timeLayer, + value: 1585758120000, + }, + ], + }); + }); + + test('onElementClick returns correct context data for numeric histogram', () => { + const { args } = sampleArgs(); + + const numberLayer: DataLayerConfigResult = { + type: 'lens_xy_data_layer', + layerId: 'numberLayer', + layerType: LayerTypes.DATA, + hide: false, + xAccessor: 'xAccessorId', + yScaleType: 'linear', + xScaleType: 'linear', + isHistogram: true, + seriesType: 'bar_stacked', + accessors: ['yAccessorId'], + palette: mockPaletteOutput, + }; + + const numberHistogramData: LensMultiTable = { + type: 'lens_multitable', + tables: { + numberLayer: { + type: 'datatable', + rows: [ + { + xAccessorId: 5, + yAccessorId: 1, + }, + { + xAccessorId: 7, + yAccessorId: 1, + }, + { + xAccessorId: 8, + yAccessorId: 1, + }, + { + xAccessorId: 10, + yAccessorId: 1, + }, + ], + columns: [ + { + id: 'xAccessorId', + name: 'bytes', + meta: { type: 'number' }, + }, + { + id: 'yAccessorId', + name: 'Count of records', + meta: { type: 'number' }, + }, + ], + }, + }, + dateRange: { + fromDate: new Date('2020-04-01T16:14:16.246Z'), + toDate: new Date('2020-04-01T17:15:41.263Z'), + }, + }; + const geometry: GeometryValue = { + x: 5, + y: 1, + accessor: 'y1', + mark: null, + datum: {}, + }; + const series = { + key: 'spec{d}yAccessor{d}splitAccessors{b-2}', + specId: 'd', + yAccessor: 'yAccessorId', + splitAccessors: {}, + seriesKeys: ['yAccessorId'], + }; + + const wrapper = mountWithIntl( + + ); + + wrapper.find(Settings).first().prop('onElementClick')!([ + [geometry, series as XYChartSeriesIdentifier], + ]); + + expect(onClickValue).toHaveBeenCalledWith({ + data: [ + { + column: 0, + row: 0, + table: numberHistogramData.tables.numberLayer, + value: 5, + }, + ], + timeFieldName: undefined, + }); + }); + + test('returns correct original data for ordinal x axis with special formatter', () => { + const geometry: GeometryValue = { x: 'BAR', y: 1, accessor: 'y1', mark: null, datum: {} }; + const series = { + key: 'spec{d}yAccessor{d}splitAccessors{b-2}', + specId: 'd', + yAccessor: 'a', + splitAccessors: {}, + seriesKeys: ['a'], + }; + + const { args, data } = sampleArgs(); + + convertSpy.mockImplementation((x) => (typeof x === 'string' ? x.toUpperCase() : x)); + + const wrapper = mountWithIntl( + + ); + + wrapper.find(Settings).first().prop('onElementClick')!([ + [geometry, series as XYChartSeriesIdentifier], + ]); + + expect(onClickValue).toHaveBeenCalledWith({ + data: [ + { + column: 3, + row: 1, + table: data.tables.first, + value: 'Bar', + }, + ], + }); + }); + + test('sets up correct yScaleType equal to binary_linear for bytes formatting', () => { + const { args, data } = sampleArgs(); + data.tables.first.columns[0].meta = { + type: 'number', + params: { id: 'bytes', params: { pattern: '0,0.00b' } }, + }; + + const wrapper = mountWithIntl( + + ); + + expect(wrapper.find(LineSeries).at(0).prop('yScaleType')).toEqual('linear_binary'); + }); + + test('allowBrushingLastHistogramBin should be fakse for ordinal data', () => { + const { args, data } = sampleArgs(); + + const wrapper = mountWithIntl( + + ); + + expect(wrapper.find(Settings).at(0).prop('allowBrushingLastHistogramBin')).toEqual(false); + }); + + test('onElementClick is not triggering event on non-interactive mode', () => { + const { args, data } = sampleArgs(); + + const wrapper = mountWithIntl( + + ); + + expect(wrapper.find(Settings).first().prop('onElementClick')).toBeUndefined(); + }); + + test('legendAction is not triggering event on non-interactive mode', () => { + const { args, data } = sampleArgs(); + + const wrapper = mountWithIntl( + + ); + + expect(wrapper.find(Settings).first().prop('legendAction')).toBeUndefined(); + }); + + test('it renders stacked bar', () => { + const { data, args } = sampleArgs(); + const component = shallow( + + ); + expect(component).toMatchSnapshot(); + expect(component.find(BarSeries)).toHaveLength(2); + expect(component.find(BarSeries).at(0).prop('stackAccessors')).toHaveLength(1); + expect(component.find(BarSeries).at(1).prop('stackAccessors')).toHaveLength(1); + }); + + test('it renders stacked area', () => { + const { data, args } = sampleArgs(); + const component = shallow( + + ); + expect(component).toMatchSnapshot(); + expect(component.find(AreaSeries)).toHaveLength(2); + expect(component.find(AreaSeries).at(0).prop('stackAccessors')).toHaveLength(1); + expect(component.find(AreaSeries).at(1).prop('stackAccessors')).toHaveLength(1); + }); + + test('it renders stacked horizontal bar', () => { + const { data, args } = sampleArgs(); + const component = shallow( + + ); + expect(component).toMatchSnapshot(); + expect(component.find(BarSeries)).toHaveLength(2); + expect(component.find(BarSeries).at(0).prop('stackAccessors')).toHaveLength(1); + expect(component.find(BarSeries).at(1).prop('stackAccessors')).toHaveLength(1); + expect(component.find(Settings).prop('rotation')).toEqual(90); + }); + + test('it renders stacked bar empty placeholder for no results', () => { + const { data, args } = sampleArgs(); + + const component = shallow( + + ); + + expect(component.find(BarSeries)).toHaveLength(0); + expect(component.find(EmptyPlaceholder).prop('icon')).toBeDefined(); + }); + + test('it passes time zone to the series', () => { + const { data, args } = sampleArgs(); + const component = shallow( + + ); + expect(component.find(LineSeries).at(0).prop('timeZone')).toEqual('CEST'); + expect(component.find(LineSeries).at(1).prop('timeZone')).toEqual('CEST'); + }); + + test('it applies histogram mode to the series for single series', () => { + const { data, args } = sampleArgs(); + const firstLayer: DataLayerConfigResult = { + ...args.layers[0], + accessors: ['b'], + seriesType: 'bar', + isHistogram: true, + type: 'lens_xy_data_layer', + layerType: LayerTypes.DATA, + xScaleType: 'ordinal', + yScaleType: 'linear', + palette: mockPaletteOutput, + }; + delete firstLayer.splitAccessor; + const component = shallow( + + ); + expect(component.find(BarSeries).at(0).prop('enableHistogramMode')).toEqual(true); + }); + + test('it does not apply histogram mode to more than one bar series for unstacked bar chart', () => { + const { data, args } = sampleArgs(); + const firstLayer: DataLayerConfigResult = { + ...args.layers[0], + seriesType: 'bar', + isHistogram: true, + type: 'lens_xy_data_layer', + layerType: LayerTypes.DATA, + xScaleType: 'ordinal', + yScaleType: 'linear', + palette: mockPaletteOutput, + }; + delete firstLayer.splitAccessor; + const component = shallow( + + ); + expect(component.find(BarSeries).at(0).prop('enableHistogramMode')).toEqual(false); + expect(component.find(BarSeries).at(1).prop('enableHistogramMode')).toEqual(false); + }); + + test('it applies histogram mode to more than one the series for unstacked line/area chart', () => { + const { data, args } = sampleArgs(); + const firstLayer: DataLayerConfigResult = { + ...args.layers[0], + seriesType: 'line', + isHistogram: true, + type: 'lens_xy_data_layer', + layerType: LayerTypes.DATA, + xScaleType: 'ordinal', + yScaleType: 'linear', + palette: mockPaletteOutput, + }; + delete firstLayer.splitAccessor; + const secondLayer: DataLayerConfigResult = { + ...args.layers[0], + seriesType: 'line', + isHistogram: true, + type: 'lens_xy_data_layer', + layerType: LayerTypes.DATA, + xScaleType: 'ordinal', + yScaleType: 'linear', + palette: mockPaletteOutput, + }; + delete secondLayer.splitAccessor; + const component = shallow( + + ); + expect(component.find(LineSeries).at(0).prop('enableHistogramMode')).toEqual(true); + expect(component.find(LineSeries).at(1).prop('enableHistogramMode')).toEqual(true); + }); + + test('it applies histogram mode to the series for stacked series', () => { + const { data, args } = sampleArgs(); + const component = shallow( + + ); + expect(component.find(BarSeries).at(0).prop('enableHistogramMode')).toEqual(true); + expect(component.find(BarSeries).at(1).prop('enableHistogramMode')).toEqual(true); + }); + + test('it does not apply histogram mode for splitted series', () => { + const { data, args } = sampleArgs(); + const component = shallow( + + ); + expect(component.find(BarSeries).at(0).prop('enableHistogramMode')).toEqual(false); + expect(component.find(BarSeries).at(1).prop('enableHistogramMode')).toEqual(false); + }); + + describe('y axes', () => { + test('single axis if possible', () => { + const args = createArgsWithLayers(); + + const component = getRenderedComponent(dataWithoutFormats, args); + const axes = component.find(Axis); + expect(axes).toHaveLength(2); + }); + + test('multiple axes because of config', () => { + const args = createArgsWithLayers(); + const newArgs = { + ...args, + layers: [ + { + ...args.layers[0], + accessors: ['a', 'b'], + yConfig: [ + { + forAccessor: 'a', + axisMode: 'left', + }, + { + forAccessor: 'b', + axisMode: 'right', + }, + ], + }, + ], + } as XYArgs; + + const component = getRenderedComponent(dataWithoutFormats, newArgs); + const axes = component.find(Axis); + expect(axes).toHaveLength(3); + expect(component.find(LineSeries).at(0).prop('groupId')).toEqual(axes.at(1).prop('groupId')); + expect(component.find(LineSeries).at(1).prop('groupId')).toEqual(axes.at(2).prop('groupId')); + }); + + test('multiple axes because of incompatible formatters', () => { + const args = createArgsWithLayers(); + const newArgs = { + ...args, + layers: [ + { + ...args.layers[0], + accessors: ['c', 'd'], + }, + ], + } as XYArgs; + + const component = getRenderedComponent(dataWithFormats, newArgs); + const axes = component.find(Axis); + expect(axes).toHaveLength(3); + expect(component.find(LineSeries).at(0).prop('groupId')).toEqual(axes.at(1).prop('groupId')); + expect(component.find(LineSeries).at(1).prop('groupId')).toEqual(axes.at(2).prop('groupId')); + }); + + test('single axis despite different formatters if enforced', () => { + const args = createArgsWithLayers(); + const newArgs = { + ...args, + layers: [ + { + ...args.layers[0], + accessors: ['c', 'd'], + yConfig: [ + { + forAccessor: 'c', + axisMode: 'left', + }, + { + forAccessor: 'd', + axisMode: 'left', + }, + ], + }, + ], + } as XYArgs; + + const component = getRenderedComponent(dataWithoutFormats, newArgs); + const axes = component.find(Axis); + expect(axes).toHaveLength(2); + }); + }); + + describe('y series coloring', () => { + test('color is applied to chart for multiple series', () => { + const args = createArgsWithLayers(); + const newArgs = { + ...args, + layers: [ + { + ...args.layers[0], + splitAccessor: undefined, + accessors: ['a', 'b'], + yConfig: [ + { + forAccessor: 'a', + color: '#550000', + }, + { + forAccessor: 'b', + color: '#FFFF00', + }, + ], + }, + { + ...args.layers[0], + splitAccessor: undefined, + accessors: ['c'], + yConfig: [ + { + forAccessor: 'c', + color: '#FEECDF', + }, + ], + }, + ], + } as XYArgs; + + const component = getRenderedComponent(dataWithoutFormats, newArgs); + expect( + (component.find(LineSeries).at(0).prop('color') as Function)!({ + yAccessor: 'a', + seriesKeys: ['a'], + }) + ).toEqual('#550000'); + expect( + (component.find(LineSeries).at(1).prop('color') as Function)!({ + yAccessor: 'b', + seriesKeys: ['b'], + }) + ).toEqual('#FFFF00'); + expect( + (component.find(LineSeries).at(2).prop('color') as Function)!({ + yAccessor: 'c', + seriesKeys: ['c'], + }) + ).toEqual('#FEECDF'); + }); + test('color is not applied to chart when splitAccessor is defined or when yConfig is not configured', () => { + const args = createArgsWithLayers(); + const newArgs = { + ...args, + layers: [ + { + ...args.layers[0], + accessors: ['a'], + yConfig: [ + { + forAccessor: 'a', + color: '#550000', + }, + ], + }, + { + ...args.layers[0], + splitAccessor: undefined, + accessors: ['c'], + }, + ], + } as XYArgs; + + const component = getRenderedComponent(dataWithoutFormats, newArgs); + expect( + (component.find(LineSeries).at(0).prop('color') as Function)!({ + yAccessor: 'a', + seriesKeys: ['a'], + }) + ).toEqual('blue'); + expect( + (component.find(LineSeries).at(1).prop('color') as Function)!({ + yAccessor: 'c', + seriesKeys: ['c'], + }) + ).toEqual('blue'); + }); + }); + + describe('provides correct series naming', () => { + const nameFnArgs = { + seriesKeys: [], + key: '', + specId: 'a', + yAccessor: '', + splitAccessors: new Map(), + }; + + test('simplest xy chart without human-readable name', () => { + const args = createArgsWithLayers(); + const newArgs = { + ...args, + layers: [ + { + ...args.layers[0], + accessors: ['a'], + splitAccessor: undefined, + columnToLabel: '', + }, + ], + }; + + const component = getRenderedComponent(dataWithoutFormats, newArgs); + const nameFn = component.find(LineSeries).prop('name') as SeriesNameFn; + + // In this case, the ID is used as the name. This shouldn't happen in practice + expect(nameFn({ ...nameFnArgs, seriesKeys: ['a'] }, false)).toEqual(''); + expect(nameFn({ ...nameFnArgs, seriesKeys: ['nonsense'] }, false)).toEqual(''); + }); + + test('simplest xy chart with empty name', () => { + const args = createArgsWithLayers(); + const newArgs = { + ...args, + layers: [ + { + ...args.layers[0], + accessors: ['a'], + splitAccessor: undefined, + columnToLabel: '{"a":""}', + }, + ], + }; + + const component = getRenderedComponent(dataWithoutFormats, newArgs); + const nameFn = component.find(LineSeries).prop('name') as SeriesNameFn; + + // In this case, the ID is used as the name. This shouldn't happen in practice + expect(nameFn({ ...nameFnArgs, seriesKeys: ['a'] }, false)).toEqual(''); + expect(nameFn({ ...nameFnArgs, seriesKeys: ['nonsense'] }, false)).toEqual(''); + }); + + test('simplest xy chart with human-readable name', () => { + const args = createArgsWithLayers(); + const newArgs = { + ...args, + layers: [ + { + ...args.layers[0], + accessors: ['a'], + splitAccessor: undefined, + columnToLabel: '{"a":"Column A"}', + }, + ], + }; + + const component = getRenderedComponent(dataWithoutFormats, newArgs); + const nameFn = component.find(LineSeries).prop('name') as SeriesNameFn; + + expect(nameFn({ ...nameFnArgs, seriesKeys: ['a'] }, false)).toEqual('Column A'); + }); + + test('multiple y accessors', () => { + const args = createArgsWithLayers(); + const newArgs = { + ...args, + layers: [ + { + ...args.layers[0], + accessors: ['a', 'b'], + splitAccessor: undefined, + columnToLabel: '{"a": "Label A"}', + }, + ], + }; + + const component = getRenderedComponent(dataWithoutFormats, newArgs); + const nameFn1 = component.find(LineSeries).at(0).prop('name') as SeriesNameFn; + const nameFn2 = component.find(LineSeries).at(1).prop('name') as SeriesNameFn; + + // This accessor has a human-readable name + expect(nameFn1({ ...nameFnArgs, seriesKeys: ['a'] }, false)).toEqual('Label A'); + // This accessor does not + expect(nameFn2({ ...nameFnArgs, seriesKeys: ['b'] }, false)).toEqual(''); + expect(nameFn1({ ...nameFnArgs, seriesKeys: ['nonsense'] }, false)).toEqual(''); + }); + + test('split series without formatting and single y accessor', () => { + const args = createArgsWithLayers(); + const newArgs = { + ...args, + layers: [ + { + ...args.layers[0], + accessors: ['a'], + splitAccessor: 'd', + columnToLabel: '{"a": "Label A"}', + }, + ], + }; + + const component = getRenderedComponent(dataWithoutFormats, newArgs); + const nameFn = component.find(LineSeries).prop('name') as SeriesNameFn; + + expect(nameFn({ ...nameFnArgs, seriesKeys: ['split1', 'a'] }, false)).toEqual('split1'); + }); + + test('split series with formatting and single y accessor', () => { + const args = createArgsWithLayers(); + const newArgs = { + ...args, + layers: [ + { + ...args.layers[0], + accessors: ['a'], + splitAccessor: 'd', + columnToLabel: '{"a": "Label A"}', + }, + ], + }; + + const component = getRenderedComponent(dataWithFormats, newArgs); + const nameFn = component.find(LineSeries).prop('name') as SeriesNameFn; + + convertSpy.mockReturnValueOnce('formatted'); + expect(nameFn({ ...nameFnArgs, seriesKeys: ['split1', 'a'] }, false)).toEqual('formatted'); + expect(getFormatSpy).toHaveBeenCalledWith({ id: 'custom' }); + }); + + test('split series without formatting with multiple y accessors', () => { + const args = createArgsWithLayers(); + const newArgs = { + ...args, + layers: [ + { + ...args.layers[0], + accessors: ['a', 'b'], + splitAccessor: 'd', + columnToLabel: '{"a": "Label A","b": "Label B"}', + }, + ], + }; + + const component = getRenderedComponent(dataWithoutFormats, newArgs); + const nameFn1 = component.find(LineSeries).at(0).prop('name') as SeriesNameFn; + const nameFn2 = component.find(LineSeries).at(0).prop('name') as SeriesNameFn; + + expect(nameFn1({ ...nameFnArgs, seriesKeys: ['split1', 'a'] }, false)).toEqual( + 'split1 - Label A' + ); + expect(nameFn2({ ...nameFnArgs, seriesKeys: ['split1', 'b'] }, false)).toEqual( + 'split1 - Label B' + ); + }); + + test('split series with formatting with multiple y accessors', () => { + const args = createArgsWithLayers(); + const newArgs = { + ...args, + layers: [ + { + ...args.layers[0], + accessors: ['a', 'b'], + splitAccessor: 'd', + columnToLabel: '{"a": "Label A","b": "Label B"}', + }, + ], + }; + + const component = getRenderedComponent(dataWithFormats, newArgs); + const nameFn1 = component.find(LineSeries).at(0).prop('name') as SeriesNameFn; + const nameFn2 = component.find(LineSeries).at(1).prop('name') as SeriesNameFn; + + convertSpy.mockReturnValueOnce('formatted1').mockReturnValueOnce('formatted2'); + expect(nameFn1({ ...nameFnArgs, seriesKeys: ['split1', 'a'] }, false)).toEqual( + 'formatted1 - Label A' + ); + expect(nameFn2({ ...nameFnArgs, seriesKeys: ['split1', 'b'] }, false)).toEqual( + 'formatted2 - Label B' + ); + }); + }); + + test('it set the scale of the x axis according to the args prop', () => { + const { data, args } = sampleArgs(); + + const component = shallow( + + ); + expect(component.find(LineSeries).at(0).prop('xScaleType')).toEqual(ScaleType.Ordinal); + expect(component.find(LineSeries).at(1).prop('xScaleType')).toEqual(ScaleType.Ordinal); + }); + + test('it set the scale of the y axis according to the args prop', () => { + const { data, args } = sampleArgs(); + + const component = shallow( + + ); + expect(component.find(LineSeries).at(0).prop('yScaleType')).toEqual(ScaleType.Sqrt); + expect(component.find(LineSeries).at(1).prop('yScaleType')).toEqual(ScaleType.Sqrt); + }); + + test('it gets the formatter for the x axis', () => { + const { data, args } = sampleArgs(); + + shallow(); + + expect(getFormatSpy).toHaveBeenCalledWith({ id: 'string' }); + }); + + test('it gets the formatter for the y axis if there is only one accessor', () => { + const { data, args } = sampleArgs(); + + shallow( + + ); + expect(getFormatSpy).toHaveBeenCalledWith({ + id: 'number', + params: { pattern: '0,0.000' }, + }); + }); + + test('it should pass the formatter function to the axis', () => { + const { data, args } = sampleArgs(); + + const instance = shallow(); + + const tickFormatter = instance.find(Axis).first().prop('tickFormat'); + + if (!tickFormatter) { + throw new Error('tickFormatter prop not found'); + } + + tickFormatter('I'); + + expect(convertSpy).toHaveBeenCalledWith('I'); + }); + + test('it should set the tickLabel visibility on the x axis if the tick labels is hidden', () => { + const { data, args } = sampleArgs(); + + args.tickLabelsVisibilitySettings = { + x: false, + yLeft: true, + yRight: true, + type: 'lens_xy_tickLabelsConfig', + }; + + const instance = shallow(); + + const axisStyle = instance.find(Axis).first().prop('style'); + + expect(axisStyle).toMatchObject({ + tickLabel: { + visible: false, + }, + }); + }); + + test('it should set the tickLabel visibility on the y axis if the tick labels is hidden', () => { + const { data, args } = sampleArgs(); + + args.tickLabelsVisibilitySettings = { + x: true, + yLeft: false, + yRight: false, + type: 'lens_xy_tickLabelsConfig', + }; + + const instance = shallow(); + + const axisStyle = instance.find(Axis).at(1).prop('style'); + + expect(axisStyle).toMatchObject({ + tickLabel: { + visible: false, + }, + }); + }); + + test('it should set the tickLabel visibility on the x axis if the tick labels is shown', () => { + const { data, args } = sampleArgs(); + + args.tickLabelsVisibilitySettings = { + x: true, + yLeft: true, + yRight: true, + type: 'lens_xy_tickLabelsConfig', + }; + + const instance = shallow(); + + const axisStyle = instance.find(Axis).first().prop('style'); + + expect(axisStyle).toMatchObject({ + tickLabel: { + visible: true, + }, + }); + }); + + test('it should set the tickLabel orientation on the x axis', () => { + const { data, args } = sampleArgs(); + + args.labelsOrientation = { + x: -45, + yLeft: 0, + yRight: -90, + type: 'lens_xy_labelsOrientationConfig', + }; + + const instance = shallow(); + + const axisStyle = instance.find(Axis).first().prop('style'); + + expect(axisStyle).toMatchObject({ + tickLabel: { + rotation: -45, + }, + }); + }); + + test('it should set the tickLabel visibility on the y axis if the tick labels is shown', () => { + const { data, args } = sampleArgs(); + + args.tickLabelsVisibilitySettings = { + x: false, + yLeft: true, + yRight: true, + type: 'lens_xy_tickLabelsConfig', + }; + + const instance = shallow(); + + const axisStyle = instance.find(Axis).at(1).prop('style'); + + expect(axisStyle).toMatchObject({ + tickLabel: { + visible: true, + }, + }); + }); + + test('it should set the tickLabel orientation on the y axis', () => { + const { data, args } = sampleArgs(); + + args.labelsOrientation = { + x: -45, + yLeft: -90, + yRight: -90, + type: 'lens_xy_labelsOrientationConfig', + }; + + const instance = shallow(); + + const axisStyle = instance.find(Axis).at(1).prop('style'); + + expect(axisStyle).toMatchObject({ + tickLabel: { + rotation: -90, + }, + }); + }); + + test('it should remove invalid rows', () => { + const data: LensMultiTable = { + type: 'lens_multitable', + tables: { + first: { + type: 'datatable', + columns: [ + { id: 'a', name: 'a', meta: { type: 'number' } }, + { id: 'b', name: 'b', meta: { type: 'number' } }, + { id: 'c', name: 'c', meta: { type: 'string' } }, + ], + rows: [ + { a: undefined, b: 2, c: 'I', d: 'Row 1' }, + { a: 1, b: 5, c: 'J', d: 'Row 2' }, + ], + }, + second: { + type: 'datatable', + columns: [ + { id: 'a', name: 'a', meta: { type: 'number' } }, + { id: 'b', name: 'b', meta: { type: 'number' } }, + { id: 'c', name: 'c', meta: { type: 'string' } }, + ], + rows: [ + { a: undefined, b: undefined, c: undefined }, + { a: undefined, b: undefined, c: undefined }, + ], + }, + }, + }; + + const args: XYArgs = { + xTitle: '', + yTitle: '', + yRightTitle: '', + legend: { type: 'lens_xy_legendConfig', isVisible: false, position: Position.Top }, + valueLabels: 'hide', + tickLabelsVisibilitySettings: { + type: 'lens_xy_tickLabelsConfig', + x: true, + yLeft: true, + yRight: true, + }, + gridlinesVisibilitySettings: { + type: 'lens_xy_gridlinesConfig', + x: true, + yLeft: false, + yRight: false, + }, + labelsOrientation: { + type: 'lens_xy_labelsOrientationConfig', + x: 0, + yLeft: 0, + yRight: 0, + }, + yLeftExtent: { + mode: 'full', + type: 'lens_xy_axisExtentConfig', + }, + yRightExtent: { + mode: 'full', + type: 'lens_xy_axisExtentConfig', + }, + layers: [ + { + layerId: 'first', + type: 'lens_xy_data_layer', + layerType: LayerTypes.DATA, + seriesType: 'line', + xAccessor: 'a', + accessors: ['c'], + splitAccessor: 'b', + columnToLabel: '', + xScaleType: 'ordinal', + yScaleType: 'linear', + isHistogram: false, + palette: mockPaletteOutput, + }, + { + layerId: 'second', + type: 'lens_xy_data_layer', + layerType: LayerTypes.DATA, + seriesType: 'line', + xAccessor: 'a', + accessors: ['c'], + splitAccessor: 'b', + columnToLabel: '', + xScaleType: 'ordinal', + yScaleType: 'linear', + isHistogram: false, + palette: mockPaletteOutput, + }, + ], + }; + + const component = shallow(); + + const series = component.find(LineSeries); + + // Only one series should be rendered, even though 2 are configured + // This one series should only have one row, even though 2 are sent + expect(series.prop('data')).toEqual([{ a: 1, b: 5, c: 'J', d: 'Row 2' }]); + }); + + test('it should not remove rows with falsy but non-undefined values', () => { + const data: LensMultiTable = { + type: 'lens_multitable', + tables: { + first: { + type: 'datatable', + columns: [ + { id: 'a', name: 'a', meta: { type: 'number' } }, + { id: 'b', name: 'b', meta: { type: 'number' } }, + { id: 'c', name: 'c', meta: { type: 'number' } }, + ], + rows: [ + { a: 0, b: 2, c: 5 }, + { a: 1, b: 0, c: 7 }, + ], + }, + }, + }; + + const args: XYArgs = { + xTitle: '', + yTitle: '', + yRightTitle: '', + legend: { type: 'lens_xy_legendConfig', isVisible: false, position: Position.Top }, + valueLabels: 'hide', + tickLabelsVisibilitySettings: { + type: 'lens_xy_tickLabelsConfig', + x: true, + yLeft: false, + yRight: false, + }, + gridlinesVisibilitySettings: { + type: 'lens_xy_gridlinesConfig', + x: true, + yLeft: false, + yRight: false, + }, + labelsOrientation: { + type: 'lens_xy_labelsOrientationConfig', + x: 0, + yLeft: 0, + yRight: 0, + }, + yLeftExtent: { + mode: 'full', + type: 'lens_xy_axisExtentConfig', + }, + yRightExtent: { + mode: 'full', + type: 'lens_xy_axisExtentConfig', + }, + layers: [ + { + layerId: 'first', + type: 'lens_xy_data_layer', + layerType: LayerTypes.DATA, + seriesType: 'line', + xAccessor: 'a', + accessors: ['c'], + splitAccessor: 'b', + columnToLabel: '', + xScaleType: 'ordinal', + yScaleType: 'linear', + isHistogram: false, + palette: mockPaletteOutput, + }, + ], + }; + + const component = shallow(); + + const series = component.find(LineSeries); + + expect(series.prop('data')).toEqual([ + { a: 0, b: 2, c: 5 }, + { a: 1, b: 0, c: 7 }, + ]); + }); + + test('it should show legend for split series, even with one row', () => { + const data: LensMultiTable = { + type: 'lens_multitable', + tables: { + first: { + type: 'datatable', + columns: [ + { id: 'a', name: 'a', meta: { type: 'number' } }, + { id: 'b', name: 'b', meta: { type: 'number' } }, + { id: 'c', name: 'c', meta: { type: 'string' } }, + ], + rows: [{ a: 1, b: 5, c: 'J' }], + }, + }, + }; + + const args: XYArgs = { + xTitle: '', + yTitle: '', + yRightTitle: '', + legend: { type: 'lens_xy_legendConfig', isVisible: true, position: Position.Top }, + valueLabels: 'hide', + tickLabelsVisibilitySettings: { + type: 'lens_xy_tickLabelsConfig', + x: true, + yLeft: false, + yRight: false, + }, + gridlinesVisibilitySettings: { + type: 'lens_xy_gridlinesConfig', + x: true, + yLeft: false, + yRight: false, + }, + labelsOrientation: { + type: 'lens_xy_labelsOrientationConfig', + x: 0, + yLeft: 0, + yRight: 0, + }, + yLeftExtent: { + mode: 'full', + type: 'lens_xy_axisExtentConfig', + }, + yRightExtent: { + mode: 'full', + type: 'lens_xy_axisExtentConfig', + }, + layers: [ + { + layerId: 'first', + type: 'lens_xy_data_layer', + layerType: LayerTypes.DATA, + seriesType: 'line', + xAccessor: 'a', + accessors: ['c'], + splitAccessor: 'b', + columnToLabel: '', + xScaleType: 'ordinal', + yScaleType: 'linear', + isHistogram: false, + palette: mockPaletteOutput, + }, + ], + }; + + const component = shallow(); + + expect(component.find(Settings).prop('showLegend')).toEqual(true); + }); + + test('it should always show legend if showSingleSeries is set', () => { + const { data, args } = sampleArgs(); + + const component = shallow( + + ); + + expect(component.find(Settings).prop('showLegend')).toEqual(true); + }); + + test('it should populate the correct legendPosition if isInside is set', () => { + const { data, args } = sampleArgs(); + + const component = shallow( + + ); + + expect(component.find(Settings).prop('legendPosition')).toEqual({ + vAlign: VerticalAlignment.Top, + hAlign: HorizontalAlignment.Right, + direction: LayoutDirection.Vertical, + floating: true, + floatingColumns: 1, + }); + }); + + test('it not show legend if isVisible is set to false', () => { + const { data, args } = sampleArgs(); + + const component = shallow( + + ); + + expect(component.find(Settings).prop('showLegend')).toEqual(false); + }); + + test('it should show legend on right side', () => { + const { data, args } = sampleArgs(); + + const component = shallow( + + ); + + expect(component.find(Settings).prop('legendPosition')).toEqual('top'); + }); + + test('it should apply the fitting function to all non-bar series', () => { + const data: LensMultiTable = { + type: 'lens_multitable', + tables: { + first: createSampleDatatableWithRows([ + { a: 1, b: 2, c: 'I', d: 'Foo' }, + { a: 1, b: 5, c: 'J', d: 'Bar' }, + ]), + }, + }; + + const args: XYArgs = createArgsWithLayers([ + { ...sampleLayer, accessors: ['a'] }, + { ...sampleLayer, seriesType: 'bar', accessors: ['a'] }, + { ...sampleLayer, seriesType: 'area', accessors: ['a'] }, + { ...sampleLayer, seriesType: 'area_stacked', accessors: ['a'] }, + ]); + + const component = shallow( + + ); + + expect(component.find(LineSeries).prop('fit')).toEqual({ type: Fit.Carry }); + expect(component.find(BarSeries).prop('fit')).toEqual(undefined); + expect(component.find(AreaSeries).at(0).prop('fit')).toEqual({ type: Fit.Carry }); + expect(component.find(AreaSeries).at(0).prop('stackAccessors')).toEqual([]); + expect(component.find(AreaSeries).at(1).prop('fit')).toEqual({ type: Fit.Carry }); + expect(component.find(AreaSeries).at(1).prop('stackAccessors')).toEqual(['c']); + }); + + test('it should apply None fitting function if not specified', () => { + const { data, args } = sampleArgs(); + + args.layers[0].accessors = ['a']; + + const component = shallow(); + + expect(component.find(LineSeries).prop('fit')).toEqual({ type: Fit.None }); + }); + + test('it should apply the xTitle if is specified', () => { + const { data, args } = sampleArgs(); + + args.xTitle = 'My custom x-axis title'; + + const component = shallow(); + + expect(component.find(Axis).at(0).prop('title')).toEqual('My custom x-axis title'); + }); + + test('it should hide the X axis title if the corresponding switch is off', () => { + const { data, args } = sampleArgs(); + + args.axisTitlesVisibilitySettings = { + x: false, + yLeft: true, + yRight: true, + type: 'lens_xy_axisTitlesVisibilityConfig', + }; + + const component = shallow(); + + const axisStyle = component.find(Axis).first().prop('style'); + + expect(axisStyle).toMatchObject({ + axisTitle: { + visible: false, + }, + }); + }); + + test('it should show the X axis gridlines if the setting is on', () => { + const { data, args } = sampleArgs(); + + args.gridlinesVisibilitySettings = { + x: true, + yLeft: false, + yRight: false, + type: 'lens_xy_gridlinesConfig', + }; + + const component = shallow(); + + expect(component.find(Axis).at(0).prop('gridLine')).toMatchObject({ + visible: true, + }); + }); + + test('it should format the boolean values correctly', () => { + const data: LensMultiTable = { + type: 'lens_multitable', + tables: { + first: { + type: 'datatable', + columns: [ + { + id: 'a', + name: 'a', + meta: { type: 'number', params: { id: 'number', params: { pattern: '0,0.000' } } }, + }, + { + id: 'b', + name: 'b', + meta: { type: 'number', params: { id: 'number', params: { pattern: '000,0' } } }, + }, + { + id: 'c', + name: 'c', + meta: { + type: 'boolean', + params: { id: 'boolean' }, + }, + }, + ], + rows: [ + { a: 5, b: 2, c: 0 }, + { a: 19, b: 5, c: 1 }, + ], + }, + }, + dateRange: { + fromDate: new Date('2019-01-02T05:00:00.000Z'), + toDate: new Date('2019-01-03T05:00:00.000Z'), + }, + }; + const timeSampleLayer: DataLayerConfigResult = { + layerId: 'first', + type: 'lens_xy_data_layer', + layerType: LayerTypes.DATA, + seriesType: 'line', + xAccessor: 'c', + accessors: ['a', 'b'], + xScaleType: 'ordinal', + yScaleType: 'linear', + isHistogram: false, + palette: mockPaletteOutput, + }; + const args = createArgsWithLayers([timeSampleLayer]); + + const getCustomFormatSpy = jest.fn(); + getCustomFormatSpy.mockReturnValue({ convert: jest.fn((x) => Boolean(x)) }); + + const component = shallow( + + ); + + expect(component.find(LineSeries).at(1).prop('data')).toEqual([ + { + a: 5, + b: 2, + c: false, + }, + { + a: 19, + b: 5, + c: true, + }, + ]); + }); +}); diff --git a/x-pack/plugins/lens/public/xy_visualization/expression.tsx b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx similarity index 82% rename from x-pack/plugins/lens/public/xy_visualization/expression.tsx rename to src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx index 5bed5d35bc302..06e333ddf1d08 100644 --- a/x-pack/plugins/lens/public/xy_visualization/expression.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx @@ -1,14 +1,14 @@ /* * 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. + * 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 './expression.scss'; import React, { useRef } from 'react'; -import ReactDOM from 'react-dom'; import { Chart, Settings, @@ -37,47 +37,42 @@ import { BarSeriesProps, LineSeriesProps, } from '@elastic/charts'; -import { I18nProvider } from '@kbn/i18n-react'; -import type { - ExpressionRenderDefinition, - Datatable, - DatatableRow, - DatatableColumn, -} from 'src/plugins/expressions/public'; +import type { Datatable, DatatableRow, DatatableColumn } from 'src/plugins/expressions/public'; import { IconType } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { RenderMode } from 'src/plugins/expressions'; -import { ThemeServiceStart } from 'kibana/public'; import { FieldFormat } from 'src/plugins/field_formats/common'; -import { EmptyPlaceholder } from '../../../../../src/plugins/charts/public'; -import { KibanaThemeProvider } from '../../../../../src/plugins/kibana_react/public'; -import type { ILensInterpreterRenderHandlers, LensFilterEvent, LensBrushEvent } from '../types'; -import type { LensMultiTable, FormatFactory } from '../../common'; -import type { DataLayerArgs, SeriesType, XYChartProps } from '../../common/expressions'; -import { visualizationTypes } from './types'; -import { VisualizationContainer } from '../visualization_container'; -import { isHorizontalChart, getSeriesColor } from './state_helpers'; -import { search } from '../../../../../src/plugins/data/public'; +import { EmptyPlaceholder } from '../../../../../plugins/charts/public'; +import type { FilterEvent, BrushEvent, FormatFactory } from '../types'; +import type { DataLayerConfigResult, SeriesType, XYChartProps, XYLayerConfig } from '../../common'; +import { isHorizontalChart, getSeriesColor } from '../helpers'; import { ChartsPluginSetup, ChartsPluginStart, PaletteRegistry, SeriesLayer, useActiveCursor, -} from '../../../../../src/plugins/charts/public'; -import { MULTILAYER_TIME_AXIS_STYLE } from '../../../../../src/plugins/charts/common'; -import { getFitOptions } from './fitting_functions'; -import { getAxesConfiguration, GroupsConfiguration, validateExtent } from './axes_configuration'; -import { getColorAssignments } from './color_assignment'; +} from '../../../../../plugins/charts/public'; +import { MULTILAYER_TIME_AXIS_STYLE } from '../../../../../plugins/charts/common'; +import { + getFilteredLayers, + getReferenceLayers, + isDataLayer, + getFitOptions, + getAxesConfiguration, + GroupsConfiguration, + validateExtent, + computeOverallDataDomain, + getColorAssignments, +} from '../helpers'; import { getXDomain, XyEndzones } from './x_domain'; -import { getLegendAction } from './get_legend_action'; +import { getLegendAction } from './legend_action'; import { computeChartMargins, getReferenceLineRequiredPaddings, ReferenceLineAnnotations, -} from './expression_reference_lines'; -import { computeOverallDataDomain } from './reference_line_helpers'; -import { getReferenceLayers, isDataLayer } from './visualization_helpers'; +} from './reference_lines'; +import { visualizationDefinitions } from '../definitions'; declare global { interface Window { @@ -99,93 +94,14 @@ export type XYChartRenderProps = XYChartProps & { useLegacyTimeAxis: boolean; minInterval: number | undefined; interactive?: boolean; - onClickValue: (data: LensFilterEvent['data']) => void; - onSelectRange: (data: LensBrushEvent['data']) => void; + onClickValue: (data: FilterEvent['data']) => void; + onSelectRange: (data: BrushEvent['data']) => void; renderMode: RenderMode; syncColors: boolean; }; -export function calculateMinInterval({ args: { layers }, data }: XYChartProps) { - const filteredLayers = getFilteredLayers(layers, data); - if (filteredLayers.length === 0) return; - const isTimeViz = filteredLayers.every((l) => l.xScaleType === 'time'); - const xColumn = data.tables[filteredLayers[0].layerId].columns.find( - (column) => column.id === filteredLayers[0].xAccessor - ); - - if (!xColumn) return; - if (!isTimeViz) { - const histogramInterval = search.aggs.getNumberHistogramIntervalByDatatableColumn(xColumn); - if (typeof histogramInterval === 'number') { - return histogramInterval; - } else { - return undefined; - } - } - const dateInterval = search.aggs.getDateHistogramMetaDataByDatatableColumn(xColumn)?.interval; - if (!dateInterval) return; - const intervalDuration = search.aggs.parseInterval(dateInterval); - if (!intervalDuration) return; - return intervalDuration.as('milliseconds'); -} - const isPrimitive = (value: unknown): boolean => value != null && typeof value !== 'object'; -export const getXyChartRenderer = (dependencies: { - formatFactory: FormatFactory; - chartsThemeService: ChartsPluginStart['theme']; - chartsActiveCursorService: ChartsPluginStart['activeCursor']; - paletteService: PaletteRegistry; - timeZone: string; - useLegacyTimeAxis: boolean; - kibanaTheme: ThemeServiceStart; -}): ExpressionRenderDefinition => ({ - name: 'lens_xy_chart_renderer', - displayName: 'XY chart', - help: i18n.translate('xpack.lens.xyChart.renderer.help', { - defaultMessage: 'X/Y chart renderer', - }), - validate: () => undefined, - reuseDomNode: true, - render: async ( - domNode: Element, - config: XYChartProps, - handlers: ILensInterpreterRenderHandlers - ) => { - handlers.onDestroy(() => ReactDOM.unmountComponentAtNode(domNode)); - const onClickValue = (data: LensFilterEvent['data']) => { - handlers.event({ name: 'filter', data }); - }; - const onSelectRange = (data: LensBrushEvent['data']) => { - handlers.event({ name: 'brush', data }); - }; - - ReactDOM.render( - - - - - , - domNode, - () => handlers.done() - ); - }, -}); - function getValueLabelsStyling(isHorizontal: boolean): { displayValue: RecursivePartial; } { @@ -208,17 +124,13 @@ function getValueLabelsStyling(isHorizontal: boolean): { } function getIconForSeriesType(seriesType: SeriesType): IconType { - return visualizationTypes.find((c) => c.id === seriesType)!.icon || 'empty'; + return visualizationDefinitions.find((c) => c.id === seriesType)!.icon || 'empty'; } const MemoizedChart = React.memo(XYChart); export function XYChartReportable(props: XYChartRenderProps) { - return ( - - - - ); + return ; } export function XYChart({ @@ -252,23 +164,25 @@ export function XYChart({ const chartBaseTheme = chartsThemeService.useChartsBaseTheme(); const darkMode = chartsThemeService.useDarkMode(); const filteredLayers = getFilteredLayers(layers, data); - const layersById = filteredLayers.reduce((memo, layer) => { + const layersById = filteredLayers.reduce>((memo, layer) => { memo[layer.layerId] = layer; return memo; - }, {} as Record); + }, {}); const handleCursorUpdate = useActiveCursor(chartsActiveCursorService, chartRef, { datatables: Object.values(data.tables), }); if (filteredLayers.length === 0) { - const icon: IconType = layers.length > 0 ? getIconForSeriesType(layers[0].seriesType) : 'bar'; + const dataLayers: DataLayerConfigResult[] = layers.filter(isDataLayer); + const icon: IconType = + dataLayers.length > 0 ? getIconForSeriesType(dataLayers[0].seriesType) : 'bar'; return ; } // use formatting hint of first x axis column to format ticks const xAxisColumn = data.tables[filteredLayers[0].layerId].columns.find( - ({ id }) => id === filteredLayers[0].xAccessor + ({ id }) => isDataLayer(filteredLayers[0]) && id === filteredLayers[0].xAccessor ); const xAxisFormatter = formatFactory(xAxisColumn && xAxisColumn.meta?.params); const layersAlreadyFormatted: Record = {}; @@ -282,7 +196,7 @@ export function XYChart({ const chartHasMoreThanOneSeries = filteredLayers.length > 1 || filteredLayers.some((layer) => layer.accessors.length > 1) || - filteredLayers.some((layer) => layer.splitAccessor); + filteredLayers.some((layer) => isDataLayer(layer) && layer.splitAccessor); const shouldRotate = isHorizontalChart(filteredLayers); const yAxesConfiguration = getAxesConfiguration( @@ -393,8 +307,12 @@ export function XYChart({ const extent = axis.groupId === 'left' ? yLeftExtent : yRightExtent; const hasBarOrArea = Boolean( axis.series.some((series) => { - const seriesType = layersById[series.layer]?.seriesType; - return seriesType?.includes('bar') || seriesType?.includes('area'); + const layer = layersById[series.layer]; + if (!(layer && isDataLayer(layer))) { + return false; + } + + return layer.seriesType.includes('bar') || layer.seriesType.includes('area'); }) ); const fit = !hasBarOrArea && extent.mode === 'dataBounds'; @@ -514,7 +432,7 @@ export function XYChart({ value: pointValue, }); } - const context: LensFilterEvent['data'] = { + const context: FilterEvent['data'] = { data: points.map((point) => ({ row: point.row, column: point.column, @@ -538,7 +456,7 @@ export function XYChart({ const xAxisColumnIndex = table.columns.findIndex((el) => el.id === filteredLayers[0].xAccessor); - const context: LensBrushEvent['data'] = { + const context: BrushEvent['data'] = { range: [min, max], table, column: xAxisColumnIndex, @@ -985,26 +903,6 @@ export function XYChart({ ); } -function getFilteredLayers(layers: DataLayerArgs[], data: LensMultiTable) { - return layers.filter((layer) => { - const { layerId, xAccessor, accessors, splitAccessor } = layer; - return ( - isDataLayer(layer) && - !( - !accessors.length || - !data.tables[layerId] || - data.tables[layerId].rows.length === 0 || - (xAccessor && - data.tables[layerId].rows.every((row) => typeof row[xAccessor] === 'undefined')) || - // stacked percentage bars have no xAccessors but splitAccessor with undefined values in them when empty - (!xAccessor && - splitAccessor && - data.tables[layerId].rows.every((row) => typeof row[splitAccessor] === 'undefined')) - ) - ); - }); -} - function assertNever(x: never): never { throw new Error('Unexpected series type: ' + x); } diff --git a/src/plugins/chart_expressions/expression_xy/public/definitions/fitting_functions.ts b/src/plugins/chart_expressions/expression_xy/public/definitions/fitting_functions.ts new file mode 100644 index 0000000000000..c9b88c97d60a2 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/public/definitions/fitting_functions.ts @@ -0,0 +1,10 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { FittingFunctions } from '../../common/constants'; diff --git a/src/plugins/chart_expressions/expression_xy/public/definitions/index.ts b/src/plugins/chart_expressions/expression_xy/public/definitions/index.ts new file mode 100644 index 0000000000000..4d9c039855cad --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/public/definitions/index.ts @@ -0,0 +1,10 @@ +/* + * 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 { visualizationDefinitions } from './visualizations'; +export { fittingFunctionDefinitions } from './fitting_functions'; diff --git a/src/plugins/chart_expressions/expression_xy/public/definitions/visualizations.ts b/src/plugins/chart_expressions/expression_xy/public/definitions/visualizations.ts new file mode 100644 index 0000000000000..f958469b3b1d7 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/public/definitions/visualizations.ts @@ -0,0 +1,128 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { SeriesTypes } from '../../common/constants'; +import { + BarIcon, + LineIcon, + AreaIcon, + BarStackedIcon, + AreaStackedIcon, + BarHorizontalIcon, + BarPercentageIcon, + AreaPercentageIcon, + BarHorizontalStackedIcon, + BarHorizontalPercentageIcon, +} from '../icons'; +import { VisualizationType } from '../types'; + +const groupLabelForBar = i18n.translate('xpack.lens.xyVisualization.barGroupLabel', { + defaultMessage: 'Bar', +}); + +const groupLabelForLineAndArea = i18n.translate('xpack.lens.xyVisualization.lineGroupLabel', { + defaultMessage: 'Line and area', +}); + +export const visualizationDefinitions: VisualizationType[] = [ + { + id: SeriesTypes.BAR, + icon: BarIcon, + label: i18n.translate('xpack.lens.xyVisualization.barLabel', { + defaultMessage: 'Bar vertical', + }), + groupLabel: groupLabelForBar, + sortPriority: 4, + }, + { + id: SeriesTypes.BAR_HORIZONTAL, + icon: BarHorizontalIcon, + label: i18n.translate('xpack.lens.xyVisualization.barHorizontalLabel', { + defaultMessage: 'H. Bar', + }), + fullLabel: i18n.translate('xpack.lens.xyVisualization.barHorizontalFullLabel', { + defaultMessage: 'Bar horizontal', + }), + groupLabel: groupLabelForBar, + }, + { + id: SeriesTypes.BAR_STACKED, + icon: BarStackedIcon, + label: i18n.translate('xpack.lens.xyVisualization.stackedBarLabel', { + defaultMessage: 'Bar vertical stacked', + }), + groupLabel: groupLabelForBar, + }, + { + id: SeriesTypes.BAR_PERCENTAGE_STACKED, + icon: BarPercentageIcon, + label: i18n.translate('xpack.lens.xyVisualization.stackedPercentageBarLabel', { + defaultMessage: 'Bar vertical percentage', + }), + groupLabel: groupLabelForBar, + }, + { + id: SeriesTypes.BAR_HORIZONTAL_STACKED, + icon: BarHorizontalStackedIcon, + label: i18n.translate('xpack.lens.xyVisualization.stackedBarHorizontalLabel', { + defaultMessage: 'H. Stacked bar', + }), + fullLabel: i18n.translate('xpack.lens.xyVisualization.stackedBarHorizontalFullLabel', { + defaultMessage: 'Bar horizontal stacked', + }), + groupLabel: groupLabelForBar, + }, + { + id: SeriesTypes.BAR_HORIZONTAL_PERCENTAGE_STACKED, + icon: BarHorizontalPercentageIcon, + label: i18n.translate('xpack.lens.xyVisualization.stackedPercentageBarHorizontalLabel', { + defaultMessage: 'H. Percentage bar', + }), + fullLabel: i18n.translate( + 'xpack.lens.xyVisualization.stackedPercentageBarHorizontalFullLabel', + { + defaultMessage: 'Bar horizontal percentage', + } + ), + groupLabel: groupLabelForBar, + }, + { + id: SeriesTypes.AREA, + icon: AreaIcon, + label: i18n.translate('xpack.lens.xyVisualization.areaLabel', { + defaultMessage: 'Area', + }), + groupLabel: groupLabelForLineAndArea, + }, + { + id: SeriesTypes.AREA_STACKED, + icon: AreaStackedIcon, + label: i18n.translate('xpack.lens.xyVisualization.stackedAreaLabel', { + defaultMessage: 'Area stacked', + }), + groupLabel: groupLabelForLineAndArea, + }, + { + id: SeriesTypes.AREA_PERCENTAGE_STACKED, + icon: AreaPercentageIcon, + label: i18n.translate('xpack.lens.xyVisualization.stackedPercentageAreaLabel', { + defaultMessage: 'Area percentage', + }), + groupLabel: groupLabelForLineAndArea, + }, + { + id: SeriesTypes.LINE, + icon: LineIcon, + label: i18n.translate('xpack.lens.xyVisualization.lineLabel', { + defaultMessage: 'Line', + }), + groupLabel: groupLabelForLineAndArea, + sortPriority: 2, + }, +]; diff --git a/src/plugins/chart_expressions/expression_xy/public/expression_renderers/index.ts b/src/plugins/chart_expressions/expression_xy/public/expression_renderers/index.ts new file mode 100644 index 0000000000000..863dbb960512d --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/public/expression_renderers/index.ts @@ -0,0 +1,10 @@ +/* + * 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 { getXyChartRenderer } from './xy_chart_renderer'; +export type { GetStartDepsFn } from './xy_chart_renderer'; diff --git a/src/plugins/chart_expressions/expression_xy/public/expression_renderers/xy_chart_renderer.tsx b/src/plugins/chart_expressions/expression_xy/public/expression_renderers/xy_chart_renderer.tsx new file mode 100644 index 0000000000000..40e4e6c706bcc --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/public/expression_renderers/xy_chart_renderer.tsx @@ -0,0 +1,80 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { I18nProvider } from '@kbn/i18n-react'; +import { ThemeServiceStart } from 'kibana/public'; +import React from 'react'; +import ReactDOM from 'react-dom'; +import { ChartsPluginStart, PaletteRegistry } from 'src/plugins/charts/public'; +import { ExpressionRenderDefinition } from 'src/plugins/expressions'; +import { FormatFactory } from 'src/plugins/field_formats/common'; +import { KibanaThemeProvider } from 'src/plugins/kibana_react/public'; +import { XYChartProps } from '../../common'; +import { XYChartReportable } from '../components'; +import { calculateMinInterval } from '../helpers'; +import { BrushEvent, FilterEvent } from '../types'; + +export type GetStartDepsFn = () => Promise<{ + formatFactory: FormatFactory; + theme: ChartsPluginStart['theme']; + activeCursor: ChartsPluginStart['activeCursor']; + paletteService: PaletteRegistry; + timeZone: string; + useLegacyTimeAxis: boolean; + kibanaTheme: ThemeServiceStart; +}>; + +interface XyChartRendererDeps { + getStartDeps: GetStartDepsFn; +} + +export const getXyChartRenderer = ({ + getStartDeps, +}: XyChartRendererDeps): ExpressionRenderDefinition => ({ + name: 'lens_xy_chart_renderer', + displayName: 'XY chart', + help: i18n.translate('xpack.lens.xyChart.renderer.help', { + defaultMessage: 'X/Y chart renderer', + }), + validate: () => undefined, + reuseDomNode: true, + render: async (domNode: Element, config: XYChartProps, handlers) => { + handlers.onDestroy(() => ReactDOM.unmountComponentAtNode(domNode)); + const onClickValue = (data: FilterEvent['data']) => { + handlers.event({ name: 'filter', data }); + }; + const onSelectRange = (data: BrushEvent['data']) => { + handlers.event({ name: 'brush', data }); + }; + const deps = await getStartDeps(); + ReactDOM.render( + + + + + , + domNode, + () => handlers.done() + ); + }, +}); diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/axes_configuration.test.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/axes_configuration.test.ts new file mode 100644 index 0000000000000..5b492d2db0f2f --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/axes_configuration.test.ts @@ -0,0 +1,334 @@ +/* + * 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 { DataLayerConfigResult } from '../../common'; +import { LayerTypes } from '../../common/constants'; +import { Datatable } from '../../../../../plugins/expressions/public'; +import { getAxesConfiguration } from './axes_configuration'; + +describe('axes_configuration', () => { + const tables: Record = { + first: { + type: 'datatable', + rows: [ + { + xAccessorId: 1585758120000, + splitAccessorId: "Men's Clothing", + yAccessorId: 1, + yAccessorId2: 1, + yAccessorId3: 1, + yAccessorId4: 4, + }, + { + xAccessorId: 1585758360000, + splitAccessorId: "Women's Accessories", + yAccessorId: 1, + yAccessorId2: 1, + yAccessorId3: 1, + yAccessorId4: 4, + }, + { + xAccessorId: 1585758360000, + splitAccessorId: "Women's Clothing", + yAccessorId: 1, + yAccessorId2: 1, + yAccessorId3: 1, + yAccessorId4: 4, + }, + { + xAccessorId: 1585759380000, + splitAccessorId: "Men's Clothing", + yAccessorId: 1, + yAccessorId2: 1, + yAccessorId3: 1, + yAccessorId4: 4, + }, + { + xAccessorId: 1585759380000, + splitAccessorId: "Men's Shoes", + yAccessorId: 1, + yAccessorId2: 1, + yAccessorId3: 1, + yAccessorId4: 4, + }, + { + xAccessorId: 1585759380000, + splitAccessorId: "Women's Clothing", + yAccessorId: 1, + yAccessorId2: 1, + yAccessorId3: 1, + yAccessorId4: 4, + }, + { + xAccessorId: 1585760700000, + splitAccessorId: "Men's Clothing", + yAccessorId: 1, + yAccessorId2: 1, + yAccessorId3: 1, + yAccessorId4: 4, + }, + { + xAccessorId: 1585760760000, + splitAccessorId: "Men's Clothing", + yAccessorId: 1, + yAccessorId2: 1, + yAccessorId3: 1, + yAccessorId4: 4, + }, + { + xAccessorId: 1585760760000, + splitAccessorId: "Men's Shoes", + yAccessorId: 1, + yAccessorId2: 1, + yAccessorId3: 1, + yAccessorId4: 4, + }, + { + xAccessorId: 1585761120000, + splitAccessorId: "Men's Shoes", + yAccessorId: 1, + yAccessorId2: 1, + yAccessorId3: 1, + yAccessorId4: 4, + }, + ], + columns: [ + { + id: 'xAccessorId', + name: 'order_date per minute', + meta: { + type: 'date', + field: 'order_date', + source: 'esaggs', + index: 'indexPatternId', + sourceParams: { + indexPatternId: 'indexPatternId', + type: 'date_histogram', + params: { + field: 'order_date', + timeRange: { from: '2020-04-01T16:14:16.246Z', to: '2020-04-01T17:15:41.263Z' }, + useNormalizedEsInterval: true, + scaleMetricValues: false, + interval: '1m', + drop_partials: false, + min_doc_count: 0, + extended_bounds: {}, + }, + }, + params: { params: { id: 'date', params: { pattern: 'HH:mm' } } }, + }, + }, + { + id: 'splitAccessorId', + name: 'Top values of category.keyword', + meta: { + type: 'string', + field: 'category.keyword', + source: 'esaggs', + index: 'indexPatternId', + sourceParams: { + indexPatternId: 'indexPatternId', + type: 'terms', + params: { + field: 'category.keyword', + orderBy: 'yAccessorId', + order: 'desc', + size: 3, + otherBucket: false, + otherBucketLabel: 'Other', + missingBucket: false, + missingBucketLabel: 'Missing', + }, + }, + params: { + id: 'terms', + params: { + id: 'string', + otherBucketLabel: 'Other', + missingBucketLabel: 'Missing', + parsedUrl: { + origin: 'http://localhost:5601', + pathname: '/jiy/app/kibana', + basePath: '/jiy', + }, + }, + }, + }, + }, + { + id: 'yAccessorId', + name: 'Count of records', + meta: { + type: 'number', + source: 'esaggs', + index: 'indexPatternId', + sourceParams: { + indexPatternId: 'indexPatternId', + type: 'count', + }, + params: { id: 'number' }, + }, + }, + { + id: 'yAccessorId2', + name: 'Other column', + meta: { + type: 'number', + source: 'esaggs', + index: 'indexPatternId', + sourceParams: { + indexPatternId: 'indexPatternId', + type: 'average', + }, + params: { id: 'bytes' }, + }, + }, + { + id: 'yAccessorId3', + name: 'Other column', + meta: { + type: 'number', + source: 'esaggs', + index: 'indexPatternId', + sourceParams: { + indexPatternId: 'indexPatternId', + type: 'average', + }, + params: { id: 'currency' }, + }, + }, + { + id: 'yAccessorId4', + name: 'Other column', + meta: { + type: 'number', + source: 'esaggs', + index: 'indexPatternId', + sourceParams: { + indexPatternId: 'indexPatternId', + type: 'average', + }, + params: { id: 'currency' }, + }, + }, + ], + }, + }; + + const sampleLayer: DataLayerConfigResult = { + type: 'lens_xy_data_layer', + layerId: 'first', + layerType: LayerTypes.DATA, + seriesType: 'line', + xAccessor: 'c', + accessors: ['yAccessorId'], + splitAccessor: 'd', + columnToLabel: '{"a": "Label A", "b": "Label B", "d": "Label D"}', + xScaleType: 'ordinal', + yScaleType: 'linear', + isHistogram: false, + palette: { type: 'palette', name: 'default' }, + }; + + it('should map auto series to left axis', () => { + const formatFactory = jest.fn(); + const groups = getAxesConfiguration([sampleLayer], false, tables, formatFactory); + expect(groups.length).toEqual(1); + expect(groups[0].position).toEqual('left'); + expect(groups[0].series[0].accessor).toEqual('yAccessorId'); + expect(groups[0].series[0].layer).toEqual('first'); + }); + + it('should map auto series to right axis if formatters do not match', () => { + const formatFactory = jest.fn(); + const twoSeriesLayer = { ...sampleLayer, accessors: ['yAccessorId', 'yAccessorId2'] }; + const groups = getAxesConfiguration([twoSeriesLayer], false, tables, formatFactory); + expect(groups.length).toEqual(2); + expect(groups[0].position).toEqual('left'); + expect(groups[1].position).toEqual('right'); + expect(groups[0].series[0].accessor).toEqual('yAccessorId'); + expect(groups[1].series[0].accessor).toEqual('yAccessorId2'); + }); + + it('should map auto series to left if left and right are already filled with non-matching series', () => { + const formatFactory = jest.fn(); + const threeSeriesLayer = { + ...sampleLayer, + accessors: ['yAccessorId', 'yAccessorId2', 'yAccessorId3'], + }; + const groups = getAxesConfiguration([threeSeriesLayer], false, tables, formatFactory); + expect(groups.length).toEqual(2); + expect(groups[0].position).toEqual('left'); + expect(groups[1].position).toEqual('right'); + expect(groups[0].series[0].accessor).toEqual('yAccessorId'); + expect(groups[0].series[1].accessor).toEqual('yAccessorId3'); + expect(groups[1].series[0].accessor).toEqual('yAccessorId2'); + }); + + it('should map right series to right axis', () => { + const formatFactory = jest.fn(); + const groups = getAxesConfiguration( + [ + { + ...sampleLayer, + yConfig: [{ type: 'lens_xy_yConfig', forAccessor: 'yAccessorId', axisMode: 'right' }], + }, + ], + false, + tables, + formatFactory + ); + expect(groups.length).toEqual(1); + expect(groups[0].position).toEqual('right'); + expect(groups[0].series[0].accessor).toEqual('yAccessorId'); + expect(groups[0].series[0].layer).toEqual('first'); + }); + + it('should map series with matching formatters to same axis', () => { + const formatFactory = jest.fn(); + const groups = getAxesConfiguration( + [ + { + ...sampleLayer, + accessors: ['yAccessorId', 'yAccessorId3', 'yAccessorId4'], + yConfig: [{ type: 'lens_xy_yConfig', forAccessor: 'yAccessorId', axisMode: 'right' }], + }, + ], + false, + tables, + formatFactory + ); + expect(groups.length).toEqual(2); + expect(groups[0].position).toEqual('left'); + expect(groups[0].series[0].accessor).toEqual('yAccessorId3'); + expect(groups[0].series[1].accessor).toEqual('yAccessorId4'); + expect(groups[1].position).toEqual('right'); + expect(groups[1].series[0].accessor).toEqual('yAccessorId'); + expect(formatFactory).toHaveBeenCalledWith({ id: 'number' }); + expect(formatFactory).toHaveBeenCalledWith({ id: 'currency' }); + }); + + it('should create one formatter per series group', () => { + const formatFactory = jest.fn(); + getAxesConfiguration( + [ + { + ...sampleLayer, + accessors: ['yAccessorId', 'yAccessorId3', 'yAccessorId4'], + yConfig: [{ type: 'lens_xy_yConfig', forAccessor: 'yAccessorId', axisMode: 'right' }], + }, + ], + false, + tables, + formatFactory + ); + expect(formatFactory).toHaveBeenCalledTimes(2); + expect(formatFactory).toHaveBeenCalledWith({ id: 'number' }); + expect(formatFactory).toHaveBeenCalledWith({ id: 'currency' }); + }); +}); diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/axes_configuration.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/axes_configuration.ts new file mode 100644 index 0000000000000..16529ecd40e90 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/axes_configuration.ts @@ -0,0 +1,146 @@ +/* + * 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 { FormatFactory } from '../types'; +import { AxisExtentConfig, DataLayerConfigResult } from '../../common'; +import { Datatable } from '../../../../../plugins/expressions/public'; +import type { + IFieldFormat, + SerializedFieldFormat, +} from '../../../../../plugins/field_formats/common'; + +interface FormattedMetric { + layer: string; + accessor: string; + fieldFormat: SerializedFieldFormat; +} + +export type GroupsConfiguration = Array<{ + groupId: string; + position: 'left' | 'right' | 'bottom' | 'top'; + formatter?: IFieldFormat; + series: Array<{ layer: string; accessor: string }>; +}>; + +export function isFormatterCompatible( + formatter1: SerializedFieldFormat, + formatter2: SerializedFieldFormat +) { + return formatter1.id === formatter2.id; +} + +export function groupAxesByType( + layers: DataLayerConfigResult[], + tables?: Record +) { + const series: { + auto: FormattedMetric[]; + left: FormattedMetric[]; + right: FormattedMetric[]; + bottom: FormattedMetric[]; + } = { + auto: [], + left: [], + right: [], + bottom: [], + }; + + layers?.forEach((layer) => { + const table = tables?.[layer.layerId]; + layer.accessors.forEach((accessor) => { + const mode = + layer.yConfig?.find((yAxisConfig) => yAxisConfig.forAccessor === accessor)?.axisMode || + 'auto'; + let formatter: SerializedFieldFormat = table?.columns.find((column) => column.id === accessor) + ?.meta?.params || { id: 'number' }; + if (layer.seriesType.includes('percentage') && formatter.id !== 'percent') { + formatter = { + id: 'percent', + params: { + pattern: '0.[00]%', + }, + }; + } + series[mode].push({ + layer: layer.layerId, + accessor, + fieldFormat: formatter, + }); + }); + }); + + series.auto.forEach((currentSeries) => { + if ( + series.left.length === 0 || + (tables && + series.left.every((leftSeries) => + isFormatterCompatible(leftSeries.fieldFormat, currentSeries.fieldFormat) + )) + ) { + series.left.push(currentSeries); + } else if ( + series.right.length === 0 || + (tables && + series.left.every((leftSeries) => + isFormatterCompatible(leftSeries.fieldFormat, currentSeries.fieldFormat) + )) + ) { + series.right.push(currentSeries); + } else if (series.right.length >= series.left.length) { + series.left.push(currentSeries); + } else { + series.right.push(currentSeries); + } + }); + return series; +} + +export function getAxesConfiguration( + layers: DataLayerConfigResult[], + shouldRotate: boolean, + tables?: Record, + formatFactory?: FormatFactory +): GroupsConfiguration { + const series = groupAxesByType(layers, tables); + + const axisGroups: GroupsConfiguration = []; + + if (series.left.length > 0) { + axisGroups.push({ + groupId: 'left', + position: shouldRotate ? 'bottom' : 'left', + formatter: formatFactory?.(series.left[0].fieldFormat), + series: series.left.map(({ fieldFormat, ...currentSeries }) => currentSeries), + }); + } + + if (series.right.length > 0) { + axisGroups.push({ + groupId: 'right', + position: shouldRotate ? 'top' : 'right', + formatter: formatFactory?.(series.right[0].fieldFormat), + series: series.right.map(({ fieldFormat, ...currentSeries }) => currentSeries), + }); + } + + return axisGroups; +} + +export function validateExtent(hasBarOrArea: boolean, extent?: AxisExtentConfig) { + const inclusiveZeroError = + extent && + hasBarOrArea && + ((extent.lowerBound !== undefined && extent.lowerBound > 0) || + (extent.upperBound !== undefined && extent.upperBound) < 0); + const boundaryError = + extent && + extent.lowerBound !== undefined && + extent.upperBound !== undefined && + extent.upperBound <= extent.lowerBound; + return { inclusiveZeroError, boundaryError }; +} diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.test.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.test.ts new file mode 100644 index 0000000000000..df763f5d5dd66 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.test.ts @@ -0,0 +1,239 @@ +/* + * 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 { getColorAssignments } from './color_assignment'; +import type { DataLayerConfigResult, LensMultiTable } from '../../common'; +import type { FormatFactory } from '../types'; +import { LayerTypes } from '../../common/constants'; + +describe('color_assignment', () => { + const layers: DataLayerConfigResult[] = [ + { + type: 'lens_xy_data_layer', + yScaleType: 'linear', + xScaleType: 'linear', + isHistogram: true, + seriesType: 'bar', + palette: { type: 'palette', name: 'palette1' }, + layerId: '1', + layerType: LayerTypes.DATA, + splitAccessor: 'split1', + accessors: ['y1', 'y2'], + }, + { + type: 'lens_xy_data_layer', + yScaleType: 'linear', + xScaleType: 'linear', + isHistogram: true, + seriesType: 'bar', + palette: { type: 'palette', name: 'palette2' }, + layerId: '2', + layerType: LayerTypes.DATA, + splitAccessor: 'split2', + accessors: ['y3', 'y4'], + }, + ]; + + const data: LensMultiTable = { + type: 'lens_multitable', + tables: { + '1': { + type: 'datatable', + columns: [ + { id: 'split1', name: '', meta: { type: 'number' } }, + { id: 'y1', name: '', meta: { type: 'number' } }, + { id: 'y2', name: '', meta: { type: 'number' } }, + ], + rows: [ + { split1: 1 }, + { split1: 2 }, + { split1: 3 }, + { split1: 1 }, + { split1: 2 }, + { split1: 3 }, + ], + }, + '2': { + type: 'datatable', + columns: [ + { id: 'split2', name: '', meta: { type: 'number' } }, + { id: 'y1', name: '', meta: { type: 'number' } }, + { id: 'y2', name: '', meta: { type: 'number' } }, + ], + rows: [ + { split2: 1 }, + { split2: 2 }, + { split2: 3 }, + { split2: 1 }, + { split2: 2 }, + { split2: 3 }, + ], + }, + }, + }; + + const formatFactory = (() => + ({ + convert(x: unknown) { + return x; + }, + } as unknown)) as FormatFactory; + + describe('totalSeriesCount', () => { + it('should calculate total number of series per palette', () => { + const assignments = getColorAssignments(layers, data, formatFactory); + // two y accessors, with 3 splitted series + expect(assignments.palette1.totalSeriesCount).toEqual(2 * 3); + expect(assignments.palette2.totalSeriesCount).toEqual(2 * 3); + }); + + it('should calculate total number of series spanning multible layers', () => { + const assignments = getColorAssignments( + [layers[0], { ...layers[1], palette: layers[0].palette }], + data, + formatFactory + ); + // two y accessors, with 3 splitted series, two times + expect(assignments.palette1.totalSeriesCount).toEqual(2 * 3 + 2 * 3); + expect(assignments.palette2).toBeUndefined(); + }); + + it('should calculate total number of series for non split series', () => { + const assignments = getColorAssignments( + [layers[0], { ...layers[1], palette: layers[0].palette, splitAccessor: undefined }], + data, + formatFactory + ); + // two y accessors, with 3 splitted series for the first layer, 2 non splitted y accessors for the second layer + expect(assignments.palette1.totalSeriesCount).toEqual(2 * 3 + 2); + expect(assignments.palette2).toBeUndefined(); + }); + + it('should format non-primitive values and count them correctly', () => { + const complexObject = { aProp: 123 }; + const formatMock = jest.fn((x) => 'formatted'); + const assignments = getColorAssignments( + layers, + { + ...data, + tables: { + ...data.tables, + '1': { ...data.tables['1'], rows: [{ split1: complexObject }, { split1: 'abc' }] }, + }, + }, + (() => + ({ + convert: formatMock, + } as unknown)) as FormatFactory + ); + expect(assignments.palette1.totalSeriesCount).toEqual(2 * 2); + expect(assignments.palette2.totalSeriesCount).toEqual(2 * 3); + expect(formatMock).toHaveBeenCalledWith(complexObject); + }); + + it('should handle missing tables', () => { + const assignments = getColorAssignments(layers, { ...data, tables: {} }, formatFactory); + // if there is no data, just assume a single split + expect(assignments.palette1.totalSeriesCount).toEqual(2); + }); + + it('should handle missing columns', () => { + const assignments = getColorAssignments( + layers, + { + ...data, + tables: { + ...data.tables, + '1': { + ...data.tables['1'], + columns: [], + }, + }, + }, + formatFactory + ); + // if the split column is missing, just assume a single split + expect(assignments.palette1.totalSeriesCount).toEqual(2); + }); + }); + + describe('getRank', () => { + it('should return the correct rank for a series key', () => { + const assignments = getColorAssignments(layers, data, formatFactory); + // 3 series in front of 2/y2 - 1/y1, 1/y2 and 2/y1 + expect(assignments.palette1.getRank(layers[0], '2', 'y2')).toEqual(3); + // 1 series in front of 1/y4 - 1/y3 + expect(assignments.palette2.getRank(layers[1], '1', 'y4')).toEqual(1); + }); + + it('should return the correct rank for a series key spanning multiple layers', () => { + const newLayers = [layers[0], { ...layers[1], palette: layers[0].palette }]; + const assignments = getColorAssignments(newLayers, data, formatFactory); + // 3 series in front of 2/y2 - 1/y1, 1/y2 and 2/y1 + expect(assignments.palette1.getRank(newLayers[0], '2', 'y2')).toEqual(3); + // 2 series in front for the current layer (1/y3, 1/y4), plus all 6 series from the first layer + expect(assignments.palette1.getRank(newLayers[1], '2', 'y3')).toEqual(8); + }); + + it('should return the correct rank for a series without a split', () => { + const newLayers = [ + layers[0], + { ...layers[1], palette: layers[0].palette, splitAccessor: undefined }, + ]; + const assignments = getColorAssignments(newLayers, data, formatFactory); + // 3 series in front of 2/y2 - 1/y1, 1/y2 and 2/y1 + expect(assignments.palette1.getRank(newLayers[0], '2', 'y2')).toEqual(3); + // 1 series in front for the current layer (y3), plus all 6 series from the first layer + expect(assignments.palette1.getRank(newLayers[1], 'Metric y4', 'y4')).toEqual(7); + }); + + it('should return the correct rank for a series with a non-primitive value', () => { + const assignments = getColorAssignments( + layers, + { + ...data, + tables: { + ...data.tables, + '1': { ...data.tables['1'], rows: [{ split1: 'abc' }, { split1: { aProp: 123 } }] }, + }, + }, + (() => + ({ + convert: () => 'formatted', + } as unknown)) as FormatFactory + ); + // 3 series in front of (complex object)/y1 - abc/y1, abc/y2 + expect(assignments.palette1.getRank(layers[0], 'formatted', 'y1')).toEqual(2); + }); + + it('should handle missing tables', () => { + const assignments = getColorAssignments(layers, { ...data, tables: {} }, formatFactory); + // if there is no data, assume it is the first splitted series. One series in front - 0/y1 + expect(assignments.palette1.getRank(layers[0], '2', 'y2')).toEqual(1); + }); + + it('should handle missing columns', () => { + const assignments = getColorAssignments( + layers, + { + ...data, + tables: { + ...data.tables, + '1': { + ...data.tables['1'], + columns: [], + }, + }, + }, + formatFactory + ); + // if the split column is missing, assume it is the first splitted series. One series in front - 0/y1 + expect(assignments.palette1.getRank(layers[0], '2', 'y2')).toEqual(1); + }); + }); +}); diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.ts new file mode 100644 index 0000000000000..5a04602bec04a --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.ts @@ -0,0 +1,157 @@ +/* + * 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 { uniq, mapValues } from 'lodash'; +import type { PaletteOutput, PaletteRegistry } from 'src/plugins/charts/public'; +import type { Datatable } from 'src/plugins/expressions'; +import { euiLightVars } from '@kbn/ui-theme'; +import type { AccessorConfig, FramePublicAPI } from '../types'; +import { getColumnToLabelMap } from './state'; +import { FormatFactory } from '../types'; +import { isDataLayer, isReferenceLayer } from './visualization'; +import { DataLayerConfigResult, ReferenceLineLayerConfigResult, XYLayerConfig } from '../../common'; + +const isPrimitive = (value: unknown): boolean => value != null && typeof value !== 'object'; + +export const defaultReferenceLineColor = euiLightVars.euiColorDarkShade; + +export type ColorAssignments = Record< + string, + { + totalSeriesCount: number; + getRank(sortedLayer: DataLayerConfigResult, seriesKey: string, yAccessor: string): number; + } +>; + +export function getColorAssignments( + layers: XYLayerConfig[], + data: { tables: Record }, + formatFactory: FormatFactory +): ColorAssignments { + const layersPerPalette: Record = {}; + + layers + .filter((layer): layer is DataLayerConfigResult => isDataLayer(layer)) + .forEach((layer) => { + const palette = layer.palette?.name || 'default'; + if (!layersPerPalette[palette]) { + layersPerPalette[palette] = []; + } + layersPerPalette[palette].push(layer); + }); + + return mapValues(layersPerPalette, (paletteLayers) => { + const seriesPerLayer = paletteLayers.map((layer, layerIndex) => { + if (!layer.splitAccessor) { + return { numberOfSeries: layer.accessors.length, splits: [] }; + } + const splitAccessor = layer.splitAccessor; + const column = data.tables[layer.layerId]?.columns.find(({ id }) => id === splitAccessor); + const columnFormatter = column && formatFactory(column.meta.params); + const splits = + !column || !data.tables[layer.layerId] + ? [] + : uniq( + data.tables[layer.layerId].rows.map((row) => { + let value = row[splitAccessor]; + if (value && !isPrimitive(value)) { + value = columnFormatter?.convert(value) ?? value; + } else { + value = String(value); + } + return value; + }) + ); + return { numberOfSeries: (splits.length || 1) * layer.accessors.length, splits }; + }); + const totalSeriesCount = seriesPerLayer.reduce( + (sum, perLayer) => sum + perLayer.numberOfSeries, + 0 + ); + return { + totalSeriesCount, + getRank(sortedLayer: DataLayerConfigResult, seriesKey: string, yAccessor: string) { + const layerIndex = paletteLayers.findIndex((l) => sortedLayer.layerId === l.layerId); + const currentSeriesPerLayer = seriesPerLayer[layerIndex]; + const splitRank = currentSeriesPerLayer.splits.indexOf(seriesKey); + return ( + (layerIndex === 0 + ? 0 + : seriesPerLayer + .slice(0, layerIndex) + .reduce((sum, perLayer) => sum + perLayer.numberOfSeries, 0)) + + (sortedLayer.splitAccessor && splitRank !== -1 + ? splitRank * sortedLayer.accessors.length + : 0) + + sortedLayer.accessors.indexOf(yAccessor) + ); + }, + }; + }); +} + +const getReferenceLineAccessorColorConfig = (layer: ReferenceLineLayerConfigResult) => { + return layer.accessors.map((accessor) => { + const currentYConfig = layer.yConfig?.find((yConfig) => yConfig.forAccessor === accessor); + return { + columnId: accessor, + triggerIcon: 'color' as const, + color: currentYConfig?.color || defaultReferenceLineColor, + }; + }); +}; + +export function getAccessorColorConfig( + colorAssignments: ColorAssignments, + frame: Pick, + layer: XYLayerConfig, + paletteService: PaletteRegistry +): AccessorConfig[] { + if (isReferenceLayer(layer)) { + return getReferenceLineAccessorColorConfig(layer); + } + + const layerContainsSplits = Boolean(layer.splitAccessor); + const currentPalette: PaletteOutput = layer.palette || { type: 'palette', name: 'default' }; + const totalSeriesCount = colorAssignments[currentPalette.name]?.totalSeriesCount; + return layer.accessors.map((accessor) => { + const currentYConfig = layer.yConfig?.find((yConfig) => yConfig.forAccessor === accessor); + if (layerContainsSplits) { + return { + columnId: accessor as string, + triggerIcon: 'disabled', + }; + } + const columnToLabel = getColumnToLabelMap(layer, frame.datasourceLayers[layer.layerId]); + const rank = colorAssignments[currentPalette.name].getRank( + layer, + columnToLabel[accessor] || accessor, + accessor + ); + const customColor = + currentYConfig?.color || + (totalSeriesCount != null + ? paletteService.get(currentPalette.name).getCategoricalColor( + [ + { + name: columnToLabel[accessor] || accessor, + rankAtDepth: rank, + totalSeriesAtDepth: totalSeriesCount, + }, + ], + { maxDepth: 1, totalSeries: totalSeriesCount }, + currentPalette.params + ) + : undefined); + return { + columnId: accessor as string, + triggerIcon: customColor ? 'color' : 'disabled', + color: customColor ?? undefined, + }; + }); +} diff --git a/x-pack/plugins/lens/public/xy_visualization/fitting_functions.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/fitting_functions.ts similarity index 66% rename from x-pack/plugins/lens/public/xy_visualization/fitting_functions.ts rename to src/plugins/chart_expressions/expression_xy/public/helpers/fitting_functions.ts index 0b0878dfe9684..01cc9a32fa527 100644 --- a/x-pack/plugins/lens/public/xy_visualization/fitting_functions.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/fitting_functions.ts @@ -1,12 +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; you may not use this file except in compliance with the Elastic License - * 2.0. + * 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 { Fit } from '@elastic/charts'; -import { FittingFunction } from '../../common/expressions'; +import { FittingFunction } from '../../common'; export function getFitEnum(fittingFunction?: FittingFunction) { if (fittingFunction) { diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/icon.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/icon.ts new file mode 100644 index 0000000000000..57e285a07232f --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/icon.ts @@ -0,0 +1,11 @@ +/* + * 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 function hasIcon(icon: string | undefined): icon is string { + return icon != null && icon !== 'empty'; +} diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/index.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/index.ts new file mode 100644 index 0000000000000..fe2b7d89f9e2b --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/index.ts @@ -0,0 +1,17 @@ +/* + * 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 './interval'; +export * from './layers'; +export * from './state'; +export * from './visualization'; +export * from './fitting_functions'; +export * from './axes_configuration'; +export * from './reference_lines'; +export * from './icon'; +export * from './color_assignment'; diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/interval.test.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/interval.test.ts new file mode 100644 index 0000000000000..0fe979b8c3fc1 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/interval.test.ts @@ -0,0 +1,80 @@ +/* + * 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 { DataLayerConfigResult, XYChartProps } from '../../common'; +import { sampleArgs } from '../../common/__mocks__'; +import { calculateMinInterval } from './interval'; + +describe('calculateMinInterval', () => { + let xyProps: XYChartProps; + + beforeEach(() => { + xyProps = sampleArgs(); + (xyProps.args.layers[0] as DataLayerConfigResult).xScaleType = 'time'; + }); + it('should use first valid layer and determine interval', async () => { + xyProps.data.tables.first.columns[2].meta.source = 'esaggs'; + xyProps.data.tables.first.columns[2].meta.sourceParams = { + type: 'date_histogram', + params: { + used_interval: '5m', + }, + }; + const result = await calculateMinInterval(xyProps); + expect(result).toEqual(5 * 60 * 1000); + }); + + it('should return interval of number histogram if available on first x axis columns', async () => { + (xyProps.args.layers[0] as DataLayerConfigResult).xScaleType = 'linear'; + xyProps.data.tables.first.columns[2].meta = { + source: 'esaggs', + type: 'number', + field: 'someField', + sourceParams: { + type: 'histogram', + params: { + interval: 'auto', + used_interval: 5, + }, + }, + }; + const result = await calculateMinInterval(xyProps); + expect(result).toEqual(5); + }); + + it('should return undefined if data table is empty', async () => { + xyProps.data.tables.first.rows = []; + xyProps.data.tables.first.columns[2].meta.source = 'esaggs'; + xyProps.data.tables.first.columns[2].meta.sourceParams = { + type: 'date_histogram', + params: { + used_interval: '5m', + }, + }; + const result = await calculateMinInterval(xyProps); + expect(result).toEqual(undefined); + }); + + it('should return undefined if interval can not be checked', async () => { + const result = await calculateMinInterval(xyProps); + expect(result).toEqual(undefined); + }); + + it('should return undefined if date column is not found', async () => { + xyProps.data.tables.first.columns.splice(2, 1); + const result = await calculateMinInterval(xyProps); + expect(result).toEqual(undefined); + }); + + it('should return undefined if x axis is not a date', async () => { + (xyProps.args.layers[0] as DataLayerConfigResult).xScaleType = 'ordinal'; + xyProps.data.tables.first.columns.splice(2, 1); + const result = await calculateMinInterval(xyProps); + expect(result).toEqual(undefined); + }); +}); diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/interval.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/interval.ts new file mode 100644 index 0000000000000..dbf01d214564d --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/interval.ts @@ -0,0 +1,36 @@ +/* + * 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 { search } from 'src/plugins/data/public'; +import { XYChartProps } from '../../common'; +import { getFilteredLayers } from './layers'; +import { isDataLayer } from './visualization'; + +export function calculateMinInterval({ args: { layers }, data }: XYChartProps) { + const filteredLayers = getFilteredLayers(layers, data); + if (filteredLayers.length === 0) return; + const isTimeViz = filteredLayers.every((l) => isDataLayer(l) && l.xScaleType === 'time'); + const xColumn = data.tables[filteredLayers[0].layerId].columns.find( + (column) => isDataLayer(filteredLayers[0]) && column.id === filteredLayers[0].xAccessor + ); + + if (!xColumn) return; + if (!isTimeViz) { + const histogramInterval = search.aggs.getNumberHistogramIntervalByDatatableColumn(xColumn); + if (typeof histogramInterval === 'number') { + return histogramInterval; + } else { + return undefined; + } + } + const dateInterval = search.aggs.getDateHistogramMetaDataByDatatableColumn(xColumn)?.interval; + if (!dateInterval) return; + const intervalDuration = search.aggs.parseInterval(dateInterval); + if (!intervalDuration) return; + return intervalDuration.as('milliseconds'); +} diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/layers.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/layers.ts new file mode 100644 index 0000000000000..d855ba3096a02 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/layers.ts @@ -0,0 +1,32 @@ +/* + * 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 { DataLayerConfigResult, LensMultiTable, XYLayerConfig } from '../../common'; +import { isDataLayer } from './visualization'; + +export function getFilteredLayers(layers: XYLayerConfig[], data: LensMultiTable) { + return layers.filter((layer): layer is DataLayerConfigResult => { + if (!isDataLayer(layer)) { + return false; + } + + const { layerId, accessors, xAccessor, splitAccessor } = layer; + + return !( + !accessors.length || + !data.tables[layerId] || + data.tables[layerId].rows.length === 0 || + (xAccessor && + data.tables[layerId].rows.every((row) => typeof row[xAccessor] === 'undefined')) || + // stacked percentage bars have no xAccessors but splitAccessor with undefined values in them when empty + (!xAccessor && + splitAccessor && + data.tables[layerId].rows.every((row) => typeof row[splitAccessor] === 'undefined')) + ); + }); +} diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/reference_lines.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/reference_lines.ts new file mode 100644 index 0000000000000..5d70d22f4e9d8 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/reference_lines.ts @@ -0,0 +1,454 @@ +/* + * 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 { groupBy, partition } from 'lodash'; +import { i18n } from '@kbn/i18n'; +import { Datatable } from 'src/plugins/expressions'; +import uuid from 'uuid/v4'; +import { LayerTypes, YAxisModes } from '../../common/constants'; +import type { DataLayerConfigResult, XYLayerConfig, YConfig } from '../../common'; +import type { XYState, AccessorConfig, DatasourcePublicAPI, FramePublicAPI } from '../types'; +import { groupAxesByType } from './axes_configuration'; +import { isHorizontalChart, isPercentageSeries, isStackedChart } from './state'; +import { checkScaleOperation, getAxisName, getDataLayers, isNumericMetric } from './visualization'; +import { BarReferenceLineIcon } from '../icons'; + +export interface ReferenceLineBase { + label: 'x' | 'yRight' | 'yLeft'; +} + +/** + * Return the reference layers groups to show based on multiple criteria: + * * what groups are current defined in data layers + * * what existing reference line are currently defined in reference layers + */ +export function getGroupsToShow( + referenceLayers: T[], + state: XYState | undefined, + datasourceLayers: Record, + tables: Record | undefined +): Array { + if (!state) { + return []; + } + const dataLayers = getDataLayers(state.layers); + const groupsAvailable = getGroupsAvailableInData(dataLayers, datasourceLayers, tables); + return referenceLayers + .filter(({ label, config }: T) => groupsAvailable[label] || config?.length) + .map((layer) => ({ ...layer, valid: groupsAvailable[layer.label] })); +} + +/** + * Returns the reference layers groups to show based on what groups are current defined in data layers. + */ +export function getGroupsRelatedToData( + referenceLayers: T[], + state: XYState | undefined, + datasourceLayers: Record, + tables: Record | undefined +): T[] { + if (!state) { + return []; + } + const dataLayers = getDataLayers(state.layers); + const groupsAvailable = getGroupsAvailableInData(dataLayers, datasourceLayers, tables); + return referenceLayers.filter(({ label }: T) => groupsAvailable[label]); +} +/** + * Returns a dictionary with the groups filled in all the data layers + */ +export function getGroupsAvailableInData( + dataLayers: DataLayerConfigResult[], + datasourceLayers: Record, + tables: Record | undefined +) { + const hasNumberHistogram = dataLayers.some( + checkScaleOperation('interval', 'number', datasourceLayers) + ); + const { right, left } = groupAxesByType(dataLayers, tables); + return { + x: dataLayers.some(({ xAccessor }) => xAccessor != null) && hasNumberHistogram, + yLeft: left.length > 0, + yRight: right.length > 0, + }; +} + +export function getStaticValue( + dataLayers: DataLayerConfigResult[], + groupId: 'x' | 'yLeft' | 'yRight', + { activeData }: Pick, + layerHasNumberHistogram: (layer: DataLayerConfigResult) => boolean +) { + const fallbackValue = 100; + if (!activeData) { + return fallbackValue; + } + + // filter and organize data dimensions into reference layer groups + // now pick the columnId in the active data + const { + dataLayers: filteredLayers, + untouchedDataLayers, + accessors, + } = getAccessorCriteriaForGroup(groupId, dataLayers, activeData); + if ( + groupId === 'x' && + filteredLayers.length && + !untouchedDataLayers.some(layerHasNumberHistogram) + ) { + return fallbackValue; + } + const computedValue = computeStaticValueForGroup( + filteredLayers, + accessors, + activeData, + groupId !== 'x', // histogram axis should compute the min based on the current data + groupId !== 'x' + ); + return computedValue ?? fallbackValue; +} + +function getAccessorCriteriaForGroup( + groupId: 'x' | 'yLeft' | 'yRight', + dataLayers: DataLayerConfigResult[], + activeData: FramePublicAPI['activeData'] +) { + switch (groupId) { + case 'x': { + const filteredDataLayers = dataLayers.filter(({ xAccessor }) => xAccessor); + // need to reshape the dataLayers to match the other accessors format + return { + dataLayers: filteredDataLayers.map(({ accessors, xAccessor, ...rest }) => ({ + ...rest, + accessors: [xAccessor] as string[], + })), + // need the untouched ones to check if there are invalid layers from the filtered ones + // to perform the checks the original accessor structure needs to be accessed + untouchedDataLayers: filteredDataLayers, + accessors: filteredDataLayers.map(({ xAccessor }) => xAccessor) as string[], + }; + } + case 'yLeft': + case 'yRight': { + const prop = groupId === 'yLeft' ? 'left' : 'right'; + const { [prop]: axis } = groupAxesByType(dataLayers, activeData); + const rightIds = new Set(axis.map(({ layer }) => layer)); + const filteredDataLayers = dataLayers.filter(({ layerId }) => rightIds.has(layerId)); + return { + dataLayers: filteredDataLayers, + untouchedDataLayers: filteredDataLayers, + accessors: axis.map(({ accessor }) => accessor), + }; + } + } +} + +export function computeOverallDataDomain( + dataLayers: DataLayerConfigResult[], + accessorIds: string[], + activeData: NonNullable, + allowStacking: boolean = true +) { + const accessorMap = new Set(accessorIds); + let min: number | undefined; + let max: number | undefined; + const [stacked, unstacked] = partition( + dataLayers, + ({ seriesType }) => isStackedChart(seriesType) && allowStacking + ); + for (const { layerId, accessors } of unstacked) { + const table = activeData[layerId]; + if (table) { + for (const accessor of accessors) { + if (accessorMap.has(accessor)) { + for (const row of table.rows) { + const value = row[accessor]; + if (typeof value === 'number') { + // when not stacked, do not keep the 0 + max = max != null ? Math.max(value, max) : value; + min = min != null ? Math.min(value, min) : value; + } + } + } + } + } + } + // stacked can span multiple layers, so compute an overall max/min by bucket + const stackedResults: Record = {}; + for (const { layerId, accessors, xAccessor } of stacked) { + const table = activeData[layerId]; + if (table) { + for (const accessor of accessors) { + if (accessorMap.has(accessor)) { + for (const row of table.rows) { + const value = row[accessor]; + // start with a shared bucket + let bucket = 'shared'; + // but if there's an xAccessor use it as new bucket system + if (xAccessor) { + bucket = row[xAccessor]; + } + if (typeof value === 'number') { + stackedResults[bucket] = stackedResults[bucket] ?? 0; + stackedResults[bucket] += value; + } + } + } + } + } + } + + for (const value of Object.values(stackedResults)) { + // for stacked extents keep 0 in view + max = Math.max(value, max || 0, 0); + min = Math.min(value, min || 0, 0); + } + + return { min, max }; +} + +function computeStaticValueForGroup( + dataLayers: DataLayerConfigResult[], + accessorIds: string[], + activeData: NonNullable, + minZeroOrNegativeBase: boolean = true, + allowStacking: boolean = true +) { + const defaultReferenceLineFactor = 3 / 4; + + if (dataLayers.length && accessorIds.length) { + if (dataLayers.some(({ seriesType }) => isPercentageSeries(seriesType))) { + return defaultReferenceLineFactor; + } + + const { min, max } = computeOverallDataDomain( + dataLayers, + accessorIds, + activeData, + allowStacking + ); + + if (min != null && max != null && isFinite(min) && isFinite(max)) { + // Custom axis bounds can go below 0, so consider also lower values than 0 + const finalMinValue = minZeroOrNegativeBase ? Math.min(0, min) : min; + const interval = max - finalMinValue; + return Number((finalMinValue + interval * defaultReferenceLineFactor).toFixed(2)); + } + } +} + +export const getReferenceSupportedLayer = ( + state?: XYState, + frame?: Pick +) => { + const referenceLineGroupIds = [ + { + id: 'yReferenceLineLeft', + label: 'yLeft' as const, + }, + { + id: 'yReferenceLineRight', + label: 'yRight' as const, + }, + { + id: 'xReferenceLine', + label: 'x' as const, + }, + ]; + const referenceLineGroups = getGroupsRelatedToData( + referenceLineGroupIds, + state, + frame?.datasourceLayers || {}, + frame?.activeData + ); + const dataLayers = getDataLayers(state?.layers || []); + const filledDataLayers = dataLayers.filter( + ({ accessors, xAccessor }) => accessors.length || xAccessor + ); + const layerHasNumberHistogram = checkScaleOperation( + 'interval', + 'number', + frame?.datasourceLayers || {} + ); + + const initialDimensions = state + ? referenceLineGroups.map(({ id, label }) => ({ + groupId: id, + columnId: uuid(), + dataType: 'number', + label: getAxisName(label, { isHorizontal: isHorizontalChart(state?.layers || []) }), + staticValue: getStaticValue( + dataLayers, + label, + { activeData: frame?.activeData }, + layerHasNumberHistogram + ), + })) + : undefined; + + return { + type: LayerTypes.REFERENCELINE, + label: i18n.translate('xpack.lens.xyChart.addReferenceLineLayerLabel', { + defaultMessage: 'Reference lines', + }), + icon: BarReferenceLineIcon, + disabled: + !filledDataLayers.length || + (!dataLayers.some(layerHasNumberHistogram) && + dataLayers.every(({ accessors }) => !accessors.length)), + toolTipContent: filledDataLayers.length + ? undefined + : i18n.translate('xpack.lens.xyChart.addReferenceLineLayerLabelDisabledHelp', { + defaultMessage: 'Add some data to enable reference layer', + }), + initialDimensions, + }; +}; +export const setReferenceDimension = ({ + prevState, + layerId, + columnId, + groupId, + previousColumn, +}: { + prevState: XYState; + layerId: string; + columnId: string; + groupId: string; + previousColumn: string; +}) => { + const foundLayer = prevState.layers.find((l) => l.layerId === layerId); + if (!foundLayer) { + return prevState; + } + const newLayer = { ...foundLayer }; + + newLayer.accessors = [...newLayer.accessors.filter((a) => a !== columnId), columnId]; + const hasYConfig = newLayer.yConfig?.some(({ forAccessor }) => forAccessor === columnId); + const previousYConfig = previousColumn + ? newLayer.yConfig?.find(({ forAccessor }) => forAccessor === previousColumn) + : false; + if (!hasYConfig) { + newLayer.yConfig = [ + ...(newLayer.yConfig || []), + ...(previousYConfig + ? [ + { + // override with previous styling, + ...previousYConfig, + // but keep the new group & id config + forAccessor: columnId, + axisMode: + groupId === 'xReferenceLine' + ? YAxisModes.BOTTOM + : groupId === 'yReferenceLineRight' + ? YAxisModes.RIGHT + : YAxisModes.LEFT, + }, + ] + : []), + ]; + } + return { + ...prevState, + layers: prevState.layers.map((l) => (l.layerId === layerId ? newLayer : l)), + }; +}; + +export const getReferenceConfiguration = ({ + state, + frame, + layer, + sortedAccessors, + mappedAccessors, +}: { + state: XYState; + frame: FramePublicAPI; + layer: XYLayerConfig; + sortedAccessors: string[]; + mappedAccessors: AccessorConfig[]; +}) => { + const idToIndex = sortedAccessors.reduce>((memo, id, index) => { + memo[id] = index; + return memo; + }, {}); + const { bottom, left, right } = groupBy( + [...(layer.yConfig || [])].sort( + ({ forAccessor: forA }, { forAccessor: forB }) => idToIndex[forA] - idToIndex[forB] + ), + ({ axisMode }) => { + return axisMode; + } + ); + const groupsToShow = getGroupsToShow( + [ + // When a reference layer panel is added, a static reference line should automatically be included by default + // in the first available axis, in the following order: vertical left, vertical right, horizontal. + { + config: left, + id: 'yReferenceLineLeft', + label: 'yLeft', + dataTestSubj: 'lnsXY_yReferenceLineLeftPanel', + }, + { + config: right, + id: 'yReferenceLineRight', + label: 'yRight', + dataTestSubj: 'lnsXY_yReferenceLineRightPanel', + }, + { + config: bottom, + id: 'xReferenceLine', + label: 'x', + dataTestSubj: 'lnsXY_xReferenceLinePanel', + }, + ], + state, + frame.datasourceLayers, + frame?.activeData + ); + const isHorizontal = isHorizontalChart(state.layers); + return { + // Each reference lines layer panel will have sections for each available axis + // (horizontal axis, vertical axis left, vertical axis right). + // Only axes that support numeric reference lines should be shown + groups: groupsToShow.map(({ config = [], id, label, dataTestSubj, valid }) => ({ + groupId: id, + groupLabel: getAxisName(label, { isHorizontal }), + accessors: config.map(({ forAccessor, color }) => ({ + columnId: forAccessor, + color: color || mappedAccessors.find(({ columnId }) => columnId === forAccessor)?.color, + triggerIcon: 'color' as const, + })), + filterOperations: isNumericMetric, + supportsMoreColumns: true, + required: false, + enableDimensionEditor: true, + supportStaticValue: true, + paramEditorCustomProps: { + label: i18n.translate('xpack.lens.indexPattern.staticValue.label', { + defaultMessage: 'Reference line value', + }), + }, + supportFieldFormat: false, + dataTestSubj, + invalid: !valid, + invalidMessage: + label === 'x' + ? i18n.translate('xpack.lens.configure.invalidBottomReferenceLineDimension', { + defaultMessage: + 'This reference line is assigned to an axis that no longer exists or is no longer valid. You may move this reference line to another available axis or remove it.', + }) + : i18n.translate('xpack.lens.configure.invalidReferenceLineDimension', { + defaultMessage: + 'This reference line is assigned to an axis that no longer exists. You may move this reference line to another available axis or remove it.', + }), + requiresPreviousColumnOnDuplicate: true, + })), + }; +}; diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/state.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/state.ts new file mode 100644 index 0000000000000..66c4be8d84974 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/state.ts @@ -0,0 +1,89 @@ +/* + * 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 { EuiIconType } from '@elastic/eui/src/components/icon/icon'; +import type { FramePublicAPI, DatasourcePublicAPI } from '../types'; +import type { SeriesType, XYLayerConfig, YConfig, ValidLayer } from '../../common'; +import { visualizationDefinitions } from '../definitions'; +import { getDataLayers, isDataLayer } from './visualization'; + +export function isHorizontalSeries(seriesType: SeriesType) { + return ( + seriesType === 'bar_horizontal' || + seriesType === 'bar_horizontal_stacked' || + seriesType === 'bar_horizontal_percentage_stacked' + ); +} + +export function isPercentageSeries(seriesType: SeriesType) { + return ( + seriesType === 'bar_percentage_stacked' || + seriesType === 'bar_horizontal_percentage_stacked' || + seriesType === 'area_percentage_stacked' + ); +} + +export function isStackedChart(seriesType: SeriesType) { + return seriesType.includes('stacked'); +} + +export function isHorizontalChart(layers: XYLayerConfig[]) { + return getDataLayers(layers).every((l) => isHorizontalSeries(l.seriesType)); +} + +export function getIconForSeries(type: SeriesType): EuiIconType { + const definition = visualizationDefinitions.find((t) => t.id === type); + + if (!definition) { + throw new Error(`Unknown series type ${type}`); + } + + return (definition.icon as EuiIconType) || 'empty'; +} + +export const getSeriesColor = (layer: XYLayerConfig, accessor: string) => { + if (isDataLayer(layer) && layer.splitAccessor) { + return null; + } + return ( + layer?.yConfig?.find((yConfig: YConfig) => yConfig.forAccessor === accessor)?.color || null + ); +}; + +export const getColumnToLabelMap = (layer: XYLayerConfig, datasource: DatasourcePublicAPI) => { + const columnToLabel: Record = {}; + layer.accessors + .concat(isDataLayer(layer) && layer.splitAccessor ? [layer.splitAccessor] : []) + .forEach((accessor) => { + const operation = datasource.getOperationForColumnId(accessor); + if (operation?.label) { + columnToLabel[accessor] = operation.label; + } + }); + return columnToLabel; +}; + +export function hasHistogramSeries( + layers: ValidLayer[] = [], + datasourceLayers?: FramePublicAPI['datasourceLayers'] +) { + if (!datasourceLayers) { + return false; + } + const validLayers = layers.filter(({ accessors }) => accessors.length); + + return validLayers.some(({ layerId, xAccessor }: ValidLayer) => { + const xAxisOperation = datasourceLayers[layerId].getOperationForColumnId(xAccessor); + return ( + xAxisOperation && + xAxisOperation.isBucketed && + xAxisOperation.scale && + xAxisOperation.scale !== 'ordinal' + ); + }); +} diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/visualization.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/visualization.ts new file mode 100644 index 0000000000000..2f7a2c8e26fd6 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/visualization.ts @@ -0,0 +1,317 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { uniq } from 'lodash'; +import { + DatasourcePublicAPI, + OperationMetadata, + VisualizationType, + State, + XYState, +} from '../types'; +import { visualizationDefinitions } from '../definitions'; +import { isHorizontalChart } from './state'; +import { + DataLayerConfigResult, + ReferenceLineLayerConfigResult, + SeriesType, + XYDataLayerConfig, + XYLayerConfig, +} from '../../common'; +import { LayerTypes } from '../../common/constants'; +import { BarHorizontalIcon, BarStackedIcon, MixedXyIcon } from '../icons'; +import { LayerType } from '../../common'; + +export function getAxisName( + axis: 'x' | 'y' | 'yLeft' | 'yRight', + { isHorizontal }: { isHorizontal: boolean } +) { + const vertical = i18n.translate('xpack.lens.xyChart.verticalAxisLabel', { + defaultMessage: 'Vertical axis', + }); + const horizontal = i18n.translate('xpack.lens.xyChart.horizontalAxisLabel', { + defaultMessage: 'Horizontal axis', + }); + if (axis === 'x') { + return isHorizontal ? vertical : horizontal; + } + if (axis === 'y') { + return isHorizontal ? horizontal : vertical; + } + const verticalLeft = i18n.translate('xpack.lens.xyChart.verticalLeftAxisLabel', { + defaultMessage: 'Vertical left axis', + }); + const verticalRight = i18n.translate('xpack.lens.xyChart.verticalRightAxisLabel', { + defaultMessage: 'Vertical right axis', + }); + const horizontalTop = i18n.translate('xpack.lens.xyChart.horizontalLeftAxisLabel', { + defaultMessage: 'Horizontal top axis', + }); + const horizontalBottom = i18n.translate('xpack.lens.xyChart.horizontalRightAxisLabel', { + defaultMessage: 'Horizontal bottom axis', + }); + if (axis === 'yLeft') { + return isHorizontal ? horizontalBottom : verticalLeft; + } + return isHorizontal ? horizontalTop : verticalRight; +} + +// min requirement for the bug: +// * 2 or more layers +// * at least one with date histogram +// * at least one with interval function +export function checkXAccessorCompatibility( + state: XYState, + datasourceLayers: Record +) { + const dataLayers = getDataLayers(state.layers); + const errors = []; + const hasDateHistogramSet = dataLayers.some( + checkScaleOperation('interval', 'date', datasourceLayers) + ); + const hasNumberHistogram = dataLayers.some( + checkScaleOperation('interval', 'number', datasourceLayers) + ); + const hasOrdinalAxis = dataLayers.some( + checkScaleOperation('ordinal', undefined, datasourceLayers) + ); + if (state.layers.length > 1 && hasDateHistogramSet && hasNumberHistogram) { + errors.push({ + shortMessage: i18n.translate('xpack.lens.xyVisualization.dataTypeFailureXShort', { + defaultMessage: `Wrong data type for {axis}.`, + values: { + axis: getAxisName('x', { isHorizontal: isHorizontalChart(state.layers) }), + }, + }), + longMessage: i18n.translate('xpack.lens.xyVisualization.dataTypeFailureXLong', { + defaultMessage: `Data type mismatch for the {axis}. Cannot mix date and number interval types.`, + values: { + axis: getAxisName('x', { isHorizontal: isHorizontalChart(state.layers) }), + }, + }), + }); + } + if (state.layers.length > 1 && (hasDateHistogramSet || hasNumberHistogram) && hasOrdinalAxis) { + errors.push({ + shortMessage: i18n.translate('xpack.lens.xyVisualization.dataTypeFailureXShort', { + defaultMessage: `Wrong data type for {axis}.`, + values: { + axis: getAxisName('x', { isHorizontal: isHorizontalChart(state.layers) }), + }, + }), + longMessage: i18n.translate('xpack.lens.xyVisualization.dataTypeFailureXOrdinalLong', { + defaultMessage: `Data type mismatch for the {axis}, use a different function.`, + values: { + axis: getAxisName('x', { isHorizontal: isHorizontalChart(state.layers) }), + }, + }), + }); + } + return errors; +} + +export function checkScaleOperation( + scaleType: 'ordinal' | 'interval' | 'ratio', + dataType: 'date' | 'number' | 'string' | undefined, + datasourceLayers: Record +) { + return (layer: XYDataLayerConfig) => { + const datasourceAPI = datasourceLayers[layer.layerId]; + if (!layer.xAccessor) { + return false; + } + const operation = datasourceAPI?.getOperationForColumnId(layer.xAccessor); + return Boolean( + operation && (!dataType || operation.dataType === dataType) && operation.scale === scaleType + ); + }; +} + +export const isDataLayer = (layer: XYLayerConfig): layer is DataLayerConfigResult => + layer.layerType === LayerTypes.DATA || !layer.layerType; + +export const getDataLayers = (layers: XYLayerConfig[]) => + (layers || []).filter((layer): layer is DataLayerConfigResult => isDataLayer(layer)); + +export const getFirstDataLayer = (layers: XYLayerConfig[]) => + (layers || []).find((layer): layer is DataLayerConfigResult => isDataLayer(layer)); + +export const isReferenceLayer = (layer: XYLayerConfig): layer is ReferenceLineLayerConfigResult => + layer.layerType === LayerTypes.REFERENCELINE; + +export const getReferenceLayers = (layers: XYLayerConfig[]) => + (layers || []).filter((layer): layer is ReferenceLineLayerConfigResult => + isReferenceLayer(layer) + ); + +export function getVisualizationType(state: State): VisualizationType | 'mixed' { + if (!state.layers.length) { + return ( + visualizationDefinitions.find((t) => t.id === state.preferredSeriesType) ?? + visualizationDefinitions[0] + ); + } + const dataLayers = getDataLayers(state?.layers); + const visualizationType = visualizationDefinitions.find( + (t) => t.id === dataLayers?.[0].seriesType + ); + const seriesTypes = uniq(dataLayers.map((l) => l.seriesType)); + + return visualizationType && seriesTypes.length === 1 ? visualizationType : 'mixed'; +} + +export function getDescription(state?: State) { + if (!state) { + return { + icon: defaultIcon, + label: i18n.translate('xpack.lens.xyVisualization.xyLabel', { + defaultMessage: 'XY', + }), + }; + } + + const visualizationType = getVisualizationType(state); + + if (visualizationType === 'mixed' && isHorizontalChart(state.layers)) { + return { + icon: BarHorizontalIcon, + label: i18n.translate('xpack.lens.xyVisualization.mixedBarHorizontalLabel', { + defaultMessage: 'Mixed bar horizontal', + }), + }; + } + + if (visualizationType === 'mixed') { + return { + icon: MixedXyIcon, + label: i18n.translate('xpack.lens.xyVisualization.mixedLabel', { + defaultMessage: 'Mixed XY', + }), + }; + } + + return { + icon: visualizationType.icon || defaultIcon, + label: visualizationType.fullLabel || visualizationType.label, + }; +} + +export const defaultIcon = BarStackedIcon; +export const defaultSeriesType = 'bar_stacked'; + +export const supportedDataLayer = { + type: LayerTypes.DATA, + label: i18n.translate('xpack.lens.xyChart.addDataLayerLabel', { + defaultMessage: 'Visualization', + }), + icon: MixedXyIcon, +}; + +// i18n ids cannot be dynamically generated, hence the function below +export function getMessageIdsForDimension( + dimension: string, + layers: number[], + isHorizontal: boolean +) { + const layersList = layers.map((i: number) => i + 1).join(', '); + switch (dimension) { + case 'Break down': + return { + shortMessage: i18n.translate('xpack.lens.xyVisualization.dataFailureSplitShort', { + defaultMessage: `Missing {axis}.`, + values: { axis: 'Break down by axis' }, + }), + longMessage: i18n.translate('xpack.lens.xyVisualization.dataFailureSplitLong', { + defaultMessage: `{layers, plural, one {Layer} other {Layers}} {layersList} {layers, plural, one {requires} other {require}} a field for the {axis}.`, + values: { layers: layers.length, layersList, axis: 'Break down by axis' }, + }), + }; + case 'Y': + return { + shortMessage: i18n.translate('xpack.lens.xyVisualization.dataFailureYShort', { + defaultMessage: `Missing {axis}.`, + values: { axis: getAxisName('y', { isHorizontal }) }, + }), + longMessage: i18n.translate('xpack.lens.xyVisualization.dataFailureYLong', { + defaultMessage: `{layers, plural, one {Layer} other {Layers}} {layersList} {layers, plural, one {requires} other {require}} a field for the {axis}.`, + values: { layers: layers.length, layersList, axis: getAxisName('y', { isHorizontal }) }, + }), + }; + } + return { shortMessage: '', longMessage: '' }; +} + +export function newLayerState( + seriesType: SeriesType, + layerId: string, + layerType: LayerType = LayerTypes.DATA +): XYLayerConfig { + if (layerType === 'data') { + return { + type: 'lens_xy_data_layer', + layerId, + seriesType, + accessors: [], + layerType, + yScaleType: 'linear', + xScaleType: 'linear', + isHistogram: false, + palette: { type: 'palette', name: 'default' }, + }; + } + + return { + type: 'lens_xy_referenceLine_layer', + layerId, + accessors: [], + layerType, + }; +} + +export function getLayersByType(state: State, byType?: string) { + return state.layers.filter(({ layerType = LayerTypes.DATA }) => + byType ? layerType === byType : true + ); +} + +export function validateLayersForDimension( + dimension: string, + layers: XYLayerConfig[], + missingCriteria: (layer: XYLayerConfig) => boolean +): + | { valid: true } + | { + valid: false; + payload: { shortMessage: string; longMessage: React.ReactNode }; + } { + // Multiple layers must be consistent: + // * either a dimension is missing in ALL of them + // * or should not miss on any + if (layers.every(missingCriteria) || !layers.some(missingCriteria)) { + return { valid: true }; + } + // otherwise it's an error and it has to be reported + const layerMissingAccessors = layers.reduce((missing: number[], layer, i) => { + if (missingCriteria(layer)) { + missing.push(i); + } + return missing; + }, []); + + return { + valid: false, + payload: getMessageIdsForDimension(dimension, layerMissingAccessors, isHorizontalChart(layers)), + }; +} + +export const isNumericMetric = (op: OperationMetadata) => + !op.isBucketed && op.dataType === 'number'; +export const isNumericDynamicMetric = (op: OperationMetadata) => + isNumericMetric(op) && !op.isStaticValue; +export const isBucketed = (op: OperationMetadata) => op.isBucketed; diff --git a/src/plugins/chart_expressions/expression_xy/public/icons/area.tsx b/src/plugins/chart_expressions/expression_xy/public/icons/area.tsx new file mode 100644 index 0000000000000..010ffaf1fb7ec --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/public/icons/area.tsx @@ -0,0 +1,32 @@ +/* + * 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 React from 'react'; +import { EuiIconProps } from '@elastic/eui'; + +export const AreaIcon = ({ title, titleId, ...props }: Omit) => ( + + {title ? {title} : null} + + + +); diff --git a/src/plugins/chart_expressions/expression_xy/public/icons/area_percentage.tsx b/src/plugins/chart_expressions/expression_xy/public/icons/area_percentage.tsx new file mode 100644 index 0000000000000..a51e66b68ba93 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/public/icons/area_percentage.tsx @@ -0,0 +1,32 @@ +/* + * 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 React from 'react'; +import { EuiIconProps } from '@elastic/eui'; + +export const AreaPercentageIcon = ({ title, titleId, ...props }: Omit) => ( + + {title ? {title} : null} + + + +); diff --git a/src/plugins/chart_expressions/expression_xy/public/icons/area_stacked.tsx b/src/plugins/chart_expressions/expression_xy/public/icons/area_stacked.tsx new file mode 100644 index 0000000000000..c2b9fbe926328 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/public/icons/area_stacked.tsx @@ -0,0 +1,32 @@ +/* + * 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 React from 'react'; +import { EuiIconProps } from '@elastic/eui'; + +export const AreaStackedIcon = ({ title, titleId, ...props }: Omit) => ( + + {title ? {title} : null} + + + +); diff --git a/src/plugins/chart_expressions/expression_xy/public/icons/bar.tsx b/src/plugins/chart_expressions/expression_xy/public/icons/bar.tsx new file mode 100644 index 0000000000000..f134d7871bfde --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/public/icons/bar.tsx @@ -0,0 +1,32 @@ +/* + * 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 React from 'react'; +import { EuiIconProps } from '@elastic/eui'; + +export const BarIcon = ({ title, titleId, ...props }: Omit) => ( + + {title ? {title} : null} + + + +); diff --git a/src/plugins/chart_expressions/expression_xy/public/icons/bar_horizontal.tsx b/src/plugins/chart_expressions/expression_xy/public/icons/bar_horizontal.tsx new file mode 100644 index 0000000000000..a2fb843cb095d --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/public/icons/bar_horizontal.tsx @@ -0,0 +1,32 @@ +/* + * 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 React from 'react'; +import { EuiIconProps } from '@elastic/eui'; + +export const BarHorizontalIcon = ({ title, titleId, ...props }: Omit) => ( + + {title ? {title} : null} + + + +); diff --git a/src/plugins/chart_expressions/expression_xy/public/icons/bar_horizontal_percentage.tsx b/src/plugins/chart_expressions/expression_xy/public/icons/bar_horizontal_percentage.tsx new file mode 100644 index 0000000000000..6b2bb61a246e1 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/public/icons/bar_horizontal_percentage.tsx @@ -0,0 +1,36 @@ +/* + * 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 React from 'react'; +import { EuiIconProps } from '@elastic/eui'; + +export const BarHorizontalPercentageIcon = ({ + title, + titleId, + ...props +}: Omit) => ( + + {title ? {title} : null} + + + +); diff --git a/src/plugins/chart_expressions/expression_xy/public/icons/bar_horizontal_stacked.tsx b/src/plugins/chart_expressions/expression_xy/public/icons/bar_horizontal_stacked.tsx new file mode 100644 index 0000000000000..b399c47d3fc7d --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/public/icons/bar_horizontal_stacked.tsx @@ -0,0 +1,36 @@ +/* + * 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 React from 'react'; +import { EuiIconProps } from '@elastic/eui'; + +export const BarHorizontalStackedIcon = ({ + title, + titleId, + ...props +}: Omit) => ( + + {title ? {title} : null} + + + +); diff --git a/src/plugins/chart_expressions/expression_xy/public/icons/bar_percentage.tsx b/src/plugins/chart_expressions/expression_xy/public/icons/bar_percentage.tsx new file mode 100644 index 0000000000000..64514cea6c012 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/public/icons/bar_percentage.tsx @@ -0,0 +1,32 @@ +/* + * 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 React from 'react'; +import { EuiIconProps } from '@elastic/eui'; + +export const BarPercentageIcon = ({ title, titleId, ...props }: Omit) => ( + + {title ? {title} : null} + + + +); diff --git a/src/plugins/chart_expressions/expression_xy/public/icons/bar_reference_line.tsx b/src/plugins/chart_expressions/expression_xy/public/icons/bar_reference_line.tsx new file mode 100644 index 0000000000000..95bd8e2a8d0a2 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/public/icons/bar_reference_line.tsx @@ -0,0 +1,37 @@ +/* + * 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 React from 'react'; +import { EuiIconProps } from '@elastic/eui'; + +export const BarReferenceLineIcon = ({ title, titleId, ...props }: Omit) => ( + + {title ? {title} : null} + + + + + +); diff --git a/src/plugins/chart_expressions/expression_xy/public/icons/bar_stacked.tsx b/src/plugins/chart_expressions/expression_xy/public/icons/bar_stacked.tsx new file mode 100644 index 0000000000000..833f3d0e816e6 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/public/icons/bar_stacked.tsx @@ -0,0 +1,32 @@ +/* + * 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 React from 'react'; +import { EuiIconProps } from '@elastic/eui'; + +export const BarStackedIcon = ({ title, titleId, ...props }: Omit) => ( + + {title ? {title} : null} + + + +); diff --git a/src/plugins/chart_expressions/expression_xy/public/icons/index.ts b/src/plugins/chart_expressions/expression_xy/public/icons/index.ts new file mode 100644 index 0000000000000..3f78474a6d54c --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/public/icons/index.ts @@ -0,0 +1,20 @@ +/* + * 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 { BarHorizontalPercentageIcon } from './bar_horizontal_percentage'; +export { BarHorizontalStackedIcon } from './bar_horizontal_stacked'; +export { BarReferenceLineIcon } from './bar_reference_line'; +export { AreaPercentageIcon } from './area_percentage'; +export { BarHorizontalIcon } from './bar_horizontal'; +export { BarPercentageIcon } from './bar_percentage'; +export { AreaStackedIcon } from './area_stacked'; +export { BarStackedIcon } from './bar_stacked'; +export { MixedXyIcon } from './mixed_xy'; +export { AreaIcon } from './area'; +export { LineIcon } from './line'; +export { BarIcon } from './bar'; diff --git a/src/plugins/chart_expressions/expression_xy/public/icons/line.tsx b/src/plugins/chart_expressions/expression_xy/public/icons/line.tsx new file mode 100644 index 0000000000000..6735f58b734ec --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/public/icons/line.tsx @@ -0,0 +1,32 @@ +/* + * 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 React from 'react'; +import { EuiIconProps } from '@elastic/eui'; + +export const LineIcon = ({ title, titleId, ...props }: Omit) => ( + + {title ? {title} : null} + + + +); diff --git a/src/plugins/chart_expressions/expression_xy/public/icons/mixed_xy.tsx b/src/plugins/chart_expressions/expression_xy/public/icons/mixed_xy.tsx new file mode 100644 index 0000000000000..e16b7f6bed76f --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/public/icons/mixed_xy.tsx @@ -0,0 +1,36 @@ +/* + * 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 React from 'react'; +import { EuiIconProps } from '@elastic/eui'; + +export const MixedXyIcon = ({ title, titleId, ...props }: Omit) => ( + + {title ? {title} : null} + + + + +); diff --git a/src/plugins/chart_expressions/expression_xy/public/plugin.ts b/src/plugins/chart_expressions/expression_xy/public/plugin.ts index d2222684cffbd..a95e4582e63c7 100755 --- a/src/plugins/chart_expressions/expression_xy/public/plugin.ts +++ b/src/plugins/chart_expressions/expression_xy/public/plugin.ts @@ -6,7 +6,12 @@ * Side Public License, v 1. */ -import { CoreSetup, CoreStart, Plugin } from '../../../../core/public'; +import { LEGACY_TIME_AXIS } from 'src/plugins/charts/common'; +import { DataPublicPluginStart } from 'src/plugins/data/public'; +import { FieldFormatsStart } from 'src/plugins/field_formats/public'; +import { ChartsPluginStart } from 'src/plugins/charts/public'; +import moment from 'moment'; +import { CoreSetup, CoreStart, IUiSettingsClient } from '../../../../core/public'; import { ExpressionXyPluginSetup, ExpressionXyPluginStart, SetupDeps } from './types'; import { xyChartFunction, @@ -20,11 +25,28 @@ import { referenceLineLayerConfigFunction, axisTitlesVisibilityConfigFunction, } from '../common'; +import { GetStartDepsFn, getXyChartRenderer } from './expression_renderers'; -export class ExpressionXyPlugin - implements Plugin -{ - public setup(core: CoreSetup, { expressions }: SetupDeps): ExpressionXyPluginSetup { +export interface XYPluginStartDependencies { + data: DataPublicPluginStart; + fieldFormats: FieldFormatsStart; + charts: ChartsPluginStart; +} + +export function getTimeZone(uiSettings: IUiSettingsClient) { + const configuredTimeZone = uiSettings.get('dateFormat:tz'); + if (configuredTimeZone === 'Browser') { + return moment.tz.guess(); + } + + return configuredTimeZone; +} + +export class ExpressionXyPlugin { + public setup( + core: CoreSetup, + { expressions, charts }: SetupDeps + ): ExpressionXyPluginSetup { expressions.registerFunction(yAxisConfigFunction); expressions.registerFunction(legendConfigFunction); expressions.registerFunction(gridlinesConfigFunction); @@ -35,6 +57,37 @@ export class ExpressionXyPlugin expressions.registerFunction(referenceLineLayerConfigFunction); expressions.registerFunction(axisTitlesVisibilityConfigFunction); expressions.registerFunction(xyChartFunction); + + const getStartDeps: GetStartDepsFn = async () => { + const [coreStart, deps] = await core.getStartServices(); + const { + data, + fieldFormats, + charts: { activeCursor, theme, palettes }, + } = deps; + + const paletteService = await palettes.getPalettes(); + + const { theme: kibanaTheme } = coreStart; + const useLegacyTimeAxis = core.uiSettings.get(LEGACY_TIME_AXIS); + + return { + data, + formatFactory: fieldFormats.deserialize, + kibanaTheme, + theme, + activeCursor, + paletteService, + useLegacyTimeAxis, + timeZone: getTimeZone(core.uiSettings), + }; + }; + + expressions.registerRenderer( + getXyChartRenderer({ + getStartDeps, + }) + ); } public start(core: CoreStart): ExpressionXyPluginStart {} diff --git a/src/plugins/chart_expressions/expression_xy/public/types.ts b/src/plugins/chart_expressions/expression_xy/public/types.ts index 2025e95fe890c..7dcf7bc79a309 100755 --- a/src/plugins/chart_expressions/expression_xy/public/types.ts +++ b/src/plugins/chart_expressions/expression_xy/public/types.ts @@ -6,10 +6,31 @@ * Side Public License, v 1. */ -import { ExpressionsPublicPlugin, ExpressionsServiceStart } from '../../../expressions/public'; +import { Query } from 'src/plugins/data/common'; +import { IconType } from '@elastic/eui'; +import { DataPublicPluginSetup } from 'src/plugins/data/public'; +import { FieldFormatsSetup } from 'src/plugins/field_formats/public'; +import { ChartsPluginSetup } from 'src/plugins/charts/public'; +import { IFieldFormat, SerializedFieldFormat } from '../../../../plugins/field_formats/common'; +import type { RangeSelectContext, ValueClickContext } from '../../../../plugins/embeddable/public'; +import { Datatable, ExpressionsServiceStart, ExpressionsSetup } from '../../../expressions/public'; +import { + AxesSettingsConfig, + AxisExtentConfig, + FittingFunction, + LabelsOrientationConfig, + LegendConfigResult, + SeriesType, + ValueLabelMode, + XYCurveType, + XYLayerConfig, +} from '../common'; export interface SetupDeps { - expressions: ReturnType; + expressions: ExpressionsSetup; + data: DataPublicPluginSetup; + fieldFormats: FieldFormatsSetup; + charts: ChartsPluginSetup; } export interface StartDeps { @@ -18,3 +39,148 @@ export interface StartDeps { export type ExpressionXyPluginSetup = void; export type ExpressionXyPluginStart = void; + +export interface FilterEvent { + name: 'filter'; + data: ValueClickContext['data']; +} + +export interface BrushEvent { + name: 'brush'; + data: RangeSelectContext['data']; +} + +export type FormatFactory = (mapping?: SerializedFieldFormat) => IFieldFormat; + +export interface OperationDescriptor extends Operation { + hasTimeShift: boolean; +} + +export type SortingHint = 'version'; +export type FieldOnlyDataType = 'document' | 'ip' | 'histogram' | 'geo_point' | 'geo_shape'; +export type DataType = 'string' | 'number' | 'date' | 'boolean' | FieldOnlyDataType; + +export interface OperationMetadata { + // The output of this operation will have this data type + dataType: DataType; + // A bucketed operation is grouped by duplicate values, otherwise each row is + // treated as unique + isBucketed: boolean; + /** + * ordinal: Each name is a unique value, but the names are in sorted order, like "Top values" + * interval: Histogram data, like date or number histograms + * ratio: Most number data is rendered as a ratio that includes 0 + */ + scale?: 'ordinal' | 'interval' | 'ratio'; + // Extra meta-information like cardinality, color + // TODO currently it's not possible to differentiate between a field from a raw + // document and an aggregated metric which might be handy in some cases. Once we + // introduce a raw document datasource, this should be considered here. + isStaticValue?: boolean; +} + +export interface Operation extends OperationMetadata { + // User-facing label for the operation + label: string; + sortingHint?: SortingHint; +} + +export interface FramePublicAPI { + datasourceLayers: Record; + appliedDatasourceLayers?: Record; // this is only set when auto-apply is turned off + /** + * Data of the chart currently rendered in the preview. + * This data might be not available (e.g. if the chart can't be rendered) or outdated and belonging to another chart. + * If accessing, make sure to check whether expected columns actually exist. + */ + activeData?: Record; +} + +/** + * This is an API provided to visualizations by the frame, which calls the publicAPI on the datasource + */ +export interface DatasourcePublicAPI { + datasourceId: string; + getTableSpec: () => Array<{ columnId: string; fields: string[] }>; + getOperationForColumnId: (columnId: string) => OperationDescriptor | null; + /** + * Collect all default visual values given the current state + */ + getVisualDefaults: () => Record>; + /** + * Retrieve the specific source id for the current state + */ + getSourceId: () => string | undefined; + /** + * Collect all defined filters from all the operations in the layer + */ + getFilters: (activeData?: FramePublicAPI['activeData']) => { + kuery: Query[][]; + lucene: Query[][]; + }; +} + +/** + * A visualization type advertised to the user in the chart switcher + */ +export interface VisualizationType { + /** + * Unique id of the visualization type within the visualization defining it + */ + id: string; + /** + * Icon used in the chart switcher + */ + icon: IconType; + /** + * Visible label used in the chart switcher and above the workspace panel in collapsed state + */ + label: string; + /** + * Optional label used in visualization type search if chart switcher is expanded and for tooltips + */ + fullLabel?: string; + /** + * The group the visualization belongs to + */ + groupLabel: string; + /** + * The priority of the visualization in the list (global priority) + * Higher number means higher priority. When omitted defaults to 0 + */ + sortPriority?: number; + /** + * Indicates if visualization is in the experimental stage. + */ + showExperimentalBadge?: boolean; +} + +export interface XYState { + preferredSeriesType: SeriesType; + legend: LegendConfigResult; + valueLabels?: ValueLabelMode; + fittingFunction?: FittingFunction; + yLeftExtent?: AxisExtentConfig; + yRightExtent?: AxisExtentConfig; + layers: XYLayerConfig[]; + xTitle?: string; + yTitle?: string; + yRightTitle?: string; + axisTitlesVisibilitySettings?: AxesSettingsConfig; + tickLabelsVisibilitySettings?: AxesSettingsConfig; + gridlinesVisibilitySettings?: AxesSettingsConfig; + labelsOrientation?: LabelsOrientationConfig; + curveType?: XYCurveType; + fillOpacity?: number; + hideEndzones?: boolean; + valuesInLegend?: boolean; +} + +export type State = XYState; + +export interface AccessorConfig { + columnId: string; + triggerIcon?: 'color' | 'disabled' | 'colorBy' | 'none' | 'invisible'; + color?: string; + palette?: string[] | Array<{ color: string; stop: number }>; +} diff --git a/src/plugins/chart_expressions/expression_xy/tsconfig.json b/src/plugins/chart_expressions/expression_xy/tsconfig.json index 34350093a8989..be36bbcaf78a2 100644 --- a/src/plugins/chart_expressions/expression_xy/tsconfig.json +++ b/src/plugins/chart_expressions/expression_xy/tsconfig.json @@ -16,5 +16,8 @@ { "path": "../../charts/tsconfig.json" }, { "path": "../../../core/tsconfig.json" }, { "path": "../../expressions/tsconfig.json" }, + { "path": "../../data/tsconfig.json"}, + { "path": "../../ui_actions/tsconfig.json" }, + { "path": "../../field_formats/tsconfig.json"}, ] } diff --git a/x-pack/plugins/lens/common/expressions/index.ts b/x-pack/plugins/lens/common/expressions/index.ts index 526bee92ec7e5..47ff8318447b2 100644 --- a/x-pack/plugins/lens/common/expressions/index.ts +++ b/x-pack/plugins/lens/common/expressions/index.ts @@ -11,6 +11,5 @@ export * from './rename_columns'; export * from './merge_tables'; export * from './time_scale'; export * from './datatable'; -export * from './xy_chart'; export * from './expression_types'; diff --git a/x-pack/plugins/lens/common/expressions/xy_chart/axis_config.ts b/x-pack/plugins/lens/common/expressions/xy_chart/axis_config.ts deleted file mode 100644 index 0b9667353706d..0000000000000 --- a/x-pack/plugins/lens/common/expressions/xy_chart/axis_config.ts +++ /dev/null @@ -1,207 +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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import { i18n } from '@kbn/i18n'; -import type { - ArgumentType, - ExpressionFunctionDefinition, -} from '../../../../../../src/plugins/expressions/common'; - -export interface AxesSettingsConfig { - x: boolean; - yLeft: boolean; - yRight: boolean; -} - -export interface AxisExtentConfig { - mode: 'full' | 'dataBounds' | 'custom'; - lowerBound?: number; - upperBound?: number; -} - -interface AxisConfig { - title: string; - hide?: boolean; -} - -export type YAxisMode = 'auto' | 'left' | 'right' | 'bottom'; -export type LineStyle = 'solid' | 'dashed' | 'dotted'; -export type FillStyle = 'none' | 'above' | 'below'; -export type IconPosition = 'auto' | 'left' | 'right' | 'above' | 'below'; - -export interface YConfig { - forAccessor: string; - axisMode?: YAxisMode; - color?: string; - icon?: string; - lineWidth?: number; - lineStyle?: LineStyle; - fill?: FillStyle; - iconPosition?: IconPosition; - textVisibility?: boolean; -} - -export type AxisTitlesVisibilityConfigResult = AxesSettingsConfig & { - type: 'lens_xy_axisTitlesVisibilityConfig'; -}; - -export const axisTitlesVisibilityConfig: ExpressionFunctionDefinition< - 'lens_xy_axisTitlesVisibilityConfig', - null, - AxesSettingsConfig, - AxisTitlesVisibilityConfigResult -> = { - name: 'lens_xy_axisTitlesVisibilityConfig', - aliases: [], - type: 'lens_xy_axisTitlesVisibilityConfig', - help: `Configure the xy chart's axis titles appearance`, - inputTypes: ['null'], - args: { - x: { - types: ['boolean'], - help: i18n.translate('xpack.lens.xyChart.xAxisTitle.help', { - defaultMessage: 'Specifies whether or not the title of the x-axis are visible.', - }), - }, - yLeft: { - types: ['boolean'], - help: i18n.translate('xpack.lens.xyChart.yLeftAxisTitle.help', { - defaultMessage: 'Specifies whether or not the title of the left y-axis are visible.', - }), - }, - yRight: { - types: ['boolean'], - help: i18n.translate('xpack.lens.xyChart.yRightAxisTitle.help', { - defaultMessage: 'Specifies whether or not the title of the right y-axis are visible.', - }), - }, - }, - fn: function fn(input: unknown, args: AxesSettingsConfig) { - return { - type: 'lens_xy_axisTitlesVisibilityConfig', - ...args, - }; - }, -}; - -export type AxisExtentConfigResult = AxisExtentConfig & { type: 'lens_xy_axisExtentConfig' }; - -export const axisExtentConfig: ExpressionFunctionDefinition< - 'lens_xy_axisExtentConfig', - null, - AxisExtentConfig, - AxisExtentConfigResult -> = { - name: 'lens_xy_axisExtentConfig', - aliases: [], - type: 'lens_xy_axisExtentConfig', - help: `Configure the xy chart's axis extents`, - inputTypes: ['null'], - args: { - mode: { - types: ['string'], - options: ['full', 'dataBounds', 'custom'], - help: i18n.translate('xpack.lens.xyChart.extentMode.help', { - defaultMessage: 'The extent mode', - }), - }, - lowerBound: { - types: ['number'], - help: i18n.translate('xpack.lens.xyChart.extentMode.help', { - defaultMessage: 'The extent mode', - }), - }, - upperBound: { - types: ['number'], - help: i18n.translate('xpack.lens.xyChart.extentMode.help', { - defaultMessage: 'The extent mode', - }), - }, - }, - fn: function fn(input: unknown, args: AxisExtentConfig) { - return { - type: 'lens_xy_axisExtentConfig', - ...args, - }; - }, -}; - -export const axisConfig: { [key in keyof AxisConfig]: ArgumentType } = { - title: { - types: ['string'], - help: i18n.translate('xpack.lens.xyChart.title.help', { - defaultMessage: 'The axis title', - }), - }, - hide: { - types: ['boolean'], - default: false, - help: 'Show / hide axis', - }, -}; - -export type YConfigResult = YConfig & { type: 'lens_xy_yConfig' }; - -export const yAxisConfig: ExpressionFunctionDefinition< - 'lens_xy_yConfig', - null, - YConfig, - YConfigResult -> = { - name: 'lens_xy_yConfig', - aliases: [], - type: 'lens_xy_yConfig', - help: `Configure the behavior of a xy chart's y axis metric`, - inputTypes: ['null'], - args: { - forAccessor: { - types: ['string'], - help: 'The accessor this configuration is for', - }, - axisMode: { - types: ['string'], - options: ['auto', 'left', 'right'], - help: 'The axis mode of the metric', - }, - color: { - types: ['string'], - help: 'The color of the series', - }, - lineStyle: { - types: ['string'], - options: ['solid', 'dotted', 'dashed'], - help: 'The style of the reference line', - }, - lineWidth: { - types: ['number'], - help: 'The width of the reference line', - }, - icon: { - types: ['string'], - help: 'An optional icon used for reference lines', - }, - iconPosition: { - types: ['string'], - options: ['auto', 'above', 'below', 'left', 'right'], - help: 'The placement of the icon for the reference line', - }, - textVisibility: { - types: ['boolean'], - help: 'Visibility of the label on the reference line', - }, - fill: { - types: ['string'], - options: ['none', 'above', 'below'], - help: '', - }, - }, - fn: function fn(input: unknown, args: YConfig) { - return { - type: 'lens_xy_yConfig', - ...args, - }; - }, -}; diff --git a/x-pack/plugins/lens/common/expressions/xy_chart/grid_lines_config.ts b/x-pack/plugins/lens/common/expressions/xy_chart/grid_lines_config.ts deleted file mode 100644 index 6338e9f039937..0000000000000 --- a/x-pack/plugins/lens/common/expressions/xy_chart/grid_lines_config.ts +++ /dev/null @@ -1,51 +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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { i18n } from '@kbn/i18n'; -import type { ExpressionFunctionDefinition } from '../../../../../../src/plugins/expressions/common'; -import type { AxesSettingsConfig } from './axis_config'; - -export type GridlinesConfigResult = AxesSettingsConfig & { type: 'lens_xy_gridlinesConfig' }; - -export const gridlinesConfig: ExpressionFunctionDefinition< - 'lens_xy_gridlinesConfig', - null, - AxesSettingsConfig, - GridlinesConfigResult -> = { - name: 'lens_xy_gridlinesConfig', - aliases: [], - type: 'lens_xy_gridlinesConfig', - help: `Configure the xy chart's gridlines appearance`, - inputTypes: ['null'], - args: { - x: { - types: ['boolean'], - help: i18n.translate('xpack.lens.xyChart.xAxisGridlines.help', { - defaultMessage: 'Specifies whether or not the gridlines of the x-axis are visible.', - }), - }, - yLeft: { - types: ['boolean'], - help: i18n.translate('xpack.lens.xyChart.yLeftAxisgridlines.help', { - defaultMessage: 'Specifies whether or not the gridlines of the left y-axis are visible.', - }), - }, - yRight: { - types: ['boolean'], - help: i18n.translate('xpack.lens.xyChart.yRightAxisgridlines.help', { - defaultMessage: 'Specifies whether or not the gridlines of the right y-axis are visible.', - }), - }, - }, - fn: function fn(input: unknown, args: AxesSettingsConfig) { - return { - type: 'lens_xy_gridlinesConfig', - ...args, - }; - }, -}; diff --git a/x-pack/plugins/lens/common/expressions/xy_chart/index.ts b/x-pack/plugins/lens/common/expressions/xy_chart/index.ts deleted file mode 100644 index a6f6c715c0ed1..0000000000000 --- a/x-pack/plugins/lens/common/expressions/xy_chart/index.ts +++ /dev/null @@ -1,17 +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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export * from './axis_config'; -export * from './fitting_function'; -export * from './grid_lines_config'; -export * from './layer_config'; -export * from './legend_config'; -export * from './series_type'; -export * from './tick_labels_config'; -export * from './xy_args'; -export * from './xy_chart'; -export * from './labels_orientation_config'; diff --git a/x-pack/plugins/lens/common/expressions/xy_chart/labels_orientation_config.ts b/x-pack/plugins/lens/common/expressions/xy_chart/labels_orientation_config.ts deleted file mode 100644 index 773ce61a102f9..0000000000000 --- a/x-pack/plugins/lens/common/expressions/xy_chart/labels_orientation_config.ts +++ /dev/null @@ -1,60 +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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import { i18n } from '@kbn/i18n'; -import type { ExpressionFunctionDefinition } from '../../../../../../src/plugins/expressions/common'; - -export interface LabelsOrientationConfig { - x: number; - yLeft: number; - yRight: number; -} - -export type LabelsOrientationConfigResult = LabelsOrientationConfig & { - type: 'lens_xy_labelsOrientationConfig'; -}; - -export const labelsOrientationConfig: ExpressionFunctionDefinition< - 'lens_xy_labelsOrientationConfig', - null, - LabelsOrientationConfig, - LabelsOrientationConfigResult -> = { - name: 'lens_xy_labelsOrientationConfig', - aliases: [], - type: 'lens_xy_labelsOrientationConfig', - help: `Configure the xy chart's tick labels orientation`, - inputTypes: ['null'], - args: { - x: { - types: ['number'], - options: [0, -90, -45], - help: i18n.translate('xpack.lens.xyChart.xAxisLabelsOrientation.help', { - defaultMessage: 'Specifies the labels orientation of the x-axis.', - }), - }, - yLeft: { - types: ['number'], - options: [0, -90, -45], - help: i18n.translate('xpack.lens.xyChart.yLeftAxisLabelsOrientation.help', { - defaultMessage: 'Specifies the labels orientation of the left y-axis.', - }), - }, - yRight: { - types: ['number'], - options: [0, -90, -45], - help: i18n.translate('xpack.lens.xyChart.yRightAxisLabelsOrientation.help', { - defaultMessage: 'Specifies the labels orientation of the right y-axis.', - }), - }, - }, - fn: function fn(input: unknown, args: LabelsOrientationConfig) { - return { - type: 'lens_xy_labelsOrientationConfig', - ...args, - }; - }, -}; diff --git a/x-pack/plugins/lens/common/expressions/xy_chart/layer_config/data_layer_config.ts b/x-pack/plugins/lens/common/expressions/xy_chart/layer_config/data_layer_config.ts deleted file mode 100644 index 322edccba19e3..0000000000000 --- a/x-pack/plugins/lens/common/expressions/xy_chart/layer_config/data_layer_config.ts +++ /dev/null @@ -1,122 +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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { layerTypes } from '../../../constants'; -import type { PaletteOutput } from '../../../../../../../src/plugins/charts/common'; -import type { ExpressionFunctionDefinition } from '../../../../../../../src/plugins/expressions/common'; -import { axisConfig, YConfig } from '../axis_config'; -import type { SeriesType } from '../series_type'; - -export interface XYDataLayerConfig { - layerId: string; - layerType: typeof layerTypes.DATA; - accessors: string[]; - seriesType: SeriesType; - xAccessor?: string; - hide?: boolean; - yConfig?: YConfig[]; - splitAccessor?: string; - palette?: PaletteOutput; -} -export interface ValidLayer extends XYDataLayerConfig { - xAccessor: NonNullable; -} - -export type DataLayerArgs = XYDataLayerConfig & { - columnToLabel?: string; // Actually a JSON key-value pair - yScaleType: 'time' | 'linear' | 'log' | 'sqrt'; - xScaleType: 'time' | 'linear' | 'ordinal'; - isHistogram: boolean; - // palette will always be set on the expression - palette: PaletteOutput; -}; - -export type DataLayerConfigResult = DataLayerArgs & { type: 'lens_xy_data_layer' }; - -export const dataLayerConfig: ExpressionFunctionDefinition< - 'lens_xy_data_layer', - null, - DataLayerArgs, - DataLayerConfigResult -> = { - name: 'lens_xy_data_layer', - aliases: [], - type: 'lens_xy_data_layer', - help: `Configure a layer in the xy chart`, - inputTypes: ['null'], - args: { - ...axisConfig, - layerId: { - types: ['string'], - help: '', - }, - xAccessor: { - types: ['string'], - help: '', - }, - layerType: { types: ['string'], options: [layerTypes.DATA], help: '' }, - seriesType: { - types: ['string'], - options: [ - 'bar', - 'line', - 'area', - 'bar_stacked', - 'area_stacked', - 'bar_percentage_stacked', - 'area_percentage_stacked', - ], - help: 'The type of chart to display.', - }, - xScaleType: { - options: ['ordinal', 'linear', 'time'], - help: 'The scale type of the x axis', - default: 'ordinal', - }, - isHistogram: { - types: ['boolean'], - default: false, - help: 'Whether to layout the chart as a histogram', - }, - yScaleType: { - options: ['log', 'sqrt', 'linear', 'time'], - help: 'The scale type of the y axes', - default: 'linear', - }, - splitAccessor: { - types: ['string'], - help: 'The column to split by', - multi: false, - }, - accessors: { - types: ['string'], - help: 'The columns to display on the y axis.', - multi: true, - }, - yConfig: { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - types: ['lens_xy_yConfig' as any], - help: 'Additional configuration for y axes', - multi: true, - }, - columnToLabel: { - types: ['string'], - help: 'JSON key-value pairs of column ID to label', - }, - palette: { - default: `{theme "palette" default={system_palette name="default"} }`, - help: '', - types: ['palette'], - }, - }, - fn: function fn(input: unknown, args: DataLayerArgs) { - return { - type: 'lens_xy_data_layer', - ...args, - }; - }, -}; diff --git a/x-pack/plugins/lens/common/expressions/xy_chart/layer_config/index.ts b/x-pack/plugins/lens/common/expressions/xy_chart/layer_config/index.ts deleted file mode 100644 index 0b27ce7d6ed85..0000000000000 --- a/x-pack/plugins/lens/common/expressions/xy_chart/layer_config/index.ts +++ /dev/null @@ -1,12 +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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import { XYDataLayerConfig } from './data_layer_config'; -import { XYReferenceLineLayerConfig } from './reference_line_layer_config'; -export * from './data_layer_config'; -export * from './reference_line_layer_config'; - -export type XYLayerConfig = XYDataLayerConfig | XYReferenceLineLayerConfig; diff --git a/x-pack/plugins/lens/common/expressions/xy_chart/layer_config/reference_line_layer_config.ts b/x-pack/plugins/lens/common/expressions/xy_chart/layer_config/reference_line_layer_config.ts deleted file mode 100644 index 6e241f8b8db65..0000000000000 --- a/x-pack/plugins/lens/common/expressions/xy_chart/layer_config/reference_line_layer_config.ts +++ /dev/null @@ -1,64 +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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { ExpressionFunctionDefinition } from '../../../../../../../src/plugins/expressions/common'; -import { layerTypes } from '../../../constants'; -import { YConfig } from '../axis_config'; - -export interface XYReferenceLineLayerConfig { - layerId: string; - layerType: typeof layerTypes.REFERENCELINE; - accessors: string[]; - yConfig?: YConfig[]; -} -export type ReferenceLineLayerArgs = XYReferenceLineLayerConfig & { - columnToLabel?: string; -}; -export type ReferenceLineLayerConfigResult = ReferenceLineLayerArgs & { - type: 'lens_xy_referenceLine_layer'; -}; - -export const referenceLineLayerConfig: ExpressionFunctionDefinition< - 'lens_xy_referenceLine_layer', - null, - ReferenceLineLayerArgs, - ReferenceLineLayerConfigResult -> = { - name: 'lens_xy_referenceLine_layer', - aliases: [], - type: 'lens_xy_referenceLine_layer', - help: `Configure a layer in the xy chart`, - inputTypes: ['null'], - args: { - layerId: { - types: ['string'], - help: '', - }, - layerType: { types: ['string'], options: [layerTypes.REFERENCELINE], help: '' }, - accessors: { - types: ['string'], - help: 'The columns to display on the y axis.', - multi: true, - }, - yConfig: { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - types: ['lens_xy_yConfig' as any], - help: 'Additional configuration for y axes', - multi: true, - }, - columnToLabel: { - types: ['string'], - help: 'JSON key-value pairs of column ID to label', - }, - }, - fn: function fn(input: unknown, args: ReferenceLineLayerArgs) { - return { - type: 'lens_xy_referenceLine_layer', - ...args, - }; - }, -}; diff --git a/x-pack/plugins/lens/common/expressions/xy_chart/legend_config.ts b/x-pack/plugins/lens/common/expressions/xy_chart/legend_config.ts deleted file mode 100644 index fdf8d06b59424..0000000000000 --- a/x-pack/plugins/lens/common/expressions/xy_chart/legend_config.ts +++ /dev/null @@ -1,131 +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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import { HorizontalAlignment, Position, VerticalAlignment } from '@elastic/charts'; -import { i18n } from '@kbn/i18n'; -import type { ExpressionFunctionDefinition } from '../../../../../../src/plugins/expressions/common'; - -export interface LegendConfig { - /** - * Flag whether the legend should be shown. If there is just a single series, it will be hidden - */ - isVisible: boolean; - /** - * Position of the legend relative to the chart - */ - position: Position; - /** - * Flag whether the legend should be shown even with just a single series - */ - showSingleSeries?: boolean; - /** - * Flag whether the legend is inside the chart - */ - isInside?: boolean; - /** - * Horizontal Alignment of the legend when it is set inside chart - */ - horizontalAlignment?: HorizontalAlignment; - /** - * Vertical Alignment of the legend when it is set inside chart - */ - verticalAlignment?: VerticalAlignment; - /** - * Number of columns when legend is set inside chart - */ - floatingColumns?: number; - /** - * Maximum number of lines per legend item - */ - maxLines?: number; - /** - * Flag whether the legend items are truncated or not - */ - shouldTruncate?: boolean; -} - -export type LegendConfigResult = LegendConfig & { type: 'lens_xy_legendConfig' }; - -export const legendConfig: ExpressionFunctionDefinition< - 'lens_xy_legendConfig', - null, - LegendConfig, - LegendConfigResult -> = { - name: 'lens_xy_legendConfig', - aliases: [], - type: 'lens_xy_legendConfig', - help: `Configure the xy chart's legend`, - inputTypes: ['null'], - args: { - isVisible: { - types: ['boolean'], - help: i18n.translate('xpack.lens.xyChart.isVisible.help', { - defaultMessage: 'Specifies whether or not the legend is visible.', - }), - }, - position: { - types: ['string'], - options: [Position.Top, Position.Right, Position.Bottom, Position.Left], - help: i18n.translate('xpack.lens.xyChart.position.help', { - defaultMessage: 'Specifies the legend position.', - }), - }, - showSingleSeries: { - types: ['boolean'], - help: i18n.translate('xpack.lens.xyChart.showSingleSeries.help', { - defaultMessage: 'Specifies whether a legend with just a single entry should be shown', - }), - }, - isInside: { - types: ['boolean'], - help: i18n.translate('xpack.lens.xyChart.isInside.help', { - defaultMessage: 'Specifies whether a legend is inside the chart', - }), - }, - horizontalAlignment: { - types: ['string'], - options: [HorizontalAlignment.Right, HorizontalAlignment.Left], - help: i18n.translate('xpack.lens.xyChart.horizontalAlignment.help', { - defaultMessage: - 'Specifies the horizontal alignment of the legend when it is displayed inside chart.', - }), - }, - verticalAlignment: { - types: ['string'], - options: [VerticalAlignment.Top, VerticalAlignment.Bottom], - help: i18n.translate('xpack.lens.xyChart.verticalAlignment.help', { - defaultMessage: - 'Specifies the vertical alignment of the legend when it is displayed inside chart.', - }), - }, - floatingColumns: { - types: ['number'], - help: i18n.translate('xpack.lens.xyChart.floatingColumns.help', { - defaultMessage: 'Specifies the number of columns when legend is displayed inside chart.', - }), - }, - maxLines: { - types: ['number'], - help: i18n.translate('xpack.lens.xyChart.maxLines.help', { - defaultMessage: 'Specifies the number of lines per legend item.', - }), - }, - shouldTruncate: { - types: ['boolean'], - default: true, - help: i18n.translate('xpack.lens.xyChart.shouldTruncate.help', { - defaultMessage: 'Specifies whether the legend items will be truncated or not', - }), - }, - }, - fn: function fn(input: unknown, args: LegendConfig) { - return { - type: 'lens_xy_legendConfig', - ...args, - }; - }, -}; diff --git a/x-pack/plugins/lens/common/expressions/xy_chart/series_type.ts b/x-pack/plugins/lens/common/expressions/xy_chart/series_type.ts deleted file mode 100644 index f9a375b8b47a1..0000000000000 --- a/x-pack/plugins/lens/common/expressions/xy_chart/series_type.ts +++ /dev/null @@ -1,18 +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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export type SeriesType = - | 'bar' - | 'bar_horizontal' - | 'line' - | 'area' - | 'bar_stacked' - | 'bar_percentage_stacked' - | 'bar_horizontal_stacked' - | 'bar_horizontal_percentage_stacked' - | 'area_stacked' - | 'area_percentage_stacked'; diff --git a/x-pack/plugins/lens/common/expressions/xy_chart/tick_labels_config.ts b/x-pack/plugins/lens/common/expressions/xy_chart/tick_labels_config.ts deleted file mode 100644 index 4af78d8355786..0000000000000 --- a/x-pack/plugins/lens/common/expressions/xy_chart/tick_labels_config.ts +++ /dev/null @@ -1,51 +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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { i18n } from '@kbn/i18n'; -import type { ExpressionFunctionDefinition } from '../../../../../../src/plugins/expressions/common'; -import type { AxesSettingsConfig } from './axis_config'; - -export type TickLabelsConfigResult = AxesSettingsConfig & { type: 'lens_xy_tickLabelsConfig' }; - -export const tickLabelsConfig: ExpressionFunctionDefinition< - 'lens_xy_tickLabelsConfig', - null, - AxesSettingsConfig, - TickLabelsConfigResult -> = { - name: 'lens_xy_tickLabelsConfig', - aliases: [], - type: 'lens_xy_tickLabelsConfig', - help: `Configure the xy chart's tick labels appearance`, - inputTypes: ['null'], - args: { - x: { - types: ['boolean'], - help: i18n.translate('xpack.lens.xyChart.xAxisTickLabels.help', { - defaultMessage: 'Specifies whether or not the tick labels of the x-axis are visible.', - }), - }, - yLeft: { - types: ['boolean'], - help: i18n.translate('xpack.lens.xyChart.yLeftAxisTickLabels.help', { - defaultMessage: 'Specifies whether or not the tick labels of the left y-axis are visible.', - }), - }, - yRight: { - types: ['boolean'], - help: i18n.translate('xpack.lens.xyChart.yRightAxisTickLabels.help', { - defaultMessage: 'Specifies whether or not the tick labels of the right y-axis are visible.', - }), - }, - }, - fn: function fn(input: unknown, args: AxesSettingsConfig) { - return { - type: 'lens_xy_tickLabelsConfig', - ...args, - }; - }, -}; diff --git a/x-pack/plugins/lens/common/expressions/xy_chart/xy_args.ts b/x-pack/plugins/lens/common/expressions/xy_chart/xy_args.ts deleted file mode 100644 index 1334c1149f47b..0000000000000 --- a/x-pack/plugins/lens/common/expressions/xy_chart/xy_args.ts +++ /dev/null @@ -1,41 +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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { AxisExtentConfigResult, AxisTitlesVisibilityConfigResult } from './axis_config'; -import type { FittingFunction } from './fitting_function'; -import type { GridlinesConfigResult } from './grid_lines_config'; -import type { DataLayerArgs } from './layer_config'; -import type { LegendConfigResult } from './legend_config'; -import type { TickLabelsConfigResult } from './tick_labels_config'; -import type { LabelsOrientationConfigResult } from './labels_orientation_config'; -import type { ValueLabelConfig } from '../../types'; - -export type XYCurveType = 'LINEAR' | 'CURVE_MONOTONE_X'; - -// Arguments to XY chart expression, with computed properties -export interface XYArgs { - title?: string; - description?: string; - xTitle: string; - yTitle: string; - yRightTitle: string; - yLeftExtent: AxisExtentConfigResult; - yRightExtent: AxisExtentConfigResult; - legend: LegendConfigResult; - valueLabels: ValueLabelConfig; - layers: DataLayerArgs[]; - fittingFunction?: FittingFunction; - axisTitlesVisibilitySettings?: AxisTitlesVisibilityConfigResult; - tickLabelsVisibilitySettings?: TickLabelsConfigResult; - gridlinesVisibilitySettings?: GridlinesConfigResult; - labelsOrientation?: LabelsOrientationConfigResult; - curveType?: XYCurveType; - fillOpacity?: number; - hideEndzones?: boolean; - valuesInLegend?: boolean; - ariaLabel?: string; -} diff --git a/x-pack/plugins/lens/common/expressions/xy_chart/xy_chart.ts b/x-pack/plugins/lens/common/expressions/xy_chart/xy_chart.ts deleted file mode 100644 index d3fb2fe7a6917..0000000000000 --- a/x-pack/plugins/lens/common/expressions/xy_chart/xy_chart.ts +++ /dev/null @@ -1,175 +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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import { i18n } from '@kbn/i18n'; -import type { ExpressionFunctionDefinition } from '../../../../../../src/plugins/expressions/common'; -import type { ExpressionValueSearchContext } from '../../../../../../src/plugins/data/common'; -import type { LensMultiTable } from '../../types'; -import type { XYArgs } from './xy_args'; -import { fittingFunctionDefinitions } from './fitting_function'; - -export interface XYChartProps { - data: LensMultiTable; - args: XYArgs; -} - -export interface XYRender { - type: 'render'; - as: 'lens_xy_chart_renderer'; - value: XYChartProps; -} - -export const xyChart: ExpressionFunctionDefinition< - 'lens_xy_chart', - LensMultiTable | ExpressionValueSearchContext | null, - XYArgs, - XYRender -> = { - name: 'lens_xy_chart', - type: 'render', - inputTypes: ['lens_multitable', 'kibana_context', 'null'], - help: i18n.translate('xpack.lens.xyChart.help', { - defaultMessage: 'An X/Y chart', - }), - args: { - title: { - types: ['string'], - help: 'The chart title.', - }, - description: { - types: ['string'], - help: '', - }, - xTitle: { - types: ['string'], - help: i18n.translate('xpack.lens.xyChart.xTitle.help', { - defaultMessage: 'X axis title', - }), - }, - yTitle: { - types: ['string'], - help: i18n.translate('xpack.lens.xyChart.yLeftTitle.help', { - defaultMessage: 'Y left axis title', - }), - }, - yRightTitle: { - types: ['string'], - help: i18n.translate('xpack.lens.xyChart.yRightTitle.help', { - defaultMessage: 'Y right axis title', - }), - }, - yLeftExtent: { - types: ['lens_xy_axisExtentConfig'], - help: i18n.translate('xpack.lens.xyChart.yLeftExtent.help', { - defaultMessage: 'Y left axis extents', - }), - }, - yRightExtent: { - types: ['lens_xy_axisExtentConfig'], - help: i18n.translate('xpack.lens.xyChart.yRightExtent.help', { - defaultMessage: 'Y right axis extents', - }), - }, - legend: { - types: ['lens_xy_legendConfig'], - help: i18n.translate('xpack.lens.xyChart.legend.help', { - defaultMessage: 'Configure the chart legend.', - }), - }, - fittingFunction: { - types: ['string'], - options: [...fittingFunctionDefinitions.map(({ id }) => id)], - help: i18n.translate('xpack.lens.xyChart.fittingFunction.help', { - defaultMessage: 'Define how missing values are treated', - }), - }, - valueLabels: { - types: ['string'], - options: ['hide', 'inside'], - help: '', - }, - tickLabelsVisibilitySettings: { - types: ['lens_xy_tickLabelsConfig'], - help: i18n.translate('xpack.lens.xyChart.tickLabelsSettings.help', { - defaultMessage: 'Show x and y axes tick labels', - }), - }, - labelsOrientation: { - types: ['lens_xy_labelsOrientationConfig'], - help: i18n.translate('xpack.lens.xyChart.labelsOrientation.help', { - defaultMessage: 'Defines the rotation of the axis labels', - }), - }, - gridlinesVisibilitySettings: { - types: ['lens_xy_gridlinesConfig'], - help: i18n.translate('xpack.lens.xyChart.gridlinesSettings.help', { - defaultMessage: 'Show x and y axes gridlines', - }), - }, - axisTitlesVisibilitySettings: { - types: ['lens_xy_axisTitlesVisibilityConfig'], - help: i18n.translate('xpack.lens.xyChart.axisTitlesSettings.help', { - defaultMessage: 'Show x and y axes titles', - }), - }, - layers: { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - types: ['lens_xy_data_layer', 'lens_xy_referenceLine_layer'] as any, - help: 'Layers of visual series', - multi: true, - }, - curveType: { - types: ['string'], - options: ['LINEAR', 'CURVE_MONOTONE_X'], - help: i18n.translate('xpack.lens.xyChart.curveType.help', { - defaultMessage: 'Define how curve type is rendered for a line chart', - }), - }, - fillOpacity: { - types: ['number'], - help: i18n.translate('xpack.lens.xyChart.fillOpacity.help', { - defaultMessage: 'Define the area chart fill opacity', - }), - }, - hideEndzones: { - types: ['boolean'], - default: false, - help: i18n.translate('xpack.lens.xyChart.hideEndzones.help', { - defaultMessage: 'Hide endzone markers for partial data', - }), - }, - valuesInLegend: { - types: ['boolean'], - default: false, - help: i18n.translate('xpack.lens.xyChart.valuesInLegend.help', { - defaultMessage: 'Show values in legend', - }), - }, - ariaLabel: { - types: ['string'], - help: i18n.translate('xpack.lens.xyChart.ariaLabel.help', { - defaultMessage: 'Specifies the aria label of the xy chart', - }), - required: false, - }, - }, - fn(data: LensMultiTable, args: XYArgs, handlers) { - return { - type: 'render', - as: 'lens_xy_chart_renderer', - value: { - data, - args: { - ...args, - ariaLabel: - args.ariaLabel ?? - (handlers.variables?.embeddableTitle as string) ?? - handlers.getExecutionContext?.()?.description, - }, - }, - }; - }, -}; diff --git a/x-pack/plugins/lens/kibana.json b/x-pack/plugins/lens/kibana.json index 17a58a0f96770..34edbd42a32e7 100644 --- a/x-pack/plugins/lens/kibana.json +++ b/x-pack/plugins/lens/kibana.json @@ -24,6 +24,7 @@ "expressionHeatmap" ], "optionalPlugins": [ + "expressionXY", "usageCollection", "taskManager", "globalSearch", diff --git a/x-pack/plugins/lens/public/shared_components/axis_title_settings.tsx b/x-pack/plugins/lens/public/shared_components/axis_title_settings.tsx index f54b07905b94c..a3b5e8311a704 100644 --- a/x-pack/plugins/lens/public/shared_components/axis_title_settings.tsx +++ b/x-pack/plugins/lens/public/shared_components/axis_title_settings.tsx @@ -15,7 +15,7 @@ import { EuiFieldText, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { AxesSettingsConfig } from '../../common/expressions'; +import { AxesSettingsConfig } from '../../../../../src/plugins/chart_expressions/expression_xy/common'; import { useDebouncedValue } from './'; type AxesSettingsConfigKeys = keyof AxesSettingsConfig; diff --git a/x-pack/plugins/lens/public/xy_visualization/axes_configuration.test.ts b/x-pack/plugins/lens/public/xy_visualization/axes_configuration.test.ts index ac3e224663ce4..18cd670ec2ad6 100644 --- a/x-pack/plugins/lens/public/xy_visualization/axes_configuration.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/axes_configuration.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { DataLayerArgs } from '../../common/expressions'; +import { DataLayerConfigResult } from '../../../../../src/plugins/chart_expressions/expression_xy/common'; import { layerTypes } from '../../common'; import { Datatable } from '../../../../../src/plugins/expressions/public'; import { getAxesConfiguration } from './axes_configuration'; @@ -219,7 +219,8 @@ describe('axes_configuration', () => { }, }; - const sampleLayer: DataLayerArgs = { + const sampleLayer: DataLayerConfigResult = { + type: 'lens_xy_data_layer', layerId: 'first', layerType: layerTypes.DATA, seriesType: 'line', @@ -271,7 +272,12 @@ describe('axes_configuration', () => { it('should map right series to right axis', () => { const formatFactory = jest.fn(); const groups = getAxesConfiguration( - [{ ...sampleLayer, yConfig: [{ forAccessor: 'yAccessorId', axisMode: 'right' }] }], + [ + { + ...sampleLayer, + yConfig: [{ type: 'lens_xy_yConfig', forAccessor: 'yAccessorId', axisMode: 'right' }], + }, + ], false, tables, formatFactory @@ -289,7 +295,7 @@ describe('axes_configuration', () => { { ...sampleLayer, accessors: ['yAccessorId', 'yAccessorId3', 'yAccessorId4'], - yConfig: [{ forAccessor: 'yAccessorId', axisMode: 'right' }], + yConfig: [{ type: 'lens_xy_yConfig', forAccessor: 'yAccessorId', axisMode: 'right' }], }, ], false, @@ -313,7 +319,7 @@ describe('axes_configuration', () => { { ...sampleLayer, accessors: ['yAccessorId', 'yAccessorId3', 'yAccessorId4'], - yConfig: [{ forAccessor: 'yAccessorId', axisMode: 'right' }], + yConfig: [{ type: 'lens_xy_yConfig', forAccessor: 'yAccessorId', axisMode: 'right' }], }, ], false, diff --git a/x-pack/plugins/lens/public/xy_visualization/axes_configuration.ts b/x-pack/plugins/lens/public/xy_visualization/axes_configuration.ts index 7adc803f31e9f..b032e7359d9fc 100644 --- a/x-pack/plugins/lens/public/xy_visualization/axes_configuration.ts +++ b/x-pack/plugins/lens/public/xy_visualization/axes_configuration.ts @@ -6,7 +6,10 @@ */ import { FormatFactory } from '../../common'; -import { AxisExtentConfig, XYDataLayerConfig } from '../../common/expressions'; +import { + AxisExtentConfig, + DataLayerConfigResult, +} from '../../../../../src/plugins/chart_expressions/expression_xy/common'; import { Datatable } from '../../../../../src/plugins/expressions/public'; import type { IFieldFormat, @@ -33,7 +36,10 @@ export function isFormatterCompatible( return formatter1.id === formatter2.id; } -export function groupAxesByType(layers: XYDataLayerConfig[], tables?: Record) { +export function groupAxesByType( + layers: DataLayerConfigResult[], + tables?: Record +) { const series: { auto: FormattedMetric[]; left: FormattedMetric[]; @@ -97,7 +103,7 @@ export function groupAxesByType(layers: XYDataLayerConfig[], tables?: Record, formatFactory?: FormatFactory diff --git a/x-pack/plugins/lens/public/xy_visualization/color_assignment.test.ts b/x-pack/plugins/lens/public/xy_visualization/color_assignment.test.ts index 9b29401d72a95..aa9e73c712fb1 100644 --- a/x-pack/plugins/lens/public/xy_visualization/color_assignment.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/color_assignment.test.ts @@ -7,12 +7,13 @@ import { getColorAssignments } from './color_assignment'; import type { FormatFactory, LensMultiTable } from '../../common'; -import type { DataLayerArgs } from '../../common/expressions'; +import type { DataLayerConfigResult } from '../../../../../src/plugins/chart_expressions/expression_xy/common'; import { layerTypes } from '../../common'; describe('color_assignment', () => { - const layers: DataLayerArgs[] = [ + const layers: DataLayerConfigResult[] = [ { + type: 'lens_xy_data_layer', yScaleType: 'linear', xScaleType: 'linear', isHistogram: true, @@ -24,6 +25,7 @@ describe('color_assignment', () => { accessors: ['y1', 'y2'], }, { + type: 'lens_xy_data_layer', yScaleType: 'linear', xScaleType: 'linear', isHistogram: true, diff --git a/x-pack/plugins/lens/public/xy_visualization/color_assignment.ts b/x-pack/plugins/lens/public/xy_visualization/color_assignment.ts index 82c1106e72a08..5efe5ab798e5a 100644 --- a/x-pack/plugins/lens/public/xy_visualization/color_assignment.ts +++ b/x-pack/plugins/lens/public/xy_visualization/color_assignment.ts @@ -11,39 +11,35 @@ import type { Datatable } from 'src/plugins/expressions'; import { euiLightVars } from '@kbn/ui-theme'; import type { AccessorConfig, FramePublicAPI } from '../types'; import { getColumnToLabelMap } from './state_helpers'; -import { FormatFactory, LayerType } from '../../common'; -import type { XYLayerConfig } from '../../common/expressions'; +import { FormatFactory } from '../../common'; import { isDataLayer, isReferenceLayer } from './visualization_helpers'; +import { + DataLayerConfigResult, + ReferenceLineLayerConfigResult, + XYLayerConfig, +} from '../../../../../src/plugins/chart_expressions/expression_xy/common'; const isPrimitive = (value: unknown): boolean => value != null && typeof value !== 'object'; -interface LayerColorConfig { - palette?: PaletteOutput; - splitAccessor?: string; - accessors: string[]; - layerId: string; - layerType: LayerType; -} - export const defaultReferenceLineColor = euiLightVars.euiColorDarkShade; export type ColorAssignments = Record< string, { totalSeriesCount: number; - getRank(sortedLayer: LayerColorConfig, seriesKey: string, yAccessor: string): number; + getRank(sortedLayer: DataLayerConfigResult, seriesKey: string, yAccessor: string): number; } >; export function getColorAssignments( - layers: LayerColorConfig[], + layers: XYLayerConfig[], data: { tables: Record }, formatFactory: FormatFactory ): ColorAssignments { - const layersPerPalette: Record = {}; + const layersPerPalette: Record = {}; layers - .filter((layer) => isDataLayer(layer)) + .filter((layer): layer is DataLayerConfigResult => isDataLayer(layer)) .forEach((layer) => { const palette = layer.palette?.name || 'default'; if (!layersPerPalette[palette]) { @@ -82,7 +78,7 @@ export function getColorAssignments( ); return { totalSeriesCount, - getRank(sortedLayer: LayerColorConfig, seriesKey: string, yAccessor: string) { + getRank(sortedLayer: DataLayerConfigResult, seriesKey: string, yAccessor: string) { const layerIndex = paletteLayers.findIndex((l) => sortedLayer.layerId === l.layerId); const currentSeriesPerLayer = seriesPerLayer[layerIndex]; const splitRank = currentSeriesPerLayer.splits.indexOf(seriesKey); @@ -102,7 +98,7 @@ export function getColorAssignments( }); } -const getReferenceLineAccessorColorConfig = (layer: XYLayerConfig) => { +const getReferenceLineAccessorColorConfig = (layer: ReferenceLineLayerConfigResult) => { return layer.accessors.map((accessor) => { const currentYConfig = layer.yConfig?.find((yConfig) => yConfig.forAccessor === accessor); return { diff --git a/x-pack/plugins/lens/public/xy_visualization/expression.test.tsx b/x-pack/plugins/lens/public/xy_visualization/expression.test.tsx deleted file mode 100644 index 654a0f1b94a14..0000000000000 --- a/x-pack/plugins/lens/public/xy_visualization/expression.test.tsx +++ /dev/null @@ -1,2993 +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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { - AreaSeries, - Axis, - BarSeries, - Position, - LineSeries, - Settings, - ScaleType, - GeometryValue, - XYChartSeriesIdentifier, - SeriesNameFn, - Fit, - HorizontalAlignment, - VerticalAlignment, - LayoutDirection, -} from '@elastic/charts'; -import { PaletteOutput } from 'src/plugins/charts/public'; -import { calculateMinInterval, XYChart, XYChartRenderProps } from './expression'; -import type { LensMultiTable } from '../../common'; -import { layerTypes } from '../../common'; -import { xyChart } from '../../common/expressions'; -import { - dataLayerConfig, - legendConfig, - tickLabelsConfig, - gridlinesConfig, - XYArgs, - LegendConfig, - DataLayerArgs, - AxesSettingsConfig, - XYChartProps, - labelsOrientationConfig, - LabelsOrientationConfig, -} from '../../common/expressions'; -import { Datatable, DatatableRow } from '../../../../../src/plugins/expressions/public'; -import React from 'react'; -import { shallow } from 'enzyme'; -import { createMockExecutionContext } from '../../../../../src/plugins/expressions/common/mocks'; -import { mountWithIntl } from '@kbn/test-jest-helpers'; -import { chartPluginMock } from '../../../../../src/plugins/charts/public/mocks'; -import { EmptyPlaceholder } from '../../../../../src/plugins/charts/public'; -import { XyEndzones } from './x_domain'; - -const onClickValue = jest.fn(); -const onSelectRange = jest.fn(); - -const chartSetupContract = chartPluginMock.createSetupContract(); -const chartStartContract = chartPluginMock.createStartContract(); - -const chartsThemeService = chartSetupContract.theme; -const chartsActiveCursorService = chartStartContract.activeCursor; - -const paletteService = chartPluginMock.createPaletteRegistry(); - -const mockPaletteOutput: PaletteOutput = { - type: 'palette', - name: 'mock', - params: {}, -}; - -const dateHistogramData: LensMultiTable = { - type: 'lens_multitable', - tables: { - timeLayer: { - type: 'datatable', - rows: [ - { - xAccessorId: 1585758120000, - splitAccessorId: "Men's Clothing", - yAccessorId: 1, - }, - { - xAccessorId: 1585758360000, - splitAccessorId: "Women's Accessories", - yAccessorId: 1, - }, - { - xAccessorId: 1585758360000, - splitAccessorId: "Women's Clothing", - yAccessorId: 1, - }, - { - xAccessorId: 1585759380000, - splitAccessorId: "Men's Clothing", - yAccessorId: 1, - }, - { - xAccessorId: 1585759380000, - splitAccessorId: "Men's Shoes", - yAccessorId: 1, - }, - { - xAccessorId: 1585759380000, - splitAccessorId: "Women's Clothing", - yAccessorId: 1, - }, - { - xAccessorId: 1585760700000, - splitAccessorId: "Men's Clothing", - yAccessorId: 1, - }, - { - xAccessorId: 1585760760000, - splitAccessorId: "Men's Clothing", - yAccessorId: 1, - }, - { - xAccessorId: 1585760760000, - splitAccessorId: "Men's Shoes", - yAccessorId: 1, - }, - { - xAccessorId: 1585761120000, - splitAccessorId: "Men's Shoes", - yAccessorId: 1, - }, - ], - columns: [ - { - id: 'xAccessorId', - name: 'order_date per minute', - meta: { - type: 'date', - field: 'order_date', - source: 'esaggs', - index: 'indexPatternId', - sourceParams: { - indexPatternId: 'indexPatternId', - type: 'date_histogram', - appliedTimeRange: { - from: '2020-04-01T16:14:16.246Z', - to: '2020-04-01T17:15:41.263Z', - }, - params: { - field: 'order_date', - timeRange: { from: '2020-04-01T16:14:16.246Z', to: '2020-04-01T17:15:41.263Z' }, - useNormalizedEsInterval: true, - scaleMetricValues: false, - interval: '1m', - drop_partials: false, - min_doc_count: 0, - extended_bounds: {}, - }, - }, - params: { id: 'date', params: { pattern: 'HH:mm' } }, - }, - }, - { - id: 'splitAccessorId', - name: 'Top values of category.keyword', - meta: { - type: 'string', - field: 'category.keyword', - source: 'esaggs', - index: 'indexPatternId', - sourceParams: { - indexPatternId: 'indexPatternId', - type: 'terms', - params: { - field: 'category.keyword', - orderBy: 'yAccessorId', - order: 'desc', - size: 3, - otherBucket: false, - otherBucketLabel: 'Other', - missingBucket: false, - missingBucketLabel: 'Missing', - }, - }, - params: { - id: 'terms', - params: { - id: 'string', - otherBucketLabel: 'Other', - missingBucketLabel: 'Missing', - parsedUrl: { - origin: 'http://localhost:5601', - pathname: '/jiy/app/kibana', - basePath: '/jiy', - }, - }, - }, - }, - }, - { - id: 'yAccessorId', - name: 'Count of records', - meta: { - type: 'number', - source: 'esaggs', - index: 'indexPatternId', - sourceParams: { - indexPatternId: 'indexPatternId', - params: {}, - }, - params: { id: 'number' }, - }, - }, - ], - }, - }, - dateRange: { - fromDate: new Date('2020-04-01T16:14:16.246Z'), - toDate: new Date('2020-04-01T17:15:41.263Z'), - }, -}; - -const dateHistogramLayer: DataLayerArgs = { - layerId: 'timeLayer', - layerType: layerTypes.DATA, - hide: false, - xAccessor: 'xAccessorId', - yScaleType: 'linear', - xScaleType: 'time', - isHistogram: true, - splitAccessor: 'splitAccessorId', - seriesType: 'bar_stacked', - accessors: ['yAccessorId'], - palette: mockPaletteOutput, -}; - -const createSampleDatatableWithRows = (rows: DatatableRow[]): Datatable => ({ - type: 'datatable', - columns: [ - { - id: 'a', - name: 'a', - meta: { type: 'number', params: { id: 'number', params: { pattern: '0,0.000' } } }, - }, - { - id: 'b', - name: 'b', - meta: { type: 'number', params: { id: 'number', params: { pattern: '000,0' } } }, - }, - { - id: 'c', - name: 'c', - meta: { - type: 'date', - field: 'order_date', - sourceParams: { type: 'date-histogram', params: { interval: 'auto' } }, - params: { id: 'string' }, - }, - }, - { id: 'd', name: 'ColD', meta: { type: 'string' } }, - ], - rows, -}); - -const sampleLayer: DataLayerArgs = { - layerId: 'first', - layerType: layerTypes.DATA, - seriesType: 'line', - xAccessor: 'c', - accessors: ['a', 'b'], - splitAccessor: 'd', - columnToLabel: '{"a": "Label A", "b": "Label B", "d": "Label D"}', - xScaleType: 'ordinal', - yScaleType: 'linear', - isHistogram: false, - palette: mockPaletteOutput, -}; - -const createArgsWithLayers = (layers: DataLayerArgs[] = [sampleLayer]): XYArgs => ({ - xTitle: '', - yTitle: '', - yRightTitle: '', - legend: { - type: 'lens_xy_legendConfig', - isVisible: false, - position: Position.Top, - }, - valueLabels: 'hide', - valuesInLegend: false, - axisTitlesVisibilitySettings: { - type: 'lens_xy_axisTitlesVisibilityConfig', - x: true, - yLeft: true, - yRight: true, - }, - tickLabelsVisibilitySettings: { - type: 'lens_xy_tickLabelsConfig', - x: true, - yLeft: false, - yRight: false, - }, - labelsOrientation: { - type: 'lens_xy_labelsOrientationConfig', - x: 0, - yLeft: -90, - yRight: -45, - }, - gridlinesVisibilitySettings: { - type: 'lens_xy_gridlinesConfig', - x: true, - yLeft: false, - yRight: false, - }, - yLeftExtent: { - mode: 'full', - type: 'lens_xy_axisExtentConfig', - }, - yRightExtent: { - mode: 'full', - type: 'lens_xy_axisExtentConfig', - }, - layers, -}); - -function sampleArgs() { - const data: LensMultiTable = { - type: 'lens_multitable', - tables: { - first: createSampleDatatableWithRows([ - { a: 1, b: 2, c: 'I', d: 'Foo' }, - { a: 1, b: 5, c: 'J', d: 'Bar' }, - ]), - }, - dateRange: { - fromDate: new Date('2019-01-02T05:00:00.000Z'), - toDate: new Date('2019-01-03T05:00:00.000Z'), - }, - }; - - const args: XYArgs = createArgsWithLayers(); - - return { data, args }; -} - -function sampleArgsWithReferenceLine(value: number = 150) { - const { data, args } = sampleArgs(); - - return { - data: { - ...data, - tables: { - ...data.tables, - referenceLine: { - type: 'datatable', - columns: [ - { - id: 'referenceLine-a', - meta: { params: { id: 'number' }, type: 'number' }, - name: 'Static value', - }, - ], - rows: [{ 'referenceLine-a': value }], - }, - }, - } as LensMultiTable, - args: { - ...args, - layers: [ - ...args.layers, - { - layerType: layerTypes.REFERENCELINE, - accessors: ['referenceLine-a'], - layerId: 'referenceLine', - seriesType: 'line', - xScaleType: 'linear', - yScaleType: 'linear', - palette: mockPaletteOutput, - isHistogram: false, - hide: true, - yConfig: [{ axisMode: 'left', forAccessor: 'referenceLine-a', type: 'lens_xy_yConfig' }], - }, - ], - } as XYArgs, - }; -} - -describe('xy_expression', () => { - describe('configs', () => { - test('legendConfig produces the correct arguments', () => { - const args: LegendConfig = { - isVisible: true, - position: Position.Left, - }; - - const result = legendConfig.fn(null, args, createMockExecutionContext()); - - expect(result).toEqual({ - type: 'lens_xy_legendConfig', - ...args, - }); - }); - - test('dataLayerConfig produces the correct arguments', () => { - const args: DataLayerArgs = { - layerId: 'first', - layerType: layerTypes.DATA, - seriesType: 'line', - xAccessor: 'c', - accessors: ['a', 'b'], - splitAccessor: 'd', - xScaleType: 'linear', - yScaleType: 'linear', - isHistogram: false, - palette: mockPaletteOutput, - }; - - const result = dataLayerConfig.fn(null, args, createMockExecutionContext()); - - expect(result).toEqual({ - type: 'lens_xy_data_layer', - ...args, - }); - }); - }); - - test('tickLabelsConfig produces the correct arguments', () => { - const args: AxesSettingsConfig = { - x: true, - yLeft: false, - yRight: false, - }; - - const result = tickLabelsConfig.fn(null, args, createMockExecutionContext()); - - expect(result).toEqual({ - type: 'lens_xy_tickLabelsConfig', - ...args, - }); - }); - - test('gridlinesConfig produces the correct arguments', () => { - const args: AxesSettingsConfig = { - x: true, - yLeft: false, - yRight: false, - }; - - const result = gridlinesConfig.fn(null, args, createMockExecutionContext()); - - expect(result).toEqual({ - type: 'lens_xy_gridlinesConfig', - ...args, - }); - }); - - test('labelsOrientationConfig produces the correct arguments', () => { - const args: LabelsOrientationConfig = { - x: 0, - yLeft: -90, - yRight: -45, - }; - - const result = labelsOrientationConfig.fn(null, args, createMockExecutionContext()); - - expect(result).toEqual({ - type: 'lens_xy_labelsOrientationConfig', - ...args, - }); - }); - - describe('xyChart', () => { - test('it renders with the specified data and args', () => { - const { data, args } = sampleArgs(); - const result = xyChart.fn(data, args, createMockExecutionContext()); - - expect(result).toEqual({ - type: 'render', - as: 'lens_xy_chart_renderer', - value: { data, args }, - }); - }); - }); - - describe('XYChart component', () => { - let getFormatSpy: jest.Mock; - let convertSpy: jest.Mock; - let defaultProps: Omit; - - const dataWithoutFormats: LensMultiTable = { - type: 'lens_multitable', - tables: { - first: { - type: 'datatable', - columns: [ - { id: 'a', name: 'a', meta: { type: 'number' } }, - { id: 'b', name: 'b', meta: { type: 'number' } }, - { id: 'c', name: 'c', meta: { type: 'string' } }, - { id: 'd', name: 'd', meta: { type: 'string' } }, - ], - rows: [ - { a: 1, b: 2, c: 'I', d: 'Row 1' }, - { a: 1, b: 5, c: 'J', d: 'Row 2' }, - ], - }, - }, - }; - const dataWithFormats: LensMultiTable = { - type: 'lens_multitable', - tables: { - first: { - type: 'datatable', - columns: [ - { id: 'a', name: 'a', meta: { type: 'number' } }, - { id: 'b', name: 'b', meta: { type: 'number' } }, - { id: 'c', name: 'c', meta: { type: 'string' } }, - { id: 'd', name: 'd', meta: { type: 'string', params: { id: 'custom' } } }, - ], - rows: [ - { a: 1, b: 2, c: 'I', d: 'Row 1' }, - { a: 1, b: 5, c: 'J', d: 'Row 2' }, - ], - }, - }, - }; - - const getRenderedComponent = (data: LensMultiTable, args: XYArgs) => { - return shallow(); - }; - - beforeEach(() => { - convertSpy = jest.fn((x) => x); - getFormatSpy = jest.fn(); - getFormatSpy.mockReturnValue({ convert: convertSpy }); - - defaultProps = { - formatFactory: getFormatSpy, - timeZone: 'UTC', - renderMode: 'view', - chartsThemeService, - chartsActiveCursorService, - paletteService, - minInterval: 50, - onClickValue, - onSelectRange, - syncColors: false, - useLegacyTimeAxis: false, - }; - }); - - test('it renders line', () => { - const { data, args } = sampleArgs(); - - const component = shallow( - - ); - expect(component).toMatchSnapshot(); - expect(component.find(LineSeries)).toHaveLength(2); - expect(component.find(LineSeries).at(0).prop('yAccessors')).toEqual(['a']); - expect(component.find(LineSeries).at(1).prop('yAccessors')).toEqual(['b']); - }); - - describe('date range', () => { - const timeSampleLayer: DataLayerArgs = { - layerId: 'first', - layerType: layerTypes.DATA, - seriesType: 'line', - xAccessor: 'c', - accessors: ['a', 'b'], - splitAccessor: 'd', - columnToLabel: '{"a": "Label A", "b": "Label B", "d": "Label D"}', - xScaleType: 'time', - yScaleType: 'linear', - isHistogram: false, - palette: mockPaletteOutput, - }; - const multiLayerArgs = createArgsWithLayers([ - timeSampleLayer, - { - ...timeSampleLayer, - layerId: 'second', - seriesType: 'bar', - xScaleType: 'time', - }, - ]); - test('it uses the full date range', () => { - const { data, args } = sampleArgs(); - - const component = shallow( - - c.id !== 'c' - ? c - : { - ...c, - meta: { - type: 'date', - source: 'esaggs', - sourceParams: { - type: 'date_histogram', - params: {}, - appliedTimeRange: { - from: '2019-01-02T05:00:00.000Z', - to: '2019-01-03T05:00:00.000Z', - }, - }, - }, - } - ), - }, - }, - }} - args={{ - ...args, - layers: [{ ...args.layers[0], seriesType: 'line', xScaleType: 'time' }], - }} - minInterval={undefined} - /> - ); - expect(component.find(Settings).prop('xDomain')).toMatchInlineSnapshot(` - Object { - "max": 1546491600000, - "min": 1546405200000, - "minInterval": undefined, - } - `); - }); - - test('it uses passed in minInterval', () => { - const data: LensMultiTable = { - type: 'lens_multitable', - tables: { - first: createSampleDatatableWithRows([{ a: 1, b: 2, c: 'I', d: 'Foo' }]), - second: createSampleDatatableWithRows([]), - }, - }; - - const component = shallow(); - - // real auto interval is 30mins = 1800000 - expect(component.find(Settings).prop('xDomain')).toMatchInlineSnapshot(` - Object { - "max": NaN, - "min": NaN, - "minInterval": 50, - } - `); - }); - - describe('axis time', () => { - const defaultTimeLayer: DataLayerArgs = { - layerId: 'first', - layerType: layerTypes.DATA, - seriesType: 'line', - xAccessor: 'c', - accessors: ['a', 'b'], - splitAccessor: 'd', - columnToLabel: '{"a": "Label A", "b": "Label B", "d": "Label D"}', - xScaleType: 'time', - yScaleType: 'linear', - isHistogram: true, - palette: mockPaletteOutput, - }; - test('it should disable the new time axis for a line time layer when isHistogram is set to false', () => { - const { data } = sampleArgs(); - - const instance = shallow( - - ); - - const axisStyle = instance.find(Axis).first().prop('timeAxisLayerCount'); - - expect(axisStyle).toBe(0); - }); - test('it should enable the new time axis for a line time layer when isHistogram is set to true', () => { - const { data } = sampleArgs(); - const timeLayerArgs = createArgsWithLayers([defaultTimeLayer]); - - const instance = shallow( - - ); - - const axisStyle = instance.find(Axis).first().prop('timeAxisLayerCount'); - - expect(axisStyle).toBe(3); - }); - test('it should disable the new time axis for a vertical bar with break down dimension', () => { - const { data } = sampleArgs(); - const timeLayer: DataLayerArgs = { - ...defaultTimeLayer, - seriesType: 'bar', - }; - const timeLayerArgs = createArgsWithLayers([timeLayer]); - - const instance = shallow( - - ); - - const axisStyle = instance.find(Axis).first().prop('timeAxisLayerCount'); - - expect(axisStyle).toBe(0); - }); - - test('it should enable the new time axis for a stacked vertical bar with break down dimension', () => { - const { data } = sampleArgs(); - const timeLayer: DataLayerArgs = { - ...defaultTimeLayer, - seriesType: 'bar_stacked', - }; - const timeLayerArgs = createArgsWithLayers([timeLayer]); - - const instance = shallow( - - ); - - const axisStyle = instance.find(Axis).first().prop('timeAxisLayerCount'); - - expect(axisStyle).toBe(3); - }); - }); - describe('endzones', () => { - const { args } = sampleArgs(); - const table = createSampleDatatableWithRows([ - { a: 1, b: 2, c: new Date('2021-04-22').valueOf(), d: 'Foo' }, - { a: 1, b: 2, c: new Date('2021-04-23').valueOf(), d: 'Foo' }, - { a: 1, b: 2, c: new Date('2021-04-24').valueOf(), d: 'Foo' }, - ]); - const data: LensMultiTable = { - type: 'lens_multitable', - tables: { - first: { - ...table, - columns: table.columns.map((c) => - c.id !== 'c' - ? c - : { - ...c, - meta: { - type: 'date', - source: 'esaggs', - sourceParams: { - type: 'date_histogram', - params: {}, - appliedTimeRange: { - from: '2021-04-22T12:00:00.000Z', - to: '2021-04-24T12:00:00.000Z', - }, - }, - }, - } - ), - }, - }, - dateRange: { - // first and last bucket are partial - fromDate: new Date('2021-04-22T12:00:00.000Z'), - toDate: new Date('2021-04-24T12:00:00.000Z'), - }, - }; - const timeArgs: XYArgs = { - ...args, - layers: [ - { - ...args.layers[0], - seriesType: 'line', - xScaleType: 'time', - isHistogram: true, - splitAccessor: undefined, - }, - ], - }; - - test('it extends interval if data is exceeding it', () => { - const component = shallow( - - ); - - expect(component.find(Settings).prop('xDomain')).toEqual({ - // shortened to 24th midnight (elastic-charts automatically adds one min interval) - max: new Date('2021-04-24').valueOf(), - // extended to 22nd midnight because of first bucket - min: new Date('2021-04-22').valueOf(), - minInterval: 24 * 60 * 60 * 1000, - }); - }); - - test('it renders endzone component bridging gap between domain and extended domain', () => { - const component = shallow( - - ); - - expect(component.find(XyEndzones).dive().find('Endzones').props()).toEqual( - expect.objectContaining({ - domainStart: new Date('2021-04-22T12:00:00.000Z').valueOf(), - domainEnd: new Date('2021-04-24T12:00:00.000Z').valueOf(), - domainMin: new Date('2021-04-22').valueOf(), - domainMax: new Date('2021-04-24').valueOf(), - }) - ); - }); - - test('should pass enabled histogram mode and min interval to endzones component', () => { - const component = shallow( - - ); - - expect(component.find(XyEndzones).dive().find('Endzones').props()).toEqual( - expect.objectContaining({ - interval: 24 * 60 * 60 * 1000, - isFullBin: false, - }) - ); - }); - - test('should pass disabled histogram mode and min interval to endzones component', () => { - const component = shallow( - - ); - - expect(component.find(XyEndzones).dive().find('Endzones').props()).toEqual( - expect.objectContaining({ - interval: 24 * 60 * 60 * 1000, - isFullBin: true, - }) - ); - }); - - test('it does not render endzones if disabled via settings', () => { - const component = shallow( - - ); - - expect(component.find(XyEndzones).length).toEqual(0); - }); - }); - }); - - describe('y axis extents', () => { - test('it passes custom y axis extents to elastic-charts axis spec', () => { - const { data, args } = sampleArgs(); - - const component = shallow( - - ); - expect(component.find(Axis).find('[id="left"]').prop('domain')).toEqual({ - fit: false, - min: 123, - max: 456, - }); - }); - - test('it passes fit to bounds y axis extents to elastic-charts axis spec', () => { - const { data, args } = sampleArgs(); - - const component = shallow( - - ); - expect(component.find(Axis).find('[id="left"]').prop('domain')).toEqual({ - fit: true, - min: NaN, - max: NaN, - }); - }); - - test('it does not allow fit for area chart', () => { - const { data, args } = sampleArgs(); - - const component = shallow( - - ); - expect(component.find(Axis).find('[id="left"]').prop('domain')).toEqual({ - fit: false, - min: NaN, - max: NaN, - }); - }); - - test('it does not allow positive lower bound for bar', () => { - const { data, args } = sampleArgs(); - - const component = shallow( - - ); - expect(component.find(Axis).find('[id="left"]').prop('domain')).toEqual({ - fit: false, - min: NaN, - max: NaN, - }); - }); - - test('it does include referenceLine values when in full extent mode', () => { - const { data, args } = sampleArgsWithReferenceLine(); - - const component = shallow(); - expect(component.find(Axis).find('[id="left"]').prop('domain')).toEqual({ - fit: false, - min: 0, - max: 150, - }); - }); - - test('it should ignore referenceLine values when set to custom extents', () => { - const { data, args } = sampleArgsWithReferenceLine(); - - const component = shallow( - - ); - expect(component.find(Axis).find('[id="left"]').prop('domain')).toEqual({ - fit: false, - min: 123, - max: 456, - }); - }); - - test('it should work for negative values in referenceLines', () => { - const { data, args } = sampleArgsWithReferenceLine(-150); - - const component = shallow(); - expect(component.find(Axis).find('[id="left"]').prop('domain')).toEqual({ - fit: false, - min: -150, - max: 5, - }); - }); - }); - - test('it has xDomain undefined if the x is not a time scale or a histogram', () => { - const { data, args } = sampleArgs(); - - const component = shallow( - - ); - const xDomain = component.find(Settings).prop('xDomain'); - expect(xDomain).toEqual(undefined); - }); - - test('it uses min interval if interval is passed in and visualization is histogram', () => { - const { data, args } = sampleArgs(); - - const component = shallow( - - ); - expect(component.find(Settings).prop('xDomain')).toEqual({ - minInterval: 101, - min: NaN, - max: NaN, - }); - }); - - test('disabled legend extra by default', () => { - const { data, args } = sampleArgs(); - const component = shallow(); - expect(component.find(Settings).at(0).prop('showLegendExtra')).toEqual(false); - }); - - test('ignores legend extra for ordinal chart', () => { - const { data, args } = sampleArgs(); - const component = shallow( - - ); - expect(component.find(Settings).at(0).prop('showLegendExtra')).toEqual(false); - }); - - test('shows legend extra for histogram chart', () => { - const { args } = sampleArgs(); - const component = shallow( - - ); - expect(component.find(Settings).at(0).prop('showLegendExtra')).toEqual(true); - }); - - test('it renders bar', () => { - const { data, args } = sampleArgs(); - const component = shallow( - - ); - expect(component).toMatchSnapshot(); - expect(component.find(BarSeries)).toHaveLength(2); - expect(component.find(BarSeries).at(0).prop('yAccessors')).toEqual(['a']); - expect(component.find(BarSeries).at(1).prop('yAccessors')).toEqual(['b']); - }); - - test('it renders area', () => { - const { data, args } = sampleArgs(); - const component = shallow( - - ); - expect(component).toMatchSnapshot(); - expect(component.find(AreaSeries)).toHaveLength(2); - expect(component.find(AreaSeries).at(0).prop('yAccessors')).toEqual(['a']); - expect(component.find(AreaSeries).at(1).prop('yAccessors')).toEqual(['b']); - }); - - test('it renders horizontal bar', () => { - const { data, args } = sampleArgs(); - const component = shallow( - - ); - expect(component).toMatchSnapshot(); - expect(component.find(BarSeries)).toHaveLength(2); - expect(component.find(BarSeries).at(0).prop('yAccessors')).toEqual(['a']); - expect(component.find(BarSeries).at(1).prop('yAccessors')).toEqual(['b']); - expect(component.find(Settings).prop('rotation')).toEqual(90); - }); - - test('it renders regular bar empty placeholder for no results', () => { - const { data, args } = sampleArgs(); - - // send empty data to the chart - data.tables.first.rows = []; - - const component = shallow(); - - expect(component.find(BarSeries)).toHaveLength(0); - expect(component.find(EmptyPlaceholder).prop('icon')).toBeDefined(); - }); - - test('onBrushEnd returns correct context data for date histogram data', () => { - const { args } = sampleArgs(); - - const wrapper = mountWithIntl( - - ); - wrapper.find(Settings).first().prop('onBrushEnd')!({ x: [1585757732783, 1585758880838] }); - - expect(onSelectRange).toHaveBeenCalledWith({ - column: 0, - table: dateHistogramData.tables.timeLayer, - range: [1585757732783, 1585758880838], - }); - }); - - test('onBrushEnd returns correct context data for number histogram data', () => { - const { args } = sampleArgs(); - - const numberLayer: DataLayerArgs = { - layerId: 'numberLayer', - layerType: layerTypes.DATA, - hide: false, - xAccessor: 'xAccessorId', - yScaleType: 'linear', - xScaleType: 'linear', - isHistogram: true, - seriesType: 'bar_stacked', - accessors: ['yAccessorId'], - palette: mockPaletteOutput, - }; - - const numberHistogramData: LensMultiTable = { - type: 'lens_multitable', - tables: { - numberLayer: { - type: 'datatable', - rows: [ - { - xAccessorId: 5, - yAccessorId: 1, - }, - { - xAccessorId: 7, - yAccessorId: 1, - }, - { - xAccessorId: 8, - yAccessorId: 1, - }, - { - xAccessorId: 10, - yAccessorId: 1, - }, - ], - columns: [ - { - id: 'xAccessorId', - name: 'bytes', - meta: { type: 'number' }, - }, - { - id: 'yAccessorId', - name: 'Count of records', - meta: { type: 'number' }, - }, - ], - }, - }, - dateRange: { - fromDate: new Date('2020-04-01T16:14:16.246Z'), - toDate: new Date('2020-04-01T17:15:41.263Z'), - }, - }; - - const wrapper = mountWithIntl( - - ); - - wrapper.find(Settings).first().prop('onBrushEnd')!({ x: [5, 8] }); - - expect(onSelectRange).toHaveBeenCalledWith({ - column: 0, - table: numberHistogramData.tables.numberLayer, - range: [5, 8], - }); - }); - - test('onBrushEnd is not set on non-interactive mode', () => { - const { args, data } = sampleArgs(); - - const wrapper = mountWithIntl( - - ); - - expect(wrapper.find(Settings).first().prop('onBrushEnd')).toBeUndefined(); - }); - - test('allowBrushingLastHistogramBin is true for date histogram data', () => { - const { args } = sampleArgs(); - - const wrapper = mountWithIntl( - - ); - expect(wrapper.find(Settings).at(0).prop('allowBrushingLastHistogramBin')).toEqual(true); - }); - - test('onElementClick returns correct context data', () => { - const geometry: GeometryValue = { x: 5, y: 1, accessor: 'y1', mark: null, datum: {} }; - const series = { - key: 'spec{d}yAccessor{d}splitAccessors{b-2}', - specId: 'd', - yAccessor: 'd', - splitAccessors: {}, - seriesKeys: [2, 'd'], - }; - - const { args, data } = sampleArgs(); - - const wrapper = mountWithIntl( - - ); - - wrapper.find(Settings).first().prop('onElementClick')!([ - [geometry, series as XYChartSeriesIdentifier], - ]); - - expect(onClickValue).toHaveBeenCalledWith({ - data: [ - { - column: 1, - row: 1, - table: data.tables.first, - value: 5, - }, - { - column: 1, - row: 0, - table: data.tables.first, - value: 2, - }, - ], - }); - }); - - test('onElementClick returns correct context data for date histogram', () => { - const geometry: GeometryValue = { - x: 1585758120000, - y: 1, - accessor: 'y1', - mark: null, - datum: {}, - }; - const series = { - key: 'spec{d}yAccessor{d}splitAccessors{b-2}', - specId: 'd', - yAccessor: 'yAccessorId', - splitAccessors: {}, - seriesKeys: ['yAccessorId'], - }; - - const { args } = sampleArgs(); - - const wrapper = mountWithIntl( - - ); - - wrapper.find(Settings).first().prop('onElementClick')!([ - [geometry, series as XYChartSeriesIdentifier], - ]); - - expect(onClickValue).toHaveBeenCalledWith({ - data: [ - { - column: 0, - row: 0, - table: dateHistogramData.tables.timeLayer, - value: 1585758120000, - }, - ], - }); - }); - - test('onElementClick returns correct context data for numeric histogram', () => { - const { args } = sampleArgs(); - - const numberLayer: DataLayerArgs = { - layerId: 'numberLayer', - layerType: layerTypes.DATA, - hide: false, - xAccessor: 'xAccessorId', - yScaleType: 'linear', - xScaleType: 'linear', - isHistogram: true, - seriesType: 'bar_stacked', - accessors: ['yAccessorId'], - palette: mockPaletteOutput, - }; - - const numberHistogramData: LensMultiTable = { - type: 'lens_multitable', - tables: { - numberLayer: { - type: 'datatable', - rows: [ - { - xAccessorId: 5, - yAccessorId: 1, - }, - { - xAccessorId: 7, - yAccessorId: 1, - }, - { - xAccessorId: 8, - yAccessorId: 1, - }, - { - xAccessorId: 10, - yAccessorId: 1, - }, - ], - columns: [ - { - id: 'xAccessorId', - name: 'bytes', - meta: { type: 'number' }, - }, - { - id: 'yAccessorId', - name: 'Count of records', - meta: { type: 'number' }, - }, - ], - }, - }, - dateRange: { - fromDate: new Date('2020-04-01T16:14:16.246Z'), - toDate: new Date('2020-04-01T17:15:41.263Z'), - }, - }; - const geometry: GeometryValue = { - x: 5, - y: 1, - accessor: 'y1', - mark: null, - datum: {}, - }; - const series = { - key: 'spec{d}yAccessor{d}splitAccessors{b-2}', - specId: 'd', - yAccessor: 'yAccessorId', - splitAccessors: {}, - seriesKeys: ['yAccessorId'], - }; - - const wrapper = mountWithIntl( - - ); - - wrapper.find(Settings).first().prop('onElementClick')!([ - [geometry, series as XYChartSeriesIdentifier], - ]); - - expect(onClickValue).toHaveBeenCalledWith({ - data: [ - { - column: 0, - row: 0, - table: numberHistogramData.tables.numberLayer, - value: 5, - }, - ], - timeFieldName: undefined, - }); - }); - - test('returns correct original data for ordinal x axis with special formatter', () => { - const geometry: GeometryValue = { x: 'BAR', y: 1, accessor: 'y1', mark: null, datum: {} }; - const series = { - key: 'spec{d}yAccessor{d}splitAccessors{b-2}', - specId: 'd', - yAccessor: 'a', - splitAccessors: {}, - seriesKeys: ['a'], - }; - - const { args, data } = sampleArgs(); - - convertSpy.mockImplementation((x) => (typeof x === 'string' ? x.toUpperCase() : x)); - - const wrapper = mountWithIntl( - - ); - - wrapper.find(Settings).first().prop('onElementClick')!([ - [geometry, series as XYChartSeriesIdentifier], - ]); - - expect(onClickValue).toHaveBeenCalledWith({ - data: [ - { - column: 3, - row: 1, - table: data.tables.first, - value: 'Bar', - }, - ], - }); - }); - - test('sets up correct yScaleType equal to binary_linear for bytes formatting', () => { - const { args, data } = sampleArgs(); - data.tables.first.columns[0].meta = { - type: 'number', - params: { id: 'bytes', params: { pattern: '0,0.00b' } }, - }; - - const wrapper = mountWithIntl( - - ); - - expect(wrapper.find(LineSeries).at(0).prop('yScaleType')).toEqual('linear_binary'); - }); - - test('allowBrushingLastHistogramBin should be fakse for ordinal data', () => { - const { args, data } = sampleArgs(); - - const wrapper = mountWithIntl( - - ); - - expect(wrapper.find(Settings).at(0).prop('allowBrushingLastHistogramBin')).toEqual(false); - }); - - test('onElementClick is not triggering event on non-interactive mode', () => { - const { args, data } = sampleArgs(); - - const wrapper = mountWithIntl( - - ); - - expect(wrapper.find(Settings).first().prop('onElementClick')).toBeUndefined(); - }); - - test('legendAction is not triggering event on non-interactive mode', () => { - const { args, data } = sampleArgs(); - - const wrapper = mountWithIntl( - - ); - - expect(wrapper.find(Settings).first().prop('legendAction')).toBeUndefined(); - }); - - test('it renders stacked bar', () => { - const { data, args } = sampleArgs(); - const component = shallow( - - ); - expect(component).toMatchSnapshot(); - expect(component.find(BarSeries)).toHaveLength(2); - expect(component.find(BarSeries).at(0).prop('stackAccessors')).toHaveLength(1); - expect(component.find(BarSeries).at(1).prop('stackAccessors')).toHaveLength(1); - }); - - test('it renders stacked area', () => { - const { data, args } = sampleArgs(); - const component = shallow( - - ); - expect(component).toMatchSnapshot(); - expect(component.find(AreaSeries)).toHaveLength(2); - expect(component.find(AreaSeries).at(0).prop('stackAccessors')).toHaveLength(1); - expect(component.find(AreaSeries).at(1).prop('stackAccessors')).toHaveLength(1); - }); - - test('it renders stacked horizontal bar', () => { - const { data, args } = sampleArgs(); - const component = shallow( - - ); - expect(component).toMatchSnapshot(); - expect(component.find(BarSeries)).toHaveLength(2); - expect(component.find(BarSeries).at(0).prop('stackAccessors')).toHaveLength(1); - expect(component.find(BarSeries).at(1).prop('stackAccessors')).toHaveLength(1); - expect(component.find(Settings).prop('rotation')).toEqual(90); - }); - - test('it renders stacked bar empty placeholder for no results', () => { - const { data, args } = sampleArgs(); - - const component = shallow( - - ); - - expect(component.find(BarSeries)).toHaveLength(0); - expect(component.find(EmptyPlaceholder).prop('icon')).toBeDefined(); - }); - - test('it passes time zone to the series', () => { - const { data, args } = sampleArgs(); - const component = shallow( - - ); - expect(component.find(LineSeries).at(0).prop('timeZone')).toEqual('CEST'); - expect(component.find(LineSeries).at(1).prop('timeZone')).toEqual('CEST'); - }); - - test('it applies histogram mode to the series for single series', () => { - const { data, args } = sampleArgs(); - const firstLayer: DataLayerArgs = { - ...args.layers[0], - accessors: ['b'], - seriesType: 'bar', - isHistogram: true, - }; - delete firstLayer.splitAccessor; - const component = shallow( - - ); - expect(component.find(BarSeries).at(0).prop('enableHistogramMode')).toEqual(true); - }); - - test('it does not apply histogram mode to more than one bar series for unstacked bar chart', () => { - const { data, args } = sampleArgs(); - const firstLayer: DataLayerArgs = { ...args.layers[0], seriesType: 'bar', isHistogram: true }; - delete firstLayer.splitAccessor; - const component = shallow( - - ); - expect(component.find(BarSeries).at(0).prop('enableHistogramMode')).toEqual(false); - expect(component.find(BarSeries).at(1).prop('enableHistogramMode')).toEqual(false); - }); - - test('it applies histogram mode to more than one the series for unstacked line/area chart', () => { - const { data, args } = sampleArgs(); - const firstLayer: DataLayerArgs = { - ...args.layers[0], - seriesType: 'line', - isHistogram: true, - }; - delete firstLayer.splitAccessor; - const secondLayer: DataLayerArgs = { - ...args.layers[0], - seriesType: 'line', - isHistogram: true, - }; - delete secondLayer.splitAccessor; - const component = shallow( - - ); - expect(component.find(LineSeries).at(0).prop('enableHistogramMode')).toEqual(true); - expect(component.find(LineSeries).at(1).prop('enableHistogramMode')).toEqual(true); - }); - - test('it applies histogram mode to the series for stacked series', () => { - const { data, args } = sampleArgs(); - const component = shallow( - - ); - expect(component.find(BarSeries).at(0).prop('enableHistogramMode')).toEqual(true); - expect(component.find(BarSeries).at(1).prop('enableHistogramMode')).toEqual(true); - }); - - test('it does not apply histogram mode for splitted series', () => { - const { data, args } = sampleArgs(); - const component = shallow( - - ); - expect(component.find(BarSeries).at(0).prop('enableHistogramMode')).toEqual(false); - expect(component.find(BarSeries).at(1).prop('enableHistogramMode')).toEqual(false); - }); - - describe('y axes', () => { - test('single axis if possible', () => { - const args = createArgsWithLayers(); - - const component = getRenderedComponent(dataWithoutFormats, args); - const axes = component.find(Axis); - expect(axes).toHaveLength(2); - }); - - test('multiple axes because of config', () => { - const args = createArgsWithLayers(); - const newArgs = { - ...args, - layers: [ - { - ...args.layers[0], - accessors: ['a', 'b'], - yConfig: [ - { - forAccessor: 'a', - axisMode: 'left', - }, - { - forAccessor: 'b', - axisMode: 'right', - }, - ], - }, - ], - } as XYArgs; - - const component = getRenderedComponent(dataWithoutFormats, newArgs); - const axes = component.find(Axis); - expect(axes).toHaveLength(3); - expect(component.find(LineSeries).at(0).prop('groupId')).toEqual( - axes.at(1).prop('groupId') - ); - expect(component.find(LineSeries).at(1).prop('groupId')).toEqual( - axes.at(2).prop('groupId') - ); - }); - - test('multiple axes because of incompatible formatters', () => { - const args = createArgsWithLayers(); - const newArgs = { - ...args, - layers: [ - { - ...args.layers[0], - accessors: ['c', 'd'], - }, - ], - } as XYArgs; - - const component = getRenderedComponent(dataWithFormats, newArgs); - const axes = component.find(Axis); - expect(axes).toHaveLength(3); - expect(component.find(LineSeries).at(0).prop('groupId')).toEqual( - axes.at(1).prop('groupId') - ); - expect(component.find(LineSeries).at(1).prop('groupId')).toEqual( - axes.at(2).prop('groupId') - ); - }); - - test('single axis despite different formatters if enforced', () => { - const args = createArgsWithLayers(); - const newArgs = { - ...args, - layers: [ - { - ...args.layers[0], - accessors: ['c', 'd'], - yConfig: [ - { - forAccessor: 'c', - axisMode: 'left', - }, - { - forAccessor: 'd', - axisMode: 'left', - }, - ], - }, - ], - } as XYArgs; - - const component = getRenderedComponent(dataWithoutFormats, newArgs); - const axes = component.find(Axis); - expect(axes).toHaveLength(2); - }); - }); - - describe('y series coloring', () => { - test('color is applied to chart for multiple series', () => { - const args = createArgsWithLayers(); - const newArgs = { - ...args, - layers: [ - { - ...args.layers[0], - splitAccessor: undefined, - accessors: ['a', 'b'], - yConfig: [ - { - forAccessor: 'a', - color: '#550000', - }, - { - forAccessor: 'b', - color: '#FFFF00', - }, - ], - }, - { - ...args.layers[0], - splitAccessor: undefined, - accessors: ['c'], - yConfig: [ - { - forAccessor: 'c', - color: '#FEECDF', - }, - ], - }, - ], - } as XYArgs; - - const component = getRenderedComponent(dataWithoutFormats, newArgs); - expect( - (component.find(LineSeries).at(0).prop('color') as Function)!({ - yAccessor: 'a', - seriesKeys: ['a'], - }) - ).toEqual('#550000'); - expect( - (component.find(LineSeries).at(1).prop('color') as Function)!({ - yAccessor: 'b', - seriesKeys: ['b'], - }) - ).toEqual('#FFFF00'); - expect( - (component.find(LineSeries).at(2).prop('color') as Function)!({ - yAccessor: 'c', - seriesKeys: ['c'], - }) - ).toEqual('#FEECDF'); - }); - test('color is not applied to chart when splitAccessor is defined or when yConfig is not configured', () => { - const args = createArgsWithLayers(); - const newArgs = { - ...args, - layers: [ - { - ...args.layers[0], - accessors: ['a'], - yConfig: [ - { - forAccessor: 'a', - color: '#550000', - }, - ], - }, - { - ...args.layers[0], - splitAccessor: undefined, - accessors: ['c'], - }, - ], - } as XYArgs; - - const component = getRenderedComponent(dataWithoutFormats, newArgs); - expect( - (component.find(LineSeries).at(0).prop('color') as Function)!({ - yAccessor: 'a', - seriesKeys: ['a'], - }) - ).toEqual('blue'); - expect( - (component.find(LineSeries).at(1).prop('color') as Function)!({ - yAccessor: 'c', - seriesKeys: ['c'], - }) - ).toEqual('blue'); - }); - }); - - describe('provides correct series naming', () => { - const nameFnArgs = { - seriesKeys: [], - key: '', - specId: 'a', - yAccessor: '', - splitAccessors: new Map(), - }; - - test('simplest xy chart without human-readable name', () => { - const args = createArgsWithLayers(); - const newArgs = { - ...args, - layers: [ - { - ...args.layers[0], - accessors: ['a'], - splitAccessor: undefined, - columnToLabel: '', - }, - ], - }; - - const component = getRenderedComponent(dataWithoutFormats, newArgs); - const nameFn = component.find(LineSeries).prop('name') as SeriesNameFn; - - // In this case, the ID is used as the name. This shouldn't happen in practice - expect(nameFn({ ...nameFnArgs, seriesKeys: ['a'] }, false)).toEqual(''); - expect(nameFn({ ...nameFnArgs, seriesKeys: ['nonsense'] }, false)).toEqual(''); - }); - - test('simplest xy chart with empty name', () => { - const args = createArgsWithLayers(); - const newArgs = { - ...args, - layers: [ - { - ...args.layers[0], - accessors: ['a'], - splitAccessor: undefined, - columnToLabel: '{"a":""}', - }, - ], - }; - - const component = getRenderedComponent(dataWithoutFormats, newArgs); - const nameFn = component.find(LineSeries).prop('name') as SeriesNameFn; - - // In this case, the ID is used as the name. This shouldn't happen in practice - expect(nameFn({ ...nameFnArgs, seriesKeys: ['a'] }, false)).toEqual(''); - expect(nameFn({ ...nameFnArgs, seriesKeys: ['nonsense'] }, false)).toEqual(''); - }); - - test('simplest xy chart with human-readable name', () => { - const args = createArgsWithLayers(); - const newArgs = { - ...args, - layers: [ - { - ...args.layers[0], - accessors: ['a'], - splitAccessor: undefined, - columnToLabel: '{"a":"Column A"}', - }, - ], - }; - - const component = getRenderedComponent(dataWithoutFormats, newArgs); - const nameFn = component.find(LineSeries).prop('name') as SeriesNameFn; - - expect(nameFn({ ...nameFnArgs, seriesKeys: ['a'] }, false)).toEqual('Column A'); - }); - - test('multiple y accessors', () => { - const args = createArgsWithLayers(); - const newArgs = { - ...args, - layers: [ - { - ...args.layers[0], - accessors: ['a', 'b'], - splitAccessor: undefined, - columnToLabel: '{"a": "Label A"}', - }, - ], - }; - - const component = getRenderedComponent(dataWithoutFormats, newArgs); - const nameFn1 = component.find(LineSeries).at(0).prop('name') as SeriesNameFn; - const nameFn2 = component.find(LineSeries).at(1).prop('name') as SeriesNameFn; - - // This accessor has a human-readable name - expect(nameFn1({ ...nameFnArgs, seriesKeys: ['a'] }, false)).toEqual('Label A'); - // This accessor does not - expect(nameFn2({ ...nameFnArgs, seriesKeys: ['b'] }, false)).toEqual(''); - expect(nameFn1({ ...nameFnArgs, seriesKeys: ['nonsense'] }, false)).toEqual(''); - }); - - test('split series without formatting and single y accessor', () => { - const args = createArgsWithLayers(); - const newArgs = { - ...args, - layers: [ - { - ...args.layers[0], - accessors: ['a'], - splitAccessor: 'd', - columnToLabel: '{"a": "Label A"}', - }, - ], - }; - - const component = getRenderedComponent(dataWithoutFormats, newArgs); - const nameFn = component.find(LineSeries).prop('name') as SeriesNameFn; - - expect(nameFn({ ...nameFnArgs, seriesKeys: ['split1', 'a'] }, false)).toEqual('split1'); - }); - - test('split series with formatting and single y accessor', () => { - const args = createArgsWithLayers(); - const newArgs = { - ...args, - layers: [ - { - ...args.layers[0], - accessors: ['a'], - splitAccessor: 'd', - columnToLabel: '{"a": "Label A"}', - }, - ], - }; - - const component = getRenderedComponent(dataWithFormats, newArgs); - const nameFn = component.find(LineSeries).prop('name') as SeriesNameFn; - - convertSpy.mockReturnValueOnce('formatted'); - expect(nameFn({ ...nameFnArgs, seriesKeys: ['split1', 'a'] }, false)).toEqual('formatted'); - expect(getFormatSpy).toHaveBeenCalledWith({ id: 'custom' }); - }); - - test('split series without formatting with multiple y accessors', () => { - const args = createArgsWithLayers(); - const newArgs = { - ...args, - layers: [ - { - ...args.layers[0], - accessors: ['a', 'b'], - splitAccessor: 'd', - columnToLabel: '{"a": "Label A","b": "Label B"}', - }, - ], - }; - - const component = getRenderedComponent(dataWithoutFormats, newArgs); - const nameFn1 = component.find(LineSeries).at(0).prop('name') as SeriesNameFn; - const nameFn2 = component.find(LineSeries).at(0).prop('name') as SeriesNameFn; - - expect(nameFn1({ ...nameFnArgs, seriesKeys: ['split1', 'a'] }, false)).toEqual( - 'split1 - Label A' - ); - expect(nameFn2({ ...nameFnArgs, seriesKeys: ['split1', 'b'] }, false)).toEqual( - 'split1 - Label B' - ); - }); - - test('split series with formatting with multiple y accessors', () => { - const args = createArgsWithLayers(); - const newArgs = { - ...args, - layers: [ - { - ...args.layers[0], - accessors: ['a', 'b'], - splitAccessor: 'd', - columnToLabel: '{"a": "Label A","b": "Label B"}', - }, - ], - }; - - const component = getRenderedComponent(dataWithFormats, newArgs); - const nameFn1 = component.find(LineSeries).at(0).prop('name') as SeriesNameFn; - const nameFn2 = component.find(LineSeries).at(1).prop('name') as SeriesNameFn; - - convertSpy.mockReturnValueOnce('formatted1').mockReturnValueOnce('formatted2'); - expect(nameFn1({ ...nameFnArgs, seriesKeys: ['split1', 'a'] }, false)).toEqual( - 'formatted1 - Label A' - ); - expect(nameFn2({ ...nameFnArgs, seriesKeys: ['split1', 'b'] }, false)).toEqual( - 'formatted2 - Label B' - ); - }); - }); - - test('it set the scale of the x axis according to the args prop', () => { - const { data, args } = sampleArgs(); - - const component = shallow( - - ); - expect(component.find(LineSeries).at(0).prop('xScaleType')).toEqual(ScaleType.Ordinal); - expect(component.find(LineSeries).at(1).prop('xScaleType')).toEqual(ScaleType.Ordinal); - }); - - test('it set the scale of the y axis according to the args prop', () => { - const { data, args } = sampleArgs(); - - const component = shallow( - - ); - expect(component.find(LineSeries).at(0).prop('yScaleType')).toEqual(ScaleType.Sqrt); - expect(component.find(LineSeries).at(1).prop('yScaleType')).toEqual(ScaleType.Sqrt); - }); - - test('it gets the formatter for the x axis', () => { - const { data, args } = sampleArgs(); - - shallow(); - - expect(getFormatSpy).toHaveBeenCalledWith({ id: 'string' }); - }); - - test('it gets the formatter for the y axis if there is only one accessor', () => { - const { data, args } = sampleArgs(); - - shallow( - - ); - expect(getFormatSpy).toHaveBeenCalledWith({ - id: 'number', - params: { pattern: '0,0.000' }, - }); - }); - - test('it should pass the formatter function to the axis', () => { - const { data, args } = sampleArgs(); - - const instance = shallow(); - - const tickFormatter = instance.find(Axis).first().prop('tickFormat'); - - if (!tickFormatter) { - throw new Error('tickFormatter prop not found'); - } - - tickFormatter('I'); - - expect(convertSpy).toHaveBeenCalledWith('I'); - }); - - test('it should set the tickLabel visibility on the x axis if the tick labels is hidden', () => { - const { data, args } = sampleArgs(); - - args.tickLabelsVisibilitySettings = { - x: false, - yLeft: true, - yRight: true, - type: 'lens_xy_tickLabelsConfig', - }; - - const instance = shallow(); - - const axisStyle = instance.find(Axis).first().prop('style'); - - expect(axisStyle).toMatchObject({ - tickLabel: { - visible: false, - }, - }); - }); - - test('it should set the tickLabel visibility on the y axis if the tick labels is hidden', () => { - const { data, args } = sampleArgs(); - - args.tickLabelsVisibilitySettings = { - x: true, - yLeft: false, - yRight: false, - type: 'lens_xy_tickLabelsConfig', - }; - - const instance = shallow(); - - const axisStyle = instance.find(Axis).at(1).prop('style'); - - expect(axisStyle).toMatchObject({ - tickLabel: { - visible: false, - }, - }); - }); - - test('it should set the tickLabel visibility on the x axis if the tick labels is shown', () => { - const { data, args } = sampleArgs(); - - args.tickLabelsVisibilitySettings = { - x: true, - yLeft: true, - yRight: true, - type: 'lens_xy_tickLabelsConfig', - }; - - const instance = shallow(); - - const axisStyle = instance.find(Axis).first().prop('style'); - - expect(axisStyle).toMatchObject({ - tickLabel: { - visible: true, - }, - }); - }); - - test('it should set the tickLabel orientation on the x axis', () => { - const { data, args } = sampleArgs(); - - args.labelsOrientation = { - x: -45, - yLeft: 0, - yRight: -90, - type: 'lens_xy_labelsOrientationConfig', - }; - - const instance = shallow(); - - const axisStyle = instance.find(Axis).first().prop('style'); - - expect(axisStyle).toMatchObject({ - tickLabel: { - rotation: -45, - }, - }); - }); - - test('it should set the tickLabel visibility on the y axis if the tick labels is shown', () => { - const { data, args } = sampleArgs(); - - args.tickLabelsVisibilitySettings = { - x: false, - yLeft: true, - yRight: true, - type: 'lens_xy_tickLabelsConfig', - }; - - const instance = shallow(); - - const axisStyle = instance.find(Axis).at(1).prop('style'); - - expect(axisStyle).toMatchObject({ - tickLabel: { - visible: true, - }, - }); - }); - - test('it should set the tickLabel orientation on the y axis', () => { - const { data, args } = sampleArgs(); - - args.labelsOrientation = { - x: -45, - yLeft: -90, - yRight: -90, - type: 'lens_xy_labelsOrientationConfig', - }; - - const instance = shallow(); - - const axisStyle = instance.find(Axis).at(1).prop('style'); - - expect(axisStyle).toMatchObject({ - tickLabel: { - rotation: -90, - }, - }); - }); - - test('it should remove invalid rows', () => { - const data: LensMultiTable = { - type: 'lens_multitable', - tables: { - first: { - type: 'datatable', - columns: [ - { id: 'a', name: 'a', meta: { type: 'number' } }, - { id: 'b', name: 'b', meta: { type: 'number' } }, - { id: 'c', name: 'c', meta: { type: 'string' } }, - ], - rows: [ - { a: undefined, b: 2, c: 'I', d: 'Row 1' }, - { a: 1, b: 5, c: 'J', d: 'Row 2' }, - ], - }, - second: { - type: 'datatable', - columns: [ - { id: 'a', name: 'a', meta: { type: 'number' } }, - { id: 'b', name: 'b', meta: { type: 'number' } }, - { id: 'c', name: 'c', meta: { type: 'string' } }, - ], - rows: [ - { a: undefined, b: undefined, c: undefined }, - { a: undefined, b: undefined, c: undefined }, - ], - }, - }, - }; - - const args: XYArgs = { - xTitle: '', - yTitle: '', - yRightTitle: '', - legend: { type: 'lens_xy_legendConfig', isVisible: false, position: Position.Top }, - valueLabels: 'hide', - tickLabelsVisibilitySettings: { - type: 'lens_xy_tickLabelsConfig', - x: true, - yLeft: true, - yRight: true, - }, - gridlinesVisibilitySettings: { - type: 'lens_xy_gridlinesConfig', - x: true, - yLeft: false, - yRight: false, - }, - labelsOrientation: { - type: 'lens_xy_labelsOrientationConfig', - x: 0, - yLeft: 0, - yRight: 0, - }, - yLeftExtent: { - mode: 'full', - type: 'lens_xy_axisExtentConfig', - }, - yRightExtent: { - mode: 'full', - type: 'lens_xy_axisExtentConfig', - }, - layers: [ - { - layerId: 'first', - layerType: layerTypes.DATA, - seriesType: 'line', - xAccessor: 'a', - accessors: ['c'], - splitAccessor: 'b', - columnToLabel: '', - xScaleType: 'ordinal', - yScaleType: 'linear', - isHistogram: false, - palette: mockPaletteOutput, - }, - { - layerId: 'second', - layerType: layerTypes.DATA, - seriesType: 'line', - xAccessor: 'a', - accessors: ['c'], - splitAccessor: 'b', - columnToLabel: '', - xScaleType: 'ordinal', - yScaleType: 'linear', - isHistogram: false, - palette: mockPaletteOutput, - }, - ], - }; - - const component = shallow(); - - const series = component.find(LineSeries); - - // Only one series should be rendered, even though 2 are configured - // This one series should only have one row, even though 2 are sent - expect(series.prop('data')).toEqual([{ a: 1, b: 5, c: 'J', d: 'Row 2' }]); - }); - - test('it should not remove rows with falsy but non-undefined values', () => { - const data: LensMultiTable = { - type: 'lens_multitable', - tables: { - first: { - type: 'datatable', - columns: [ - { id: 'a', name: 'a', meta: { type: 'number' } }, - { id: 'b', name: 'b', meta: { type: 'number' } }, - { id: 'c', name: 'c', meta: { type: 'number' } }, - ], - rows: [ - { a: 0, b: 2, c: 5 }, - { a: 1, b: 0, c: 7 }, - ], - }, - }, - }; - - const args: XYArgs = { - xTitle: '', - yTitle: '', - yRightTitle: '', - legend: { type: 'lens_xy_legendConfig', isVisible: false, position: Position.Top }, - valueLabels: 'hide', - tickLabelsVisibilitySettings: { - type: 'lens_xy_tickLabelsConfig', - x: true, - yLeft: false, - yRight: false, - }, - gridlinesVisibilitySettings: { - type: 'lens_xy_gridlinesConfig', - x: true, - yLeft: false, - yRight: false, - }, - labelsOrientation: { - type: 'lens_xy_labelsOrientationConfig', - x: 0, - yLeft: 0, - yRight: 0, - }, - yLeftExtent: { - mode: 'full', - type: 'lens_xy_axisExtentConfig', - }, - yRightExtent: { - mode: 'full', - type: 'lens_xy_axisExtentConfig', - }, - layers: [ - { - layerId: 'first', - layerType: layerTypes.DATA, - seriesType: 'line', - xAccessor: 'a', - accessors: ['c'], - splitAccessor: 'b', - columnToLabel: '', - xScaleType: 'ordinal', - yScaleType: 'linear', - isHistogram: false, - palette: mockPaletteOutput, - }, - ], - }; - - const component = shallow(); - - const series = component.find(LineSeries); - - expect(series.prop('data')).toEqual([ - { a: 0, b: 2, c: 5 }, - { a: 1, b: 0, c: 7 }, - ]); - }); - - test('it should show legend for split series, even with one row', () => { - const data: LensMultiTable = { - type: 'lens_multitable', - tables: { - first: { - type: 'datatable', - columns: [ - { id: 'a', name: 'a', meta: { type: 'number' } }, - { id: 'b', name: 'b', meta: { type: 'number' } }, - { id: 'c', name: 'c', meta: { type: 'string' } }, - ], - rows: [{ a: 1, b: 5, c: 'J' }], - }, - }, - }; - - const args: XYArgs = { - xTitle: '', - yTitle: '', - yRightTitle: '', - legend: { type: 'lens_xy_legendConfig', isVisible: true, position: Position.Top }, - valueLabels: 'hide', - tickLabelsVisibilitySettings: { - type: 'lens_xy_tickLabelsConfig', - x: true, - yLeft: false, - yRight: false, - }, - gridlinesVisibilitySettings: { - type: 'lens_xy_gridlinesConfig', - x: true, - yLeft: false, - yRight: false, - }, - labelsOrientation: { - type: 'lens_xy_labelsOrientationConfig', - x: 0, - yLeft: 0, - yRight: 0, - }, - yLeftExtent: { - mode: 'full', - type: 'lens_xy_axisExtentConfig', - }, - yRightExtent: { - mode: 'full', - type: 'lens_xy_axisExtentConfig', - }, - layers: [ - { - layerId: 'first', - layerType: layerTypes.DATA, - seriesType: 'line', - xAccessor: 'a', - accessors: ['c'], - splitAccessor: 'b', - columnToLabel: '', - xScaleType: 'ordinal', - yScaleType: 'linear', - isHistogram: false, - palette: mockPaletteOutput, - }, - ], - }; - - const component = shallow(); - - expect(component.find(Settings).prop('showLegend')).toEqual(true); - }); - - test('it should always show legend if showSingleSeries is set', () => { - const { data, args } = sampleArgs(); - - const component = shallow( - - ); - - expect(component.find(Settings).prop('showLegend')).toEqual(true); - }); - - test('it should populate the correct legendPosition if isInside is set', () => { - const { data, args } = sampleArgs(); - - const component = shallow( - - ); - - expect(component.find(Settings).prop('legendPosition')).toEqual({ - vAlign: VerticalAlignment.Top, - hAlign: HorizontalAlignment.Right, - direction: LayoutDirection.Vertical, - floating: true, - floatingColumns: 1, - }); - }); - - test('it not show legend if isVisible is set to false', () => { - const { data, args } = sampleArgs(); - - const component = shallow( - - ); - - expect(component.find(Settings).prop('showLegend')).toEqual(false); - }); - - test('it should show legend on right side', () => { - const { data, args } = sampleArgs(); - - const component = shallow( - - ); - - expect(component.find(Settings).prop('legendPosition')).toEqual('top'); - }); - - test('it should apply the fitting function to all non-bar series', () => { - const data: LensMultiTable = { - type: 'lens_multitable', - tables: { - first: createSampleDatatableWithRows([ - { a: 1, b: 2, c: 'I', d: 'Foo' }, - { a: 1, b: 5, c: 'J', d: 'Bar' }, - ]), - }, - }; - - const args: XYArgs = createArgsWithLayers([ - { ...sampleLayer, accessors: ['a'] }, - { ...sampleLayer, seriesType: 'bar', accessors: ['a'] }, - { ...sampleLayer, seriesType: 'area', accessors: ['a'] }, - { ...sampleLayer, seriesType: 'area_stacked', accessors: ['a'] }, - ]); - - const component = shallow( - - ); - - expect(component.find(LineSeries).prop('fit')).toEqual({ type: Fit.Carry }); - expect(component.find(BarSeries).prop('fit')).toEqual(undefined); - expect(component.find(AreaSeries).at(0).prop('fit')).toEqual({ type: Fit.Carry }); - expect(component.find(AreaSeries).at(0).prop('stackAccessors')).toEqual([]); - expect(component.find(AreaSeries).at(1).prop('fit')).toEqual({ type: Fit.Carry }); - expect(component.find(AreaSeries).at(1).prop('stackAccessors')).toEqual(['c']); - }); - - test('it should apply None fitting function if not specified', () => { - const { data, args } = sampleArgs(); - - args.layers[0].accessors = ['a']; - - const component = shallow( - - ); - - expect(component.find(LineSeries).prop('fit')).toEqual({ type: Fit.None }); - }); - - test('it should apply the xTitle if is specified', () => { - const { data, args } = sampleArgs(); - - args.xTitle = 'My custom x-axis title'; - - const component = shallow( - - ); - - expect(component.find(Axis).at(0).prop('title')).toEqual('My custom x-axis title'); - }); - - test('it should hide the X axis title if the corresponding switch is off', () => { - const { data, args } = sampleArgs(); - - args.axisTitlesVisibilitySettings = { - x: false, - yLeft: true, - yRight: true, - type: 'lens_xy_axisTitlesVisibilityConfig', - }; - - const component = shallow( - - ); - - const axisStyle = component.find(Axis).first().prop('style'); - - expect(axisStyle).toMatchObject({ - axisTitle: { - visible: false, - }, - }); - }); - - test('it should show the X axis gridlines if the setting is on', () => { - const { data, args } = sampleArgs(); - - args.gridlinesVisibilitySettings = { - x: true, - yLeft: false, - yRight: false, - type: 'lens_xy_gridlinesConfig', - }; - - const component = shallow( - - ); - - expect(component.find(Axis).at(0).prop('gridLine')).toMatchObject({ - visible: true, - }); - }); - - test('it should format the boolean values correctly', () => { - const data: LensMultiTable = { - type: 'lens_multitable', - tables: { - first: { - type: 'datatable', - columns: [ - { - id: 'a', - name: 'a', - meta: { type: 'number', params: { id: 'number', params: { pattern: '0,0.000' } } }, - }, - { - id: 'b', - name: 'b', - meta: { type: 'number', params: { id: 'number', params: { pattern: '000,0' } } }, - }, - { - id: 'c', - name: 'c', - meta: { - type: 'boolean', - params: { id: 'boolean' }, - }, - }, - ], - rows: [ - { a: 5, b: 2, c: 0 }, - { a: 19, b: 5, c: 1 }, - ], - }, - }, - dateRange: { - fromDate: new Date('2019-01-02T05:00:00.000Z'), - toDate: new Date('2019-01-03T05:00:00.000Z'), - }, - }; - const timeSampleLayer: DataLayerArgs = { - layerId: 'first', - layerType: layerTypes.DATA, - seriesType: 'line', - xAccessor: 'c', - accessors: ['a', 'b'], - xScaleType: 'ordinal', - yScaleType: 'linear', - isHistogram: false, - palette: mockPaletteOutput, - }; - const args = createArgsWithLayers([timeSampleLayer]); - - const getCustomFormatSpy = jest.fn(); - getCustomFormatSpy.mockReturnValue({ convert: jest.fn((x) => Boolean(x)) }); - - const component = shallow( - - ); - - expect(component.find(LineSeries).at(1).prop('data')).toEqual([ - { - a: 5, - b: 2, - c: false, - }, - { - a: 19, - b: 5, - c: true, - }, - ]); - }); - }); - - describe('calculateMinInterval', () => { - let xyProps: XYChartProps; - - beforeEach(() => { - xyProps = sampleArgs(); - xyProps.args.layers[0].xScaleType = 'time'; - }); - it('should use first valid layer and determine interval', async () => { - xyProps.data.tables.first.columns[2].meta.source = 'esaggs'; - xyProps.data.tables.first.columns[2].meta.sourceParams = { - type: 'date_histogram', - params: { - used_interval: '5m', - }, - }; - const result = await calculateMinInterval(xyProps); - expect(result).toEqual(5 * 60 * 1000); - }); - - it('should return interval of number histogram if available on first x axis columns', async () => { - xyProps.args.layers[0].xScaleType = 'linear'; - xyProps.data.tables.first.columns[2].meta = { - source: 'esaggs', - type: 'number', - field: 'someField', - sourceParams: { - type: 'histogram', - params: { - interval: 'auto', - used_interval: 5, - }, - }, - }; - const result = await calculateMinInterval(xyProps); - expect(result).toEqual(5); - }); - - it('should return undefined if data table is empty', async () => { - xyProps.data.tables.first.rows = []; - xyProps.data.tables.first.columns[2].meta.source = 'esaggs'; - xyProps.data.tables.first.columns[2].meta.sourceParams = { - type: 'date_histogram', - params: { - used_interval: '5m', - }, - }; - const result = await calculateMinInterval(xyProps); - expect(result).toEqual(undefined); - }); - - it('should return undefined if interval can not be checked', async () => { - const result = await calculateMinInterval(xyProps); - expect(result).toEqual(undefined); - }); - - it('should return undefined if date column is not found', async () => { - xyProps.data.tables.first.columns.splice(2, 1); - const result = await calculateMinInterval(xyProps); - expect(result).toEqual(undefined); - }); - - it('should return undefined if x axis is not a date', async () => { - xyProps.args.layers[0].xScaleType = 'ordinal'; - xyProps.data.tables.first.columns.splice(2, 1); - const result = await calculateMinInterval(xyProps); - expect(result).toEqual(undefined); - }); - }); -}); diff --git a/x-pack/plugins/lens/public/xy_visualization/expression_reference_lines.test.tsx b/x-pack/plugins/lens/public/xy_visualization/expression_reference_lines.test.tsx deleted file mode 100644 index 4ec38f31b85af..0000000000000 --- a/x-pack/plugins/lens/public/xy_visualization/expression_reference_lines.test.tsx +++ /dev/null @@ -1,374 +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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { LineAnnotation, RectAnnotation } from '@elastic/charts'; -import { shallow } from 'enzyme'; -import React from 'react'; -import { chartPluginMock } from 'src/plugins/charts/public/mocks'; -import { FieldFormat } from 'src/plugins/field_formats/common'; -import { LensMultiTable } from '../../common'; -import { ReferenceLineLayerArgs, YConfig } from '../../common/expressions'; -import { - ReferenceLineAnnotations, - ReferenceLineAnnotationsProps, -} from './expression_reference_lines'; - -const paletteService = chartPluginMock.createPaletteRegistry(); - -const row: Record = { - xAccessorFirstId: 1, - xAccessorSecondId: 2, - yAccessorLeftFirstId: 5, - yAccessorLeftSecondId: 10, - yAccessorRightFirstId: 5, - yAccessorRightSecondId: 10, -}; - -const histogramData: LensMultiTable = { - type: 'lens_multitable', - tables: { - firstLayer: { - type: 'datatable', - rows: [row], - columns: Object.keys(row).map((id) => ({ - id, - name: `Static value: ${row[id]}`, - meta: { - type: 'number', - params: { id: 'number' }, - }, - })), - }, - }, - dateRange: { - fromDate: new Date('2020-04-01T16:14:16.246Z'), - toDate: new Date('2020-04-01T17:15:41.263Z'), - }, -}; - -function createLayers(yConfigs: ReferenceLineLayerArgs['yConfig']): ReferenceLineLayerArgs[] { - return [ - { - layerId: 'firstLayer', - layerType: 'referenceLine', - accessors: (yConfigs || []).map(({ forAccessor }) => forAccessor), - yConfig: yConfigs, - }, - ]; -} - -interface YCoords { - y0: number | undefined; - y1: number | undefined; -} -interface XCoords { - x0: number | undefined; - x1: number | undefined; -} - -function getAxisFromId(layerPrefix: string): YConfig['axisMode'] { - return /left/i.test(layerPrefix) ? 'left' : /right/i.test(layerPrefix) ? 'right' : 'bottom'; -} - -const emptyCoords = { x0: undefined, x1: undefined, y0: undefined, y1: undefined }; - -describe('ReferenceLineAnnotations', () => { - describe('with fill', () => { - let formatters: Record<'left' | 'right' | 'bottom', FieldFormat | undefined>; - let defaultProps: Omit; - - beforeEach(() => { - formatters = { - left: { convert: jest.fn((x) => x) } as unknown as FieldFormat, - right: { convert: jest.fn((x) => x) } as unknown as FieldFormat, - bottom: { convert: jest.fn((x) => x) } as unknown as FieldFormat, - }; - - defaultProps = { - formatters, - paletteService, - syncColors: false, - isHorizontal: false, - axesMap: { left: true, right: false }, - paddingMap: {}, - }; - }); - - it.each([ - ['yAccessorLeft', 'above'], - ['yAccessorLeft', 'below'], - ['yAccessorRight', 'above'], - ['yAccessorRight', 'below'], - ] as Array<[string, YConfig['fill']]>)( - 'should render a RectAnnotation for a reference line with fill set: %s %s', - (layerPrefix, fill) => { - const axisMode = getAxisFromId(layerPrefix); - const wrapper = shallow( - - ); - - const y0 = fill === 'above' ? 5 : undefined; - const y1 = fill === 'above' ? undefined : 5; - - expect(wrapper.find(LineAnnotation).exists()).toBe(true); - expect(wrapper.find(RectAnnotation).exists()).toBe(true); - expect(wrapper.find(RectAnnotation).prop('dataValues')).toEqual( - expect.arrayContaining([ - { - coordinates: { x0: undefined, x1: undefined, y0, y1 }, - details: y0 ?? y1, - header: undefined, - }, - ]) - ); - } - ); - - it.each([ - ['xAccessor', 'above'], - ['xAccessor', 'below'], - ] as Array<[string, YConfig['fill']]>)( - 'should render a RectAnnotation for a reference line with fill set: %s %s', - (layerPrefix, fill) => { - const wrapper = shallow( - - ); - - const x0 = fill === 'above' ? 1 : undefined; - const x1 = fill === 'above' ? undefined : 1; - - expect(wrapper.find(LineAnnotation).exists()).toBe(true); - expect(wrapper.find(RectAnnotation).exists()).toBe(true); - expect(wrapper.find(RectAnnotation).prop('dataValues')).toEqual( - expect.arrayContaining([ - { - coordinates: { ...emptyCoords, x0, x1 }, - details: x0 ?? x1, - header: undefined, - }, - ]) - ); - } - ); - - it.each([ - ['yAccessorLeft', 'above', { y0: 5, y1: 10 }, { y0: 10, y1: undefined }], - ['yAccessorLeft', 'below', { y0: undefined, y1: 5 }, { y0: 5, y1: 10 }], - ['yAccessorRight', 'above', { y0: 5, y1: 10 }, { y0: 10, y1: undefined }], - ['yAccessorRight', 'below', { y0: undefined, y1: 5 }, { y0: 5, y1: 10 }], - ] as Array<[string, YConfig['fill'], YCoords, YCoords]>)( - 'should avoid overlap between two reference lines with fill in the same direction: 2 x %s %s', - (layerPrefix, fill, coordsA, coordsB) => { - const axisMode = getAxisFromId(layerPrefix); - const wrapper = shallow( - - ); - - expect(wrapper.find(RectAnnotation).first().prop('dataValues')).toEqual( - expect.arrayContaining([ - { - coordinates: { ...emptyCoords, ...coordsA }, - details: coordsA.y0 ?? coordsA.y1, - header: undefined, - }, - ]) - ); - expect(wrapper.find(RectAnnotation).last().prop('dataValues')).toEqual( - expect.arrayContaining([ - { - coordinates: { ...emptyCoords, ...coordsB }, - details: coordsB.y1 ?? coordsB.y0, - header: undefined, - }, - ]) - ); - } - ); - - it.each([ - ['xAccessor', 'above', { x0: 1, x1: 2 }, { x0: 2, x1: undefined }], - ['xAccessor', 'below', { x0: undefined, x1: 1 }, { x0: 1, x1: 2 }], - ] as Array<[string, YConfig['fill'], XCoords, XCoords]>)( - 'should avoid overlap between two reference lines with fill in the same direction: 2 x %s %s', - (layerPrefix, fill, coordsA, coordsB) => { - const wrapper = shallow( - - ); - - expect(wrapper.find(RectAnnotation).first().prop('dataValues')).toEqual( - expect.arrayContaining([ - { - coordinates: { ...emptyCoords, ...coordsA }, - details: coordsA.x0 ?? coordsA.x1, - header: undefined, - }, - ]) - ); - expect(wrapper.find(RectAnnotation).last().prop('dataValues')).toEqual( - expect.arrayContaining([ - { - coordinates: { ...emptyCoords, ...coordsB }, - details: coordsB.x1 ?? coordsB.x0, - header: undefined, - }, - ]) - ); - } - ); - - it.each(['yAccessorLeft', 'yAccessorRight', 'xAccessor'])( - 'should let areas in different directions overlap: %s', - (layerPrefix) => { - const axisMode = getAxisFromId(layerPrefix); - - const wrapper = shallow( - - ); - - expect(wrapper.find(RectAnnotation).first().prop('dataValues')).toEqual( - expect.arrayContaining([ - { - coordinates: { ...emptyCoords, ...(axisMode === 'bottom' ? { x0: 1 } : { y0: 5 }) }, - details: axisMode === 'bottom' ? 1 : 5, - header: undefined, - }, - ]) - ); - expect(wrapper.find(RectAnnotation).last().prop('dataValues')).toEqual( - expect.arrayContaining([ - { - coordinates: { ...emptyCoords, ...(axisMode === 'bottom' ? { x1: 2 } : { y1: 10 }) }, - details: axisMode === 'bottom' ? 2 : 10, - header: undefined, - }, - ]) - ); - } - ); - - it.each([ - ['above', { y0: 5, y1: 10 }, { y0: 10, y1: undefined }], - ['below', { y0: undefined, y1: 5 }, { y0: 5, y1: 10 }], - ] as Array<[YConfig['fill'], YCoords, YCoords]>)( - 'should be robust and works also for different axes when on same direction: 1x Left + 1x Right both %s', - (fill, coordsA, coordsB) => { - const wrapper = shallow( - - ); - - expect(wrapper.find(RectAnnotation).first().prop('dataValues')).toEqual( - expect.arrayContaining([ - { - coordinates: { ...emptyCoords, ...coordsA }, - details: coordsA.y0 ?? coordsA.y1, - header: undefined, - }, - ]) - ); - expect(wrapper.find(RectAnnotation).last().prop('dataValues')).toEqual( - expect.arrayContaining([ - { - coordinates: { ...emptyCoords, ...coordsB }, - details: coordsB.y1 ?? coordsB.y0, - header: undefined, - }, - ]) - ); - } - ); - }); -}); diff --git a/x-pack/plugins/lens/public/xy_visualization/expression_thresholds.scss b/x-pack/plugins/lens/public/xy_visualization/expression_thresholds.scss deleted file mode 100644 index 41b30ce40676b..0000000000000 --- a/x-pack/plugins/lens/public/xy_visualization/expression_thresholds.scss +++ /dev/null @@ -1,18 +0,0 @@ -.lnsXyDecorationRotatedWrapper { - display: inline-block; - overflow: hidden; - line-height: $euiLineHeight; - - .lnsXyDecorationRotatedWrapper__label { - display: inline-block; - white-space: nowrap; - transform: translate(0, 100%) rotate(-90deg); - transform-origin: 0 0; - - &::after { - content: ''; - float: left; - margin-top: 100%; - } - } -} \ No newline at end of file diff --git a/x-pack/plugins/lens/public/xy_visualization/index.ts b/x-pack/plugins/lens/public/xy_visualization/index.ts index 9697ba149e16e..fb2ffa3743cfa 100644 --- a/x-pack/plugins/lens/public/xy_visualization/index.ts +++ b/x-pack/plugins/lens/public/xy_visualization/index.ts @@ -10,7 +10,6 @@ import type { ExpressionsSetup } from '../../../../../src/plugins/expressions/pu import type { EditorFrameSetup } from '../types'; import type { ChartsPluginSetup } from '../../../../../src/plugins/charts/public'; import type { LensPluginStartDependencies } from '../plugin'; -import { getTimeZone } from '../utils'; import type { FormatFactory } from '../../common'; import { LEGACY_TIME_AXIS } from '../../../../../src/plugins/charts/common'; @@ -24,24 +23,13 @@ export interface XyVisualizationPluginSetupPlugins { export class XyVisualization { setup( core: CoreSetup, - { expressions, formatFactory, editorFrame }: XyVisualizationPluginSetupPlugins + { editorFrame }: XyVisualizationPluginSetupPlugins ) { editorFrame.registerVisualization(async () => { - const { getXyChartRenderer, getXyVisualization } = await import('../async_services'); + const { getXyVisualization } = await import('../async_services'); const [, { charts, fieldFormats }] = await core.getStartServices(); const palettes = await charts.palettes.getPalettes(); const useLegacyTimeAxis = core.uiSettings.get(LEGACY_TIME_AXIS); - expressions.registerRenderer( - getXyChartRenderer({ - formatFactory, - chartsThemeService: charts.theme, - chartsActiveCursorService: charts.activeCursor, - paletteService: palettes, - timeZone: getTimeZone(core.uiSettings), - useLegacyTimeAxis, - kibanaTheme: core.theme, - }) - ); return getXyVisualization({ paletteService: palettes, fieldFormats, diff --git a/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.test.ts b/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.test.ts index 9def75615e6c8..d847e6f1afdc1 100644 --- a/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { XYDataLayerConfig } from '../../common/expressions'; +import { DataLayerConfigResult } from '../../../../../src/plugins/chart_expressions/expression_xy/common'; import { FramePublicAPI } from '../types'; import { computeOverallDataDomain, getStaticValue } from './reference_line_helpers'; @@ -51,7 +51,7 @@ describe('reference_line helpers', () => { // accessor id has no hit in data expect( getStaticValue( - [{ layerId: 'id-a', seriesType: 'area' } as XYDataLayerConfig], // missing xAccessor for groupId == x + [{ layerId: 'id-a', seriesType: 'area' } as DataLayerConfigResult], // missing xAccessor for groupId == x 'x', { activeData: getActiveData([ @@ -69,7 +69,7 @@ describe('reference_line helpers', () => { seriesType: 'area', layerType: 'data', accessors: ['d'], - } as XYDataLayerConfig, + } as DataLayerConfigResult, ], // missing hit of accessor "d" in data 'yLeft', { @@ -88,7 +88,7 @@ describe('reference_line helpers', () => { seriesType: 'area', layerType: 'data', accessors: ['a'], - } as XYDataLayerConfig, + } as DataLayerConfigResult, ], // missing yConfig fallbacks to left axis, but the requested group is yRight 'yRight', { @@ -107,7 +107,7 @@ describe('reference_line helpers', () => { seriesType: 'area', layerType: 'data', accessors: ['a'], - } as XYDataLayerConfig, + } as DataLayerConfigResult, ], // same as above with x groupId 'x', { @@ -130,7 +130,7 @@ describe('reference_line helpers', () => { layerType: 'data', accessors: ['a'], yConfig: [{ forAccessor: 'a', axisMode: 'right' }], - } as XYDataLayerConfig, + } as DataLayerConfigResult, ], 'yRight', { @@ -155,7 +155,7 @@ describe('reference_line helpers', () => { seriesType: 'area', layerType: 'data', accessors: ['a'], - } as XYDataLayerConfig, + } as DataLayerConfigResult, ], 'yLeft', { @@ -178,7 +178,7 @@ describe('reference_line helpers', () => { layerType: 'data', accessors: ['a'], yConfig: [{ forAccessor: 'a', axisMode: 'right' }], - } as XYDataLayerConfig, + } as DataLayerConfigResult, ], 'yRight', { @@ -205,7 +205,7 @@ describe('reference_line helpers', () => { seriesType: 'area', layerType: 'data', accessors: ['a', 'b'], - } as XYDataLayerConfig, + } as DataLayerConfigResult, ], 'yLeft', { activeData: tables }, @@ -220,7 +220,7 @@ describe('reference_line helpers', () => { seriesType: 'area', layerType: 'data', accessors: ['a', 'b'], - } as XYDataLayerConfig, + } as DataLayerConfigResult, ], 'yRight', { activeData: tables }, @@ -243,7 +243,7 @@ describe('reference_line helpers', () => { seriesType: 'area', layerType: 'data', accessors: ['a', 'b'], - } as XYDataLayerConfig, + } as DataLayerConfigResult, ], 'yLeft', { activeData: tables }, @@ -258,7 +258,7 @@ describe('reference_line helpers', () => { seriesType: 'area', layerType: 'data', accessors: ['a', 'b'], - } as XYDataLayerConfig, + } as DataLayerConfigResult, ], 'yRight', { activeData: tables }, @@ -277,7 +277,12 @@ describe('reference_line helpers', () => { layerType: 'data', xAccessor: 'a', accessors: [], - } as XYDataLayerConfig, + type: 'lens_xy_data_layer', + yScaleType: 'linear', + xScaleType: 'linear', + isHistogram: false, + palette: { type: 'palette', name: 'palette1' }, + } as DataLayerConfigResult, ], 'x', // this is influenced by the callback { @@ -300,7 +305,12 @@ describe('reference_line helpers', () => { layerType: 'data', xAccessor: 'a', accessors: [], - } as XYDataLayerConfig, + type: 'lens_xy_data_layer', + yScaleType: 'linear', + xScaleType: 'linear', + isHistogram: false, + palette: { type: 'palette', name: 'palette1' }, + } as DataLayerConfigResult, ], 'x', { @@ -324,7 +334,7 @@ describe('reference_line helpers', () => { for (const seriesType of ['bar_stacked', 'bar_horizontal_stacked', 'area_stacked']) expect( computeOverallDataDomain( - [{ layerId: 'id-a', seriesType, accessors: ['a', 'b', 'c'] } as XYDataLayerConfig], + [{ layerId: 'id-a', seriesType, accessors: ['a', 'b', 'c'] } as DataLayerConfigResult], ['a', 'b', 'c'], getActiveData([ { @@ -350,7 +360,7 @@ describe('reference_line helpers', () => { ]) expect( computeOverallDataDomain( - [{ layerId: 'id-a', seriesType, accessors: ['a', 'b', 'c'] } as XYDataLayerConfig], + [{ layerId: 'id-a', seriesType, accessors: ['a', 'b', 'c'] } as DataLayerConfigResult], ['a', 'b', 'c'], getActiveData([ { @@ -375,7 +385,7 @@ describe('reference_line helpers', () => { [ { layerId: 'id-a', seriesType, accessors: ['a', 'b', 'c'] }, { layerId: 'id-b', seriesType, accessors: ['d', 'e', 'f'] }, - ] as XYDataLayerConfig[], + ] as DataLayerConfigResult[], ['a', 'b', 'c', 'd', 'e', 'f'], getActiveData([ { id: 'id-a', rows: [{ a: 25, b: 100, c: 100 }] }, @@ -389,7 +399,7 @@ describe('reference_line helpers', () => { [ { layerId: 'id-a', seriesType, accessors: ['a', 'b', 'c'] }, { layerId: 'id-b', seriesType, accessors: ['d', 'e', 'f'] }, - ] as XYDataLayerConfig[], + ] as DataLayerConfigResult[], ['a', 'b', 'c', 'd', 'e', 'f'], getActiveData([ { @@ -425,7 +435,7 @@ describe('reference_line helpers', () => { [ { layerId: 'id-a', seriesType, accessors: ['a', 'b', 'c'] }, { layerId: 'id-b', seriesType, accessors: ['d', 'e', 'f'] }, - ] as XYDataLayerConfig[], + ] as DataLayerConfigResult[], ['a', 'b', 'c', 'd', 'e', 'f'], getActiveData([ { id: 'id-a', rows: Array(3).fill({ a: 100, b: 100, c: 100 }) }, @@ -443,7 +453,7 @@ describe('reference_line helpers', () => { [ { layerId: 'id-a', seriesType: nonStackedSeries, accessors: ['a', 'b', 'c'] }, { layerId: 'id-b', seriesType: stackedSeries, accessors: ['d', 'e', 'f'] }, - ] as XYDataLayerConfig[], + ] as DataLayerConfigResult[], ['a', 'b', 'c', 'd', 'e', 'f'], getActiveData([ { id: 'id-a', rows: [{ a: 100, b: 100, c: 100 }] }, @@ -465,7 +475,7 @@ describe('reference_line helpers', () => { [ { layerId: 'id-a', seriesType, xAccessor: 'c', accessors: ['a', 'b'] }, { layerId: 'id-b', seriesType, xAccessor: 'f', accessors: ['d', 'e'] }, - ] as XYDataLayerConfig[], + ] as DataLayerConfigResult[], ['a', 'b', 'd', 'e'], getActiveData([ { @@ -492,7 +502,7 @@ describe('reference_line helpers', () => { [ { layerId: 'id-a', seriesType, accessors: ['c'] }, { layerId: 'id-b', seriesType, accessors: ['f'] }, - ] as XYDataLayerConfig[], + ] as DataLayerConfigResult[], ['c', 'f'], getActiveData([ { @@ -520,7 +530,7 @@ describe('reference_line helpers', () => { [ { layerId: 'id-a', seriesType, xAccessor: 'c', accessors: ['a', 'b'] }, { layerId: 'id-b', seriesType, xAccessor: 'f', accessors: ['d', 'e'] }, - ] as XYDataLayerConfig[], + ] as DataLayerConfigResult[], ['a', 'b', 'd', 'e'], getActiveData([ { @@ -549,7 +559,7 @@ describe('reference_line helpers', () => { layerId: 'id-a', seriesType: 'area_stacked', accessors: ['a', 'b', 'c'], - } as XYDataLayerConfig, + } as DataLayerConfigResult, ], ['a', 'b', 'c'], getActiveData([ @@ -573,7 +583,7 @@ describe('reference_line helpers', () => { layerId: 'id-a', seriesType: 'area', accessors: ['a', 'b', 'c'], - } as XYDataLayerConfig, + } as DataLayerConfigResult, ], ['a', 'b', 'c'], getActiveData([ @@ -608,7 +618,7 @@ describe('reference_line helpers', () => { [ { layerId: 'id-a', seriesType: 'area', accessors: ['a', 'b', 'c'] }, { layerId: 'id-b', seriesType: 'line', accessors: ['d', 'e', 'f'] }, - ] as XYDataLayerConfig[], + ] as DataLayerConfigResult[], ['a', 'b'], getActiveData([{ id: 'id-c', rows: [{ a: 100, b: 100 }] }]) // mind the layer id here ) @@ -619,7 +629,7 @@ describe('reference_line helpers', () => { [ { layerId: 'id-a', seriesType: 'bar', accessors: ['a', 'b', 'c'] }, { layerId: 'id-b', seriesType: 'bar_stacked' }, - ] as XYDataLayerConfig[], + ] as DataLayerConfigResult[], ['a', 'b'], getActiveData([]) ) diff --git a/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx b/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx index 05a81b15efaba..5cdb110098987 100644 --- a/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx @@ -8,7 +8,13 @@ import { groupBy, partition } from 'lodash'; import { i18n } from '@kbn/i18n'; import { layerTypes } from '../../common'; -import type { XYDataLayerConfig, XYLayerConfig, YConfig } from '../../common/expressions'; +import type { + DataLayerConfigResult, + XYDataLayerConfig, + XYLayerConfig, + YAxisMode, + YConfig, +} from '../../../../../src/plugins/chart_expressions/expression_xy/common'; import { Datatable } from '../../../../../src/plugins/expressions/public'; import type { AccessorConfig, DatasourcePublicAPI, FramePublicAPI, Visualization } from '../types'; import { groupAxesByType } from './axes_configuration'; @@ -68,7 +74,7 @@ export function getGroupsRelatedToData( * Returns a dictionary with the groups filled in all the data layers */ export function getGroupsAvailableInData( - dataLayers: XYDataLayerConfig[], + dataLayers: DataLayerConfigResult[], datasourceLayers: Record, tables: Record | undefined ) { @@ -84,7 +90,7 @@ export function getGroupsAvailableInData( } export function getStaticValue( - dataLayers: XYDataLayerConfig[], + dataLayers: DataLayerConfigResult[], groupId: 'x' | 'yLeft' | 'yRight', { activeData }: Pick, layerHasNumberHistogram: (layer: XYDataLayerConfig) => boolean @@ -120,7 +126,7 @@ export function getStaticValue( function getAccessorCriteriaForGroup( groupId: 'x' | 'yLeft' | 'yRight', - dataLayers: XYDataLayerConfig[], + dataLayers: DataLayerConfigResult[], activeData: FramePublicAPI['activeData'] ) { switch (groupId) { @@ -333,20 +339,26 @@ export const setReferenceDimension: Visualization['setDimension'] = ({ ? newLayer.yConfig?.find(({ forAccessor }) => forAccessor === previousColumn) : false; if (!hasYConfig) { + const axisMode: YAxisMode = + groupId === 'xReferenceLine' + ? 'bottom' + : groupId === 'yReferenceLineRight' + ? 'right' + : 'left'; + newLayer.yConfig = [ ...(newLayer.yConfig || []), - { - // override with previous styling, - ...previousYConfig, - // but keep the new group & id config - forAccessor: columnId, - axisMode: - groupId === 'xReferenceLine' - ? 'bottom' - : groupId === 'yReferenceLineRight' - ? 'right' - : 'left', - }, + ...(previousYConfig + ? [ + { + // override with previous styling, + ...previousYConfig, + // but keep the new group & id config + forAccessor: columnId, + axisMode, + }, + ] + : []), ]; } return { diff --git a/x-pack/plugins/lens/public/xy_visualization/state_helpers.ts b/x-pack/plugins/lens/public/xy_visualization/state_helpers.ts index f37ff5460f314..bda0317e9f7ea 100644 --- a/x-pack/plugins/lens/public/xy_visualization/state_helpers.ts +++ b/x-pack/plugins/lens/public/xy_visualization/state_helpers.ts @@ -7,7 +7,12 @@ import { EuiIconType } from '@elastic/eui/src/components/icon/icon'; import type { FramePublicAPI, DatasourcePublicAPI } from '../types'; -import type { SeriesType, XYLayerConfig, YConfig, ValidLayer } from '../../common/expressions'; +import type { + SeriesType, + XYLayerConfig, + YConfig, + ValidLayer, +} from '../../../../../src/plugins/chart_expressions/expression_xy/common'; import { visualizationTypes } from './types'; import { getDataLayers, isDataLayer } from './visualization_helpers'; diff --git a/x-pack/plugins/lens/public/xy_visualization/to_expression.test.ts b/x-pack/plugins/lens/public/xy_visualization/to_expression.test.ts index ac3fdcf30a4ad..bbfe911bdcd67 100644 --- a/x-pack/plugins/lens/public/xy_visualization/to_expression.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/to_expression.test.ts @@ -75,6 +75,11 @@ describe('#toExpression', () => { splitAccessor: 'd', xAccessor: 'a', accessors: ['b', 'c'], + xScaleType: 'linear', + yScaleType: 'linear', + isHistogram: false, + type: 'lens_xy_data_layer', + palette: { type: 'palette', name: 'default' }, }, ], }, @@ -99,6 +104,11 @@ describe('#toExpression', () => { splitAccessor: 'd', xAccessor: 'a', accessors: ['b', 'c'], + xScaleType: 'linear', + yScaleType: 'linear', + isHistogram: false, + type: 'lens_xy_data_layer', + palette: { type: 'palette', name: 'default' }, }, ], }, @@ -122,6 +132,11 @@ describe('#toExpression', () => { splitAccessor: 'd', xAccessor: 'a', accessors: ['b', 'c'], + xScaleType: 'linear', + yScaleType: 'linear', + isHistogram: false, + type: 'lens_xy_data_layer', + palette: { type: 'palette', name: 'default' }, }, ], }, @@ -150,6 +165,11 @@ describe('#toExpression', () => { splitAccessor: undefined, xAccessor: undefined, accessors: ['a'], + xScaleType: 'linear', + yScaleType: 'linear', + isHistogram: false, + type: 'lens_xy_data_layer', + palette: { type: 'palette', name: 'default' }, }, ], }, @@ -175,6 +195,11 @@ describe('#toExpression', () => { splitAccessor: undefined, xAccessor: 'a', accessors: [], + xScaleType: 'linear', + yScaleType: 'linear', + isHistogram: false, + type: 'lens_xy_data_layer', + palette: { type: 'palette', name: 'default' }, }, ], }, @@ -197,6 +222,11 @@ describe('#toExpression', () => { splitAccessor: 'd', xAccessor: 'a', accessors: ['b', 'c'], + xScaleType: 'linear', + yScaleType: 'linear', + isHistogram: false, + type: 'lens_xy_data_layer', + palette: { type: 'palette', name: 'default' }, }, ], }, @@ -234,6 +264,11 @@ describe('#toExpression', () => { splitAccessor: 'd', xAccessor: 'a', accessors: ['b', 'c'], + xScaleType: 'linear', + yScaleType: 'linear', + isHistogram: false, + type: 'lens_xy_data_layer', + palette: { type: 'palette', name: 'default' }, }, ], }, @@ -262,6 +297,11 @@ describe('#toExpression', () => { splitAccessor: 'd', xAccessor: 'a', accessors: ['b', 'c'], + xScaleType: 'linear', + yScaleType: 'linear', + isHistogram: false, + type: 'lens_xy_data_layer', + palette: { type: 'palette', name: 'default' }, }, ], }, @@ -288,6 +328,11 @@ describe('#toExpression', () => { splitAccessor: 'd', xAccessor: 'a', accessors: ['b', 'c'], + xScaleType: 'linear', + yScaleType: 'linear', + isHistogram: false, + type: 'lens_xy_data_layer', + palette: { type: 'palette', name: 'default' }, }, ], }, @@ -316,6 +361,11 @@ describe('#toExpression', () => { splitAccessor: 'd', xAccessor: 'a', accessors: ['b', 'c'], + xScaleType: 'linear', + yScaleType: 'linear', + isHistogram: false, + type: 'lens_xy_data_layer', + palette: { type: 'palette', name: 'default' }, }, ], }, @@ -338,13 +388,19 @@ describe('#toExpression', () => { splitAccessor: 'd', xAccessor: 'a', accessors: ['b', 'c'], - yConfig: [{ forAccessor: 'a' }], + yConfig: [{ forAccessor: 'a', type: 'lens_xy_yConfig' }], + xScaleType: 'linear', + yScaleType: 'linear', + isHistogram: false, + type: 'lens_xy_data_layer', + palette: { type: 'palette', name: 'default' }, }, { layerId: 'referenceLine', layerType: layerTypes.REFERENCELINE, accessors: ['b', 'c'], - yConfig: [{ forAccessor: 'a' }], + yConfig: [{ forAccessor: 'a', type: 'lens_xy_yConfig' }], + type: 'lens_xy_referenceLine_layer', }, ], }, diff --git a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts index ec93295d647bf..4701fec2268c8 100644 --- a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts +++ b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts @@ -12,11 +12,12 @@ import { State } from './types'; import { OperationMetadata, DatasourcePublicAPI } from '../types'; import { getColumnToLabelMap } from './state_helpers'; import type { + ReferenceLineLayerConfigResult, ValidLayer, XYLayerConfig, XYReferenceLineLayerConfig, YConfig, -} from '../../common/expressions'; +} from '../../../../../src/plugins/chart_expressions/expression_xy/common'; import { layerTypes } from '../../common'; import { hasIcon } from './xy_config_panel/shared/icon_select'; import { defaultReferenceLineColor } from './color_assignment'; @@ -125,7 +126,7 @@ export const buildExpression = ( attributes: Partial<{ title: string; description: string }> = {} ): Ast | null => { const validLayers = state.layers - .filter((layer): layer is ValidLayer => Boolean(layer.accessors.length)) + .filter((layer): layer is ValidLayer => Boolean(layer.accessors.length)) .map((layer) => { if (!datasourceLayers) { return layer; @@ -324,7 +325,7 @@ export const buildExpression = ( }; const referenceLineLayerToExpression = ( - layer: XYReferenceLineLayerConfig, + layer: ReferenceLineLayerConfigResult, datasourceLayer: DatasourcePublicAPI ): Ast => { return { diff --git a/x-pack/plugins/lens/public/xy_visualization/types.ts b/x-pack/plugins/lens/public/xy_visualization/types.ts index b59d69bd8cbe6..69d34da25c05e 100644 --- a/x-pack/plugins/lens/public/xy_visualization/types.ts +++ b/x-pack/plugins/lens/public/xy_visualization/types.ts @@ -27,7 +27,7 @@ import type { AxesSettingsConfig, FittingFunction, LabelsOrientationConfig, -} from '../../common/expressions'; +} from '../../../../../src/plugins/chart_expressions/expression_xy/common'; import type { ValueLabelConfig } from '../../common/types'; // Persisted parts of the state diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts b/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts index 89b496a785d9f..f7409ef87277c 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts @@ -8,8 +8,13 @@ import { getXyVisualization } from './visualization'; import { Position } from '@elastic/charts'; import { Operation, VisualizeEditorContext, Suggestion, OperationDescriptor } from '../types'; -import type { State, XYSuggestion } from './types'; -import type { SeriesType, XYDataLayerConfig, XYLayerConfig } from '../../common/expressions'; +import type { State, XYState, XYSuggestion } from './types'; +import type { + DataLayerConfigResult, + SeriesType, + XYDataLayerConfig, + XYLayerConfig, +} from '../../../../../src/plugins/chart_expressions/expression_xy/common'; import { layerTypes } from '../../common'; import { createMockDatasource, createMockFramePublicAPI } from '../mocks'; import { LensIconChartBar } from '../assets/chart_bar'; @@ -19,7 +24,7 @@ import { fieldFormatsServiceMock } from '../../../../../src/plugins/field_format import { Datatable } from 'src/plugins/expressions'; import { themeServiceMock } from '../../../../../src/core/public/mocks'; -function exampleState(): State { +function exampleState(): XYState { return { legend: { position: Position.Bottom, isVisible: true }, valueLabels: 'hide', @@ -32,7 +37,12 @@ function exampleState(): State { splitAccessor: 'd', xAccessor: 'a', accessors: ['b', 'c'], - } as XYDataLayerConfig, + xScaleType: 'linear', + yScaleType: 'linear', + isHistogram: false, + type: 'lens_xy_data_layer', + palette: { type: 'palette', name: 'default' }, + }, ], }; } @@ -100,12 +110,12 @@ describe('xy_visualization', () => { }); describe('#getVisualizationTypeId', () => { - function mixedState(...types: SeriesType[]) { + function mixedState(...types: SeriesType[]): XYState { const state = exampleState(); return { ...state, layers: types.map((t, i) => ({ - ...(state.layers[0] as XYDataLayerConfig), + ...(state.layers[0] as DataLayerConfigResult), layerId: `layer_${i}`, seriesType: t, })), @@ -187,6 +197,11 @@ describe('xy_visualization', () => { splitAccessor: 'e', xAccessor: 'f', accessors: ['g', 'h'], + xScaleType: 'linear', + yScaleType: 'linear', + isHistogram: false, + type: 'lens_xy_data_layer', + palette: { type: 'palette', name: 'default' }, }, ], }; @@ -278,6 +293,11 @@ describe('xy_visualization', () => { seriesType: 'area', xAccessor: undefined, accessors: [], + xScaleType: 'linear', + yScaleType: 'linear', + isHistogram: false, + type: 'lens_xy_data_layer', + palette: { type: 'palette', name: 'default' }, }, ], }, @@ -307,6 +327,11 @@ describe('xy_visualization', () => { seriesType: 'area', xAccessor: 'a', accessors: [], + xScaleType: 'linear', + yScaleType: 'linear', + isHistogram: false, + type: 'lens_xy_data_layer', + palette: { type: 'palette', name: 'default' }, }, ], }, @@ -334,6 +359,7 @@ describe('xy_visualization', () => { layerId: 'referenceLine', layerType: layerTypes.REFERENCELINE, accessors: [], + type: 'lens_xy_referenceLine_layer', }, ], }, @@ -415,6 +441,11 @@ describe('xy_visualization', () => { seriesType: 'line', xAccessor: undefined, accessors: ['a'], + xScaleType: 'linear', + yScaleType: 'linear', + isHistogram: false, + type: 'lens_xy_data_layer', + palette: { type: 'palette', name: 'default' }, }, ], }, @@ -633,6 +664,11 @@ describe('xy_visualization', () => { seriesType: 'area', xAccessor: 'a', accessors: [], + xScaleType: 'linear', + yScaleType: 'linear', + isHistogram: false, + type: 'lens_xy_data_layer', + palette: { type: 'palette', name: 'default' }, }, ], }, @@ -789,7 +825,7 @@ describe('xy_visualization', () => { ...baseState.layers[0], accessors: ['a'], seriesType: 'bar_percentage_stacked', - } as XYDataLayerConfig, + } as XYLayerConfig, ], }, frame, @@ -1008,12 +1044,18 @@ describe('xy_visualization', () => { splitAccessor: undefined, xAccessor: undefined, accessors: ['a'], + xScaleType: 'linear', + yScaleType: 'linear', + isHistogram: false, + type: 'lens_xy_data_layer', + palette: { type: 'palette', name: 'default' }, }, { layerId: 'referenceLine', layerType: layerTypes.REFERENCELINE, accessors: [], - yConfig: [{ axisMode: 'left', forAccessor: 'a' }], + yConfig: [{ axisMode: 'left', forAccessor: 'a', type: 'lens_xy_yConfig' }], + type: 'lens_xy_referenceLine_layer', }, ], }; @@ -1059,7 +1101,9 @@ describe('xy_visualization', () => { it('should return a group for the vertical right axis', () => { const state = getStateWithBaseReferenceLine(); - state.layers[0].yConfig = [{ axisMode: 'right', forAccessor: 'a' }]; + state.layers[0].yConfig = [ + { axisMode: 'right', forAccessor: 'a', type: 'lens_xy_yConfig' }, + ]; state.layers[1].yConfig![0].axisMode = 'right'; const options = xyVisualization.getConfiguration({ @@ -1141,13 +1185,13 @@ describe('xy_visualization', () => { (state.layers[0] as XYDataLayerConfig).accessors = ['a', 'b']; // invert them on purpose (state.layers[0] as XYDataLayerConfig).yConfig = [ - { axisMode: 'right', forAccessor: 'b' }, - { axisMode: 'left', forAccessor: 'a' }, + { axisMode: 'right', forAccessor: 'b', type: 'lens_xy_yConfig' }, + { axisMode: 'left', forAccessor: 'a', type: 'lens_xy_yConfig' }, ]; state.layers[1].yConfig = [ - { forAccessor: 'c', axisMode: 'bottom' }, - { forAccessor: 'b', axisMode: 'right' }, - { forAccessor: 'a', axisMode: 'left' }, + { forAccessor: 'c', axisMode: 'bottom', type: 'lens_xy_yConfig' }, + { forAccessor: 'b', axisMode: 'right', type: 'lens_xy_yConfig' }, + { forAccessor: 'a', axisMode: 'left', type: 'lens_xy_yConfig' }, ]; // set the xAccessor as number histogram frame.datasourceLayers.referenceLine.getOperationForColumnId = jest.fn((accessor) => { @@ -1321,7 +1365,7 @@ describe('xy_visualization', () => { ...baseState.layers[0], splitAccessor: undefined, ...layerConfigOverride, - } as XYDataLayerConfig, + } as XYLayerConfig, ], }, frame, @@ -1358,6 +1402,7 @@ describe('xy_visualization', () => { { forAccessor: 'b', color: 'red', + type: 'lens_xy_yConfig', }, ], }, @@ -1503,6 +1548,11 @@ describe('xy_visualization', () => { seriesType: 'area', xAccessor: 'a', accessors: [], + xScaleType: 'linear', + yScaleType: 'linear', + isHistogram: false, + type: 'lens_xy_data_layer', + palette: { type: 'palette', name: 'default' }, }, ], }) @@ -1519,6 +1569,11 @@ describe('xy_visualization', () => { seriesType: 'area', xAccessor: 'a', accessors: [], + xScaleType: 'linear', + yScaleType: 'linear', + isHistogram: false, + type: 'lens_xy_data_layer', + palette: { type: 'palette', name: 'default' }, }, { layerId: 'second', @@ -1526,6 +1581,11 @@ describe('xy_visualization', () => { seriesType: 'area', xAccessor: 'a', accessors: [], + xScaleType: 'linear', + yScaleType: 'linear', + isHistogram: false, + type: 'lens_xy_data_layer', + palette: { type: 'palette', name: 'default' }, }, ], }) @@ -1542,6 +1602,11 @@ describe('xy_visualization', () => { seriesType: 'area', xAccessor: 'a', accessors: ['a'], + xScaleType: 'linear', + yScaleType: 'linear', + isHistogram: false, + type: 'lens_xy_data_layer', + palette: { type: 'palette', name: 'default' }, }, { layerId: 'second', @@ -1550,6 +1615,11 @@ describe('xy_visualization', () => { xAccessor: undefined, accessors: ['a'], splitAccessor: 'a', + xScaleType: 'linear', + yScaleType: 'linear', + isHistogram: false, + type: 'lens_xy_data_layer', + palette: { type: 'palette', name: 'default' }, }, ], }) @@ -1567,6 +1637,11 @@ describe('xy_visualization', () => { xAccessor: undefined, accessors: [], splitAccessor: 'a', + xScaleType: 'linear', + yScaleType: 'linear', + isHistogram: false, + type: 'lens_xy_data_layer', + palette: { type: 'palette', name: 'default' }, }, ], }) @@ -1583,6 +1658,11 @@ describe('xy_visualization', () => { xAccessor: undefined, accessors: [], splitAccessor: 'a', + xScaleType: 'linear', + yScaleType: 'linear', + isHistogram: false, + type: 'lens_xy_data_layer', + palette: { type: 'palette', name: 'default' }, }, { layerId: 'second', @@ -1591,6 +1671,11 @@ describe('xy_visualization', () => { xAccessor: undefined, accessors: [], splitAccessor: 'a', + xScaleType: 'linear', + yScaleType: 'linear', + isHistogram: false, + type: 'lens_xy_data_layer', + palette: { type: 'palette', name: 'default' }, }, ], }) @@ -1607,6 +1692,11 @@ describe('xy_visualization', () => { seriesType: 'area', xAccessor: 'a', accessors: [], + xScaleType: 'linear', + yScaleType: 'linear', + isHistogram: false, + type: 'lens_xy_data_layer', + palette: { type: 'palette', name: 'default' }, }, { layerId: 'second', @@ -1614,6 +1704,11 @@ describe('xy_visualization', () => { seriesType: 'area', xAccessor: undefined, accessors: ['a'], + xScaleType: 'linear', + yScaleType: 'linear', + isHistogram: false, + type: 'lens_xy_data_layer', + palette: { type: 'palette', name: 'default' }, }, ], }) @@ -1635,6 +1730,11 @@ describe('xy_visualization', () => { seriesType: 'area', xAccessor: 'a', accessors: ['a'], + xScaleType: 'linear', + yScaleType: 'linear', + isHistogram: false, + type: 'lens_xy_data_layer', + palette: { type: 'palette', name: 'default' }, }, { layerId: 'second', @@ -1643,6 +1743,11 @@ describe('xy_visualization', () => { xAccessor: undefined, accessors: [], splitAccessor: 'a', + xScaleType: 'linear', + yScaleType: 'linear', + isHistogram: false, + type: 'lens_xy_data_layer', + palette: { type: 'palette', name: 'default' }, }, { layerId: 'third', @@ -1651,6 +1756,11 @@ describe('xy_visualization', () => { xAccessor: undefined, accessors: [], splitAccessor: 'a', + xScaleType: 'linear', + yScaleType: 'linear', + isHistogram: false, + type: 'lens_xy_data_layer', + palette: { type: 'palette', name: 'default' }, }, ], }) @@ -1672,6 +1782,11 @@ describe('xy_visualization', () => { seriesType: 'area', xAccessor: 'a', accessors: [], + xScaleType: 'linear', + yScaleType: 'linear', + isHistogram: false, + type: 'lens_xy_data_layer', + palette: { type: 'palette', name: 'default' }, }, { layerId: 'second', @@ -1679,6 +1794,11 @@ describe('xy_visualization', () => { seriesType: 'area', xAccessor: 'a', accessors: ['a'], + xScaleType: 'linear', + yScaleType: 'linear', + isHistogram: false, + type: 'lens_xy_data_layer', + palette: { type: 'palette', name: 'default' }, }, { layerId: 'third', @@ -1686,6 +1806,11 @@ describe('xy_visualization', () => { seriesType: 'area', xAccessor: 'a', accessors: ['a'], + xScaleType: 'linear', + yScaleType: 'linear', + isHistogram: false, + type: 'lens_xy_data_layer', + palette: { type: 'palette', name: 'default' }, }, ], }) @@ -1710,6 +1835,11 @@ describe('xy_visualization', () => { splitAccessor: 'd', xAccessor: 'a', accessors: ['b'], // just use a single accessor to avoid too much noise + xScaleType: 'linear', + yScaleType: 'linear', + isHistogram: false, + type: 'lens_xy_data_layer', + palette: { type: 'palette', name: 'default' }, }, ], }, @@ -1758,6 +1888,11 @@ describe('xy_visualization', () => { splitAccessor: 'd', xAccessor: 'a', accessors: ['b'], + xScaleType: 'linear', + yScaleType: 'linear', + isHistogram: false, + type: 'lens_xy_data_layer', + palette: { type: 'palette', name: 'default' }, }, { layerId: 'second', @@ -1766,6 +1901,11 @@ describe('xy_visualization', () => { splitAccessor: 'd', xAccessor: 'e', accessors: ['b'], + xScaleType: 'linear', + yScaleType: 'linear', + isHistogram: false, + type: 'lens_xy_data_layer', + palette: { type: 'palette', name: 'default' }, }, ], }, @@ -1814,6 +1954,11 @@ describe('xy_visualization', () => { splitAccessor: 'd', xAccessor: 'a', accessors: ['b'], + xScaleType: 'linear', + yScaleType: 'linear', + isHistogram: false, + type: 'lens_xy_data_layer', + palette: { type: 'palette', name: 'default' }, }, { layerId: 'second', @@ -1822,6 +1967,11 @@ describe('xy_visualization', () => { splitAccessor: 'd', xAccessor: 'e', accessors: ['b'], + xScaleType: 'linear', + yScaleType: 'linear', + isHistogram: false, + type: 'lens_xy_data_layer', + palette: { type: 'palette', name: 'default' }, }, ], }, @@ -1885,6 +2035,11 @@ describe('xy_visualization', () => { seriesType: 'area', xAccessor: 'a', accessors: ['b'], + xScaleType: 'linear', + yScaleType: 'linear', + isHistogram: false, + type: 'lens_xy_data_layer', + palette: { type: 'palette', name: 'default' }, }, ], }, diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx index 33b285bd19ea3..01d928201705f 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx @@ -21,7 +21,13 @@ import { DimensionEditor } from './xy_config_panel/dimension_editor'; import { LayerHeader } from './xy_config_panel/layer_header'; import type { Visualization, AccessorConfig, FramePublicAPI } from '../types'; import { State, visualizationTypes, XYSuggestion } from './types'; -import { SeriesType, XYDataLayerConfig, XYLayerConfig, YAxisMode } from '../../common/expressions'; +import { + SeriesType, + XYDataLayerConfig, + XYLayerConfig, + YAxisMode, + YConfigResult, +} from '../../../../../src/plugins/chart_expressions/expression_xy/common'; import { layerTypes } from '../../common'; import { isHorizontalChart } from './state_helpers'; import { toExpression, toPreviewExpression, getSortedAccessors } from './to_expression'; @@ -136,6 +142,11 @@ export const getXyVisualization = ({ seriesType: defaultSeriesType, showGridlines: false, layerType: layerTypes.DATA, + type: 'lens_xy_data_layer', + xScaleType: 'linear', + yScaleType: 'linear', + isHistogram: false, + palette: { name: 'default', type: 'palette' }, }, ], } @@ -291,11 +302,12 @@ export const getXyVisualization = ({ return prevState; } const axisMode = axisPosition as YAxisMode; - const yConfig = metrics.map((metric, idx) => { + const yConfig = metrics.map((metric, idx) => { return { color: metric.color, forAccessor: metric.accessor ?? foundLayer.accessors[idx], ...(axisMode && { axisMode }), + type: 'lens_xy_yConfig', }; }); const newLayer = { diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization_helpers.tsx b/x-pack/plugins/lens/public/xy_visualization/visualization_helpers.tsx index 69df0d80300b2..b327508a143fa 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization_helpers.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/visualization_helpers.tsx @@ -11,11 +11,12 @@ import { DatasourcePublicAPI, OperationMetadata, VisualizationType } from '../ty import { State, visualizationTypes, XYState } from './types'; import { isHorizontalChart } from './state_helpers'; import { + DataLayerConfigResult, + ReferenceLineLayerConfigResult, SeriesType, XYDataLayerConfig, XYLayerConfig, - XYReferenceLineLayerConfig, -} from '../../common/expressions'; +} from '../../../../../src/plugins/chart_expressions/expression_xy/common'; import { layerTypes } from '..'; import { LensIconChartBarHorizontal } from '../assets/chart_bar_horizontal'; import { LensIconChartMixedXy } from '../assets/chart_mixed_xy'; @@ -127,20 +128,22 @@ export function checkScaleOperation( }; } -export const isDataLayer = (layer: Pick): layer is XYDataLayerConfig => +export const isDataLayer = (layer: XYLayerConfig): layer is DataLayerConfigResult => layer.layerType === layerTypes.DATA || !layer.layerType; export const getDataLayers = (layers: XYLayerConfig[]) => - (layers || []).filter((layer): layer is XYDataLayerConfig => isDataLayer(layer)); + (layers || []).filter((layer): layer is DataLayerConfigResult => isDataLayer(layer)); export const getFirstDataLayer = (layers: XYLayerConfig[]) => - (layers || []).find((layer): layer is XYDataLayerConfig => isDataLayer(layer)); + (layers || []).find((layer): layer is DataLayerConfigResult => isDataLayer(layer)); -export const isReferenceLayer = (layer: XYLayerConfig): layer is XYReferenceLineLayerConfig => +export const isReferenceLayer = (layer: XYLayerConfig): layer is ReferenceLineLayerConfigResult => layer.layerType === layerTypes.REFERENCELINE; export const getReferenceLayers = (layers: XYLayerConfig[]) => - (layers || []).filter((layer): layer is XYReferenceLineLayerConfig => isReferenceLayer(layer)); + (layers || []).filter((layer): layer is ReferenceLineLayerConfigResult => + isReferenceLayer(layer) + ); export function getVisualizationType(state: State): VisualizationType | 'mixed' { if (!state.layers.length) { @@ -241,9 +244,23 @@ export function newLayerState( layerId: string, layerType: LayerType = layerTypes.DATA ): XYLayerConfig { + if (layerType === 'data') { + return { + type: 'lens_xy_data_layer', + layerId, + seriesType, + accessors: [], + layerType, + yScaleType: 'linear', + xScaleType: 'linear', + isHistogram: false, + palette: { type: 'palette', name: 'default' }, + }; + } + return { + type: 'lens_xy_referenceLine_layer', layerId, - seriesType, accessors: [], layerType, }; @@ -257,7 +274,7 @@ export function getLayersByType(state: State, byType?: string) { export function validateLayersForDimension( dimension: string, - layers: XYDataLayerConfig[], + layers: DataLayerConfigResult[], missingCriteria: (layer: XYDataLayerConfig) => boolean ): | { valid: true } diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/axis_settings_popover.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/axis_settings_popover.tsx index 3766e1f022c88..06b8f3d399256 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/axis_settings_popover.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/axis_settings_popover.tsx @@ -19,7 +19,11 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { isEqual } from 'lodash'; -import { XYLayerConfig, AxesSettingsConfig, AxisExtentConfig } from '../../../common/expressions'; +import { + XYLayerConfig, + AxesSettingsConfig, + AxisExtentConfig, +} from '../../../../../../src/plugins/chart_expressions/expression_xy/common'; import { ToolbarPopover, useDebouncedValue, diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/color_picker.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/color_picker.tsx index 5db92e2dbb568..b3fad0e7dcfdf 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/color_picker.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/color_picker.tsx @@ -109,6 +109,7 @@ export const ColorPicker = ({ newYConfigs.push({ forAccessor: accessor, color: output.hex, + type: 'lens_xy_yConfig', }); } setState(updateLayer(state, { ...layer, yConfig: newYConfigs }, index)); diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/dimension_editor.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/dimension_editor.tsx index dce32d1d6b116..5f67941b970bb 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/dimension_editor.tsx @@ -12,7 +12,7 @@ import type { PaletteRegistry } from 'src/plugins/charts/public'; import type { VisualizationDimensionEditorProps } from '../../types'; import { State } from '../types'; import { FormatFactory } from '../../../common'; -import { YAxisMode } from '../../../common/expressions'; +import { YAxisMode } from '../../../../../../src/plugins/chart_expressions/expression_xy/common'; import { isHorizontalChart } from '../state_helpers'; import { ColorPicker } from './color_picker'; import { ReferenceLinePanel } from './reference_line_panel'; @@ -138,6 +138,7 @@ export function DimensionEditor( newYAxisConfigs.push({ forAccessor: accessor, axisMode: newMode, + type: 'lens_xy_yConfig', }); } setState(updateLayer(state, { ...layer, yConfig: newYAxisConfigs }, index)); diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/index.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/index.tsx index 240b85a74eb2e..256f6f1a3e96c 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/index.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/index.tsx @@ -11,7 +11,10 @@ import { Position, ScaleType, VerticalAlignment, HorizontalAlignment } from '@el import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import type { VisualizationToolbarProps, FramePublicAPI } from '../../types'; import { State, XYState } from '../types'; -import { AxesSettingsConfig, AxisExtentConfig } from '../../../common/expressions'; +import { + AxesSettingsConfig, + AxisExtentConfig, +} from '../../../../../../src/plugins/chart_expressions/expression_xy/common'; import { isHorizontalChart } from '../state_helpers'; import { LegendSettingsPopover } from '../../shared_components'; import { AxisSettingsPopover } from './axis_settings_popover'; diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/layer_header.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/layer_header.tsx index 465a627fa33b2..6044b58d8207f 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/layer_header.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/layer_header.tsx @@ -10,7 +10,10 @@ import { i18n } from '@kbn/i18n'; import { EuiIcon, EuiPopover, EuiSelectable, EuiText, EuiPopoverTitle } from '@elastic/eui'; import type { VisualizationLayerWidgetProps, VisualizationType } from '../../types'; import { State, visualizationTypes } from '../types'; -import { SeriesType, XYDataLayerConfig } from '../../../common/expressions'; +import { + DataLayerConfigResult, + SeriesType, +} from '../../../../../../src/plugins/chart_expressions/expression_xy/common'; import { isHorizontalChart, isHorizontalSeries } from '../state_helpers'; import { trackUiEvent } from '../../lens_ui_telemetry'; import { StaticHeader } from '../../shared_components'; @@ -44,8 +47,9 @@ function ReferenceLayerHeader() { function DataLayerHeader(props: VisualizationLayerWidgetProps) { const [isPopoverOpen, setPopoverIsOpen] = useState(false); const { state, layerId } = props; - const index = state.layers.findIndex((l) => l.layerId === layerId); - const layer = state.layers[index] as XYDataLayerConfig; + const layers = state.layers as DataLayerConfigResult[]; + const index = layers.findIndex((l) => l.layerId === layerId); + const layer = layers[index]; const currentVisType = visualizationTypes.find(({ id }) => id === layer.seriesType)!; const horizontalOnly = isHorizontalChart(state.layers); diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_panel.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_panel.tsx index 02cd9d45a4190..2842ef96d7d7c 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_panel.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_panel.tsx @@ -12,9 +12,10 @@ import type { PaletteRegistry } from 'src/plugins/charts/public'; import type { VisualizationDimensionEditorProps } from '../../types'; import { State, XYState } from '../types'; import { FormatFactory } from '../../../common'; -import { YConfig } from '../../../common/expressions'; -import { FillStyle } from '../../../common/expressions/xy_chart'; - +import { + YConfig, + FillStyle, +} from '../../../../../../src/plugins/chart_expressions/expression_xy/common'; import { ColorPicker } from './color_picker'; import { updateLayer } from '.'; import { useDebouncedValue } from '../../shared_components'; @@ -56,6 +57,7 @@ export const ReferenceLinePanel = ( newYConfigs.push({ forAccessor: accessor, ...yConfig, + type: 'lens_xy_yConfig', }); } setLocalState(updateLayer(localState, { ...layer, yConfig: newYConfigs }, index)); diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/shared/line_style_settings.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/shared/line_style_settings.tsx index e10156c2c31c1..2b6e7bd76e513 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/shared/line_style_settings.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/shared/line_style_settings.tsx @@ -14,8 +14,11 @@ import { EuiFlexItem, EuiFormRow, } from '@elastic/eui'; -import { YConfig } from '../../../../common/expressions'; -import { LineStyle } from '../../../../common/expressions/xy_chart'; +import { + LineStyle, + YConfig, +} from '../../../../../../../src/plugins/chart_expressions/expression_xy/common'; + import { idPrefix } from '../dimension_editor'; export const LineStyleSettings = ({ diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/shared/marker_decoration_settings.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/shared/marker_decoration_settings.tsx index 9579cbd1f0c0b..33f42831f6fec 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/shared/marker_decoration_settings.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/shared/marker_decoration_settings.tsx @@ -8,8 +8,10 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { EuiButtonGroup, EuiFormRow } from '@elastic/eui'; -import { YConfig } from '../../../../common/expressions'; -import { IconPosition } from '../../../../common/expressions/xy_chart'; +import { + IconPosition, + YConfig, +} from '../../../../../../../src/plugins/chart_expressions/expression_xy/common'; import { TooltipWrapper } from '../../../shared_components'; import { hasIcon, IconSelect } from './icon_select'; diff --git a/x-pack/plugins/lens/common/expressions/xy_chart/fitting_function.ts b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/fitting_function_definitions.ts similarity index 88% rename from x-pack/plugins/lens/common/expressions/xy_chart/fitting_function.ts rename to x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/fitting_function_definitions.ts index 0cfea62d578d7..a357f7c2f92ca 100644 --- a/x-pack/plugins/lens/common/expressions/xy_chart/fitting_function.ts +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/fitting_function_definitions.ts @@ -6,10 +6,9 @@ */ import { i18n } from '@kbn/i18n'; +import type { FittingFunction } from '../../../../../../../src/plugins/chart_expressions/expression_xy/common'; -export type FittingFunction = typeof fittingFunctionDefinitions[number]['id']; - -export const fittingFunctionDefinitions = [ +export const fittingFunctionDefinitions: Array<{ id: FittingFunction } & Record> = [ { id: 'None', title: i18n.translate('xpack.lens.fittingFunctionsTitle.none', { @@ -55,4 +54,4 @@ export const fittingFunctionDefinitions = [ defaultMessage: 'Fill gaps with the next value', }), }, -] as const; +]; diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/index.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/index.tsx index 0436a93be94ee..54fd07d5ce688 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/index.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/index.tsx @@ -13,7 +13,7 @@ import { LineCurveOption } from './line_curve_option'; import { FillOpacityOption } from './fill_opacity_option'; import { XYState } from '../../types'; import { hasHistogramSeries } from '../../state_helpers'; -import { ValidLayer } from '../../../../common/expressions'; +import { ValidLayer } from '../../../../../../../src/plugins/chart_expressions/expression_xy/common'; import type { FramePublicAPI } from '../../../types'; import { getDataLayers } from '../../visualization_helpers'; diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/line_curve_option.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/line_curve_option.tsx index 96926412afb8a..9e69c427d99fe 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/line_curve_option.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/line_curve_option.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { EuiFormRow, EuiSwitch } from '@elastic/eui'; -import type { XYCurveType } from '../../../../common/expressions'; +import type { XYCurveType } from '../../../../../../../src/plugins/chart_expressions/expression_xy/common'; export interface LineCurveOptionProps { /** diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/missing_values_option.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/missing_values_option.tsx index a858d1c879efe..87547702a5be1 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/missing_values_option.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/missing_values_option.tsx @@ -8,8 +8,8 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { EuiFormRow, EuiIconTip, EuiSuperSelect, EuiText } from '@elastic/eui'; -import { fittingFunctionDefinitions } from '../../../../common/expressions'; -import type { FittingFunction } from '../../../../common/expressions'; +import { fittingFunctionDefinitions } from './fitting_function_definitions'; +import type { FittingFunction } from '../../../../../../../src/plugins/chart_expressions/expression_xy/common'; export interface MissingValuesOptionProps { fittingFunction?: FittingFunction; @@ -65,7 +65,7 @@ export const MissingValuesOptions: React.FC = ({ }; })} valueOfSelected={fittingFunction || 'None'} - onChange={(value) => onFittingFnChange(value)} + onChange={(value: FittingFunction) => onFittingFnChange(value)} itemLayoutAlign="top" hasDividers /> diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/visual_options_popover.test.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/visual_options_popover.test.tsx index 4e2be2f0e4749..a484bc08d37a1 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/visual_options_popover.test.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/visual_options_popover.test.tsx @@ -16,7 +16,7 @@ import { ToolbarPopover, ValueLabelsSettings } from '../../../shared_components' import { MissingValuesOptions } from './missing_values_option'; import { FillOpacityOption } from './fill_opacity_option'; import { layerTypes } from '../../../../common'; -import { XYDataLayerConfig } from '../../../../common/expressions'; +import { XYLayerConfig } from '../../../../../../../src/plugins/chart_expressions/expression_xy/common'; describe('Visual options popover', () => { let frame: FramePublicAPI; @@ -34,6 +34,11 @@ describe('Visual options popover', () => { splitAccessor: 'baz', xAccessor: 'foo', accessors: ['bar'], + xScaleType: 'linear', + yScaleType: 'linear', + isHistogram: false, + palette: { type: 'palette', name: 'default' }, + type: 'lens_xy_data_layer', }, ], }; @@ -53,7 +58,7 @@ describe('Visual options popover', () => { setState={jest.fn()} state={{ ...state, - layers: [{ ...state.layers[0], seriesType: 'bar_stacked' } as XYDataLayerConfig], + layers: [{ ...state.layers[0], seriesType: 'bar_stacked' } as XYLayerConfig], }} /> ); @@ -69,9 +74,7 @@ describe('Visual options popover', () => { setState={jest.fn()} state={{ ...state, - layers: [ - { ...state.layers[0], seriesType: 'area_percentage_stacked' } as XYDataLayerConfig, - ], + layers: [{ ...state.layers[0], seriesType: 'area_percentage_stacked' } as XYLayerConfig], }} /> ); @@ -88,9 +91,7 @@ describe('Visual options popover', () => { setState={jest.fn()} state={{ ...state, - layers: [ - { ...state.layers[0], seriesType: 'area_percentage_stacked' } as XYDataLayerConfig, - ], + layers: [{ ...state.layers[0], seriesType: 'area_percentage_stacked' } as XYLayerConfig], }} /> ); @@ -106,9 +107,7 @@ describe('Visual options popover', () => { setState={jest.fn()} state={{ ...state, - layers: [ - { ...state.layers[0], seriesType: 'area_percentage_stacked' } as XYDataLayerConfig, - ], + layers: [{ ...state.layers[0], seriesType: 'area_percentage_stacked' } as XYLayerConfig], }} /> ); @@ -145,7 +144,7 @@ describe('Visual options popover', () => { setState={jest.fn()} state={{ ...state, - layers: [{ ...state.layers[0], seriesType: 'bar_horizontal' } as XYDataLayerConfig], + layers: [{ ...state.layers[0], seriesType: 'bar_horizontal' } as XYLayerConfig], fittingFunction: 'Carry', }} /> @@ -162,7 +161,7 @@ describe('Visual options popover', () => { setState={jest.fn()} state={{ ...state, - layers: [{ ...state.layers[0], seriesType: 'bar_horizontal' } as XYDataLayerConfig], + layers: [{ ...state.layers[0], seriesType: 'bar_horizontal' } as XYLayerConfig], fittingFunction: 'Carry', }} /> @@ -179,7 +178,7 @@ describe('Visual options popover', () => { setState={jest.fn()} state={{ ...state, - layers: [{ ...state.layers[0], seriesType: 'line' } as XYDataLayerConfig], + layers: [{ ...state.layers[0], seriesType: 'line' } as XYLayerConfig], fittingFunction: 'Carry', }} /> @@ -197,7 +196,7 @@ describe('Visual options popover', () => { setState={jest.fn()} state={{ ...state, - layers: [{ ...state.layers[0], seriesType: 'bar_horizontal' } as XYDataLayerConfig], + layers: [{ ...state.layers[0], seriesType: 'bar_horizontal' } as XYLayerConfig], fittingFunction: 'Carry', }} /> @@ -214,7 +213,7 @@ describe('Visual options popover', () => { setState={jest.fn()} state={{ ...state, - layers: [{ ...state.layers[0], seriesType: 'area' } as XYDataLayerConfig], + layers: [{ ...state.layers[0], seriesType: 'area' } as XYLayerConfig], fittingFunction: 'Carry', }} /> @@ -237,7 +236,7 @@ describe('Visual options popover', () => { state={{ ...state, layers: [ - { ...state.layers[0], seriesType: 'bar' } as XYDataLayerConfig, + { ...state.layers[0], seriesType: 'bar' } as XYLayerConfig, { seriesType: 'bar', layerType: layerTypes.DATA, @@ -245,6 +244,11 @@ describe('Visual options popover', () => { splitAccessor: 'baz', xAccessor: 'foo', accessors: ['bar'], + xScaleType: 'linear', + yScaleType: 'linear', + isHistogram: false, + palette: { type: 'palette', name: 'default' }, + type: 'lens_xy_data_layer', }, ], fittingFunction: 'Carry', diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/xy_config_panel.test.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/xy_config_panel.test.tsx index 1e80c6e843ba2..4036d6d3b2eb0 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/xy_config_panel.test.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/xy_config_panel.test.tsx @@ -18,7 +18,7 @@ import { createMockFramePublicAPI, createMockDatasource } from '../../mocks'; import { chartPluginMock } from 'src/plugins/charts/public/mocks'; import { EuiColorPicker } from '@elastic/eui'; import { layerTypes } from '../../../common'; -import { XYDataLayerConfig } from '../../../common/expressions'; +import { DataLayerConfigResult } from '../../../../../../src/plugins/chart_expressions/expression_xy/common'; describe('XY Config panels', () => { let frame: FramePublicAPI; @@ -36,6 +36,11 @@ describe('XY Config panels', () => { splitAccessor: 'baz', xAccessor: 'foo', accessors: ['bar'], + xScaleType: 'linear', + yScaleType: 'linear', + isHistogram: false, + type: 'lens_xy_data_layer', + palette: { type: 'palette', name: 'default' }, }, ], }; @@ -64,7 +69,12 @@ describe('XY Config panels', () => { setState={jest.fn()} state={{ ...state, - layers: [{ ...state.layers[0], yConfig: [{ axisMode: 'right', forAccessor: 'bar' }] }], + layers: [ + { + ...state.layers[0], + yConfig: [{ axisMode: 'right', forAccessor: 'bar', type: 'lens_xy_yConfig' }], + }, + ], }} /> ); @@ -80,7 +90,12 @@ describe('XY Config panels', () => { setState={jest.fn()} state={{ ...state, - layers: [{ ...state.layers[0], yConfig: [{ axisMode: 'right', forAccessor: 'foo' }] }], + layers: [ + { + ...state.layers[0], + yConfig: [{ axisMode: 'right', forAccessor: 'foo', type: 'lens_xy_yConfig' }], + }, + ], }} /> ); @@ -100,7 +115,12 @@ describe('XY Config panels', () => { state={{ ...state, hideEndzones: true, - layers: [{ ...state.layers[0], yConfig: [{ axisMode: 'right', forAccessor: 'foo' }] }], + layers: [ + { + ...state.layers[0], + yConfig: [{ axisMode: 'right', forAccessor: 'foo', type: 'lens_xy_yConfig' }], + }, + ], }} /> ); @@ -208,7 +228,7 @@ describe('XY Config panels', () => { groupId="left" state={{ ...state, - layers: [{ ...state.layers[0], seriesType: 'bar_horizontal' } as XYDataLayerConfig], + layers: [{ ...state.layers[0], seriesType: 'bar_horizontal' } as DataLayerConfigResult], }} formatFactory={jest.fn()} paletteService={chartPluginMock.createPaletteRegistry()} @@ -276,6 +296,11 @@ describe('XY Config panels', () => { splitAccessor: undefined, xAccessor: 'foo', accessors: ['bar'], + xScaleType: 'linear', + yScaleType: 'linear', + isHistogram: false, + type: 'lens_xy_data_layer', + palette: { type: 'palette', name: 'default' }, }, ], }} @@ -316,7 +341,12 @@ describe('XY Config panels', () => { splitAccessor: undefined, xAccessor: 'foo', accessors: ['bar'], - yConfig: [{ forAccessor: 'bar', color: 'red' }], + yConfig: [{ forAccessor: 'bar', color: 'red', type: 'lens_xy_yConfig' }], + xScaleType: 'linear', + yScaleType: 'linear', + isHistogram: false, + type: 'lens_xy_data_layer', + palette: { type: 'palette', name: 'default' }, }, ], }} diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.test.ts b/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.test.ts index 679f3537fdd6a..920fcd7a7c5a2 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.test.ts @@ -15,7 +15,7 @@ import { PaletteOutput } from 'src/plugins/charts/public'; import { layerTypes } from '../../common'; import { fieldFormatsServiceMock } from '../../../../../src/plugins/field_formats/public/mocks'; import { themeServiceMock } from '../../../../../src/core/public/mocks'; -import { XYDataLayerConfig } from '../../common/expressions'; +import { XYDataLayerConfig } from '../../../../../src/plugins/chart_expressions/expression_xy/common'; jest.mock('../id_generator'); @@ -199,6 +199,11 @@ describe('xy_suggestions', () => { seriesType: 'bar', accessors: ['bytes'], splitAccessor: undefined, + yScaleType: 'linear', + xScaleType: 'linear', + isHistogram: false, + palette: { type: 'palette', name: 'default' }, + type: 'lens_xy_data_layer', }, { layerId: 'second', @@ -206,6 +211,11 @@ describe('xy_suggestions', () => { seriesType: 'bar', accessors: ['bytes'], splitAccessor: undefined, + yScaleType: 'linear', + xScaleType: 'linear', + isHistogram: false, + palette: { type: 'palette', name: 'default' }, + type: 'lens_xy_data_layer', }, ], }, @@ -306,6 +316,11 @@ describe('xy_suggestions', () => { xAccessor: 'date', accessors: ['bytes'], splitAccessor: undefined, + yScaleType: 'linear', + xScaleType: 'linear', + isHistogram: false, + palette: { type: 'palette', name: 'default' }, + type: 'lens_xy_data_layer', }, ], }, @@ -348,6 +363,11 @@ describe('xy_suggestions', () => { xAccessor: 'date', accessors: ['bytes'], splitAccessor: undefined, + yScaleType: 'linear', + xScaleType: 'linear', + isHistogram: false, + palette: { type: 'palette', name: 'default' }, + type: 'lens_xy_data_layer', }, { layerId: 'second', @@ -356,6 +376,11 @@ describe('xy_suggestions', () => { xAccessor: undefined, accessors: [], splitAccessor: undefined, + yScaleType: 'linear', + xScaleType: 'linear', + isHistogram: false, + palette: { type: 'palette', name: 'default' }, + type: 'lens_xy_data_layer', }, ], }, @@ -585,6 +610,11 @@ describe('xy_suggestions', () => { seriesType: 'bar', splitAccessor: 'product', xAccessor: 'date', + yScaleType: 'linear', + xScaleType: 'linear', + isHistogram: false, + palette: { type: 'palette', name: 'default' }, + type: 'lens_xy_data_layer', }, ], }, @@ -640,6 +670,11 @@ describe('xy_suggestions', () => { seriesType: 'line', splitAccessor: undefined, xAccessor: '', + yScaleType: 'linear', + xScaleType: 'linear', + isHistogram: false, + palette: { type: 'palette', name: 'default' }, + type: 'lens_xy_data_layer', }, ], }; @@ -679,6 +714,11 @@ describe('xy_suggestions', () => { seriesType: 'bar', splitAccessor: undefined, xAccessor: 'date', + yScaleType: 'linear', + xScaleType: 'linear', + isHistogram: false, + palette: { type: 'palette', name: 'default' }, + type: 'lens_xy_data_layer', }, ], }; @@ -722,6 +762,11 @@ describe('xy_suggestions', () => { seriesType: 'bar', splitAccessor: 'product', xAccessor: 'date', + yScaleType: 'linear', + xScaleType: 'linear', + isHistogram: false, + palette: { type: 'palette', name: 'default' }, + type: 'lens_xy_data_layer', }, ], }; @@ -766,6 +811,11 @@ describe('xy_suggestions', () => { seriesType: 'bar', splitAccessor: 'dummyCol', xAccessor: 'product', + yScaleType: 'linear', + xScaleType: 'linear', + isHistogram: false, + palette: { type: 'palette', name: 'default' }, + type: 'lens_xy_data_layer', }, ], }; @@ -804,6 +854,11 @@ describe('xy_suggestions', () => { seriesType: 'bar', splitAccessor: 'date', xAccessor: 'product', + yScaleType: 'linear', + xScaleType: 'linear', + isHistogram: false, + palette: { type: 'palette', name: 'default' }, + type: 'lens_xy_data_layer', }, ], }; @@ -845,6 +900,11 @@ describe('xy_suggestions', () => { seriesType: 'bar', splitAccessor: 'dummyCol', xAccessor: 'product', + yScaleType: 'linear', + xScaleType: 'linear', + isHistogram: false, + palette: { type: 'palette', name: 'default' }, + type: 'lens_xy_data_layer', }, ], }; @@ -890,6 +950,11 @@ describe('xy_suggestions', () => { seriesType: 'bar', splitAccessor: 'category', xAccessor: 'product', + yScaleType: 'linear', + xScaleType: 'linear', + isHistogram: false, + palette: { type: 'palette', name: 'default' }, + type: 'lens_xy_data_layer', }, ], }; @@ -936,6 +1001,11 @@ describe('xy_suggestions', () => { seriesType: 'bar', splitAccessor: 'dummyCol', xAccessor: 'product', + yScaleType: 'linear', + xScaleType: 'linear', + isHistogram: false, + palette: { type: 'palette', name: 'default' }, + type: 'lens_xy_data_layer', }, ], }; diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.ts b/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.ts index 656c3fa8422e5..c6cefff276c16 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.ts +++ b/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.ts @@ -17,7 +17,11 @@ import { TableChangeType, } from '../types'; import { State, XYState, visualizationTypes } from './types'; -import type { SeriesType, XYDataLayerConfig } from '../../common/expressions'; +import type { + DataLayerConfigResult, + SeriesType, + XYLayerConfig, +} from '../../../../../src/plugins/chart_expressions/expression_xy/common'; import { layerTypes } from '../../common'; import { getIconForSeries } from './state_helpers'; import { getDataLayers, isDataLayer } from './visualization_helpers'; @@ -498,26 +502,33 @@ function buildSuggestion({ splitBy = xValue; xValue = undefined; } - const existingLayer: XYDataLayerConfig | {} = - getExistingLayer(currentState, layerId) || ({} as XYDataLayerConfig); + const existingLayer = getExistingLayer(currentState, layerId) || null; const accessors = yValues.map((col) => col.columnId); - const newLayer = { - ...existingLayer, - palette: mainPalette || ('palette' in existingLayer ? existingLayer.palette : undefined), + const newLayer: DataLayerConfigResult = { + ...(existingLayer || {}), + palette: + mainPalette || + (existingLayer && 'palette' in existingLayer + ? (existingLayer as DataLayerConfigResult).palette + : { type: 'palette', name: '' }), layerId, seriesType, xAccessor: xValue?.columnId, splitAccessor: splitBy?.columnId, accessors, yConfig: - 'yConfig' in existingLayer && existingLayer.yConfig + existingLayer && 'yConfig' in existingLayer && existingLayer.yConfig ? existingLayer.yConfig.filter(({ forAccessor }) => accessors.indexOf(forAccessor) !== -1) : undefined, layerType: layerTypes.DATA, + xScaleType: (existingLayer as DataLayerConfigResult).xScaleType ?? 'linear', + yScaleType: (existingLayer as DataLayerConfigResult).yScaleType ?? 'linear', + isHistogram: (existingLayer as DataLayerConfigResult).isHistogram ?? false, + type: 'lens_xy_data_layer', }; // Maintain consistent order for any layers that were saved - const keptLayers = currentState + const keptLayers: XYLayerConfig[] = currentState ? currentState.layers // Remove layers that aren't being suggested .filter((layer) => keptLayerIds.includes(layer.layerId)) @@ -564,7 +575,8 @@ function buildSuggestion({ yRight: true, }, preferredSeriesType: seriesType, - layers: Object.keys(existingLayer).length ? keptLayers : [...keptLayers, newLayer], + layers: + existingLayer && Object.keys(existingLayer).length ? keptLayers : [...keptLayers, newLayer], }; return { diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_visualization.ts b/x-pack/plugins/lens/public/xy_visualization/xy_visualization.ts index 894b003b4b371..7b9fa15413a57 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_visualization.ts +++ b/x-pack/plugins/lens/public/xy_visualization/xy_visualization.ts @@ -5,6 +5,5 @@ * 2.0. */ -export * from './expression'; export * from './types'; export * from './visualization'; diff --git a/x-pack/plugins/lens/tsconfig.json b/x-pack/plugins/lens/tsconfig.json index d886c87bc1622..345deaa0d1742 100644 --- a/x-pack/plugins/lens/tsconfig.json +++ b/x-pack/plugins/lens/tsconfig.json @@ -38,7 +38,8 @@ { "path": "../../../src/plugins/embeddable/tsconfig.json"}, { "path": "../../../src/plugins/presentation_util/tsconfig.json"}, { "path": "../../../src/plugins/field_formats/tsconfig.json"}, + { "path": "../../../src/plugins/chart_expressions/expression_xy/tsconfig.json"}, { "path": "../../../src/plugins/chart_expressions/expression_heatmap/tsconfig.json"}, - { "path": "../../../src/plugins/chart_expressions/expression_gauge/tsconfig.json"} + { "path": "../../../src/plugins/chart_expressions/expression_gauge/tsconfig.json"}, ] } From b2090f3a17a1bfab9cbfd9ebcb15394ee25ff029 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Fri, 11 Mar 2022 18:12:40 +0200 Subject: [PATCH 005/133] Small refactoring. --- .../chart_expressions/expression_xy/public/plugin.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/public/plugin.ts b/src/plugins/chart_expressions/expression_xy/public/plugin.ts index a95e4582e63c7..7c6d523cffea9 100755 --- a/src/plugins/chart_expressions/expression_xy/public/plugin.ts +++ b/src/plugins/chart_expressions/expression_xy/public/plugin.ts @@ -83,11 +83,7 @@ export class ExpressionXyPlugin { }; }; - expressions.registerRenderer( - getXyChartRenderer({ - getStartDeps, - }) - ); + expressions.registerRenderer(getXyChartRenderer({ getStartDeps })); } public start(core: CoreStart): ExpressionXyPluginStart {} From 33a06fa82d5c4e4d1b558bee7d3cf17c6c5ab526 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Mon, 14 Mar 2022 12:05:10 +0200 Subject: [PATCH 006/133] Fixed types. --- .../expression_xy/common/index.ts | 2 +- .../common/types/expression_functions.ts | 5 +- .../public/components/xy_chart.tsx | 5 +- .../public/definitions/fitting_functions.ts | 10 - .../expression_xy/public/definitions/index.ts | 1 - .../public/helpers/color_assignment.ts | 10 +- .../expression_xy/public/helpers/layers.ts | 4 +- .../public/helpers/reference_lines.ts | 386 +----------------- .../expression_xy/public/helpers/state.ts | 11 +- .../public/helpers/visualization.ts | 302 +------------- .../expression_xy/public/types.ts | 34 -- .../expression_xy/server/index.ts | 5 +- .../expression_xy/server/plugin.ts | 2 +- x-pack/plugins/lens/public/expressions.ts | 27 -- x-pack/plugins/lens/public/index.ts | 48 ++- .../axes_configuration.test.ts | 6 +- .../xy_visualization/axes_configuration.ts | 13 +- .../xy_visualization/color_assignment.ts | 20 +- .../reference_line_helpers.tsx | 11 +- .../public/xy_visualization/state_helpers.ts | 3 +- .../xy_visualization/to_expression.test.ts | 16 +- .../public/xy_visualization/to_expression.ts | 3 +- .../lens/public/xy_visualization/types.ts | 17 +- .../xy_visualization/visualization.test.ts | 52 +-- .../public/xy_visualization/visualization.tsx | 38 +- .../visualization_helpers.tsx | 31 +- .../axis_settings_popover.test.tsx | 4 + .../xy_config_panel/axis_settings_popover.tsx | 2 +- .../xy_config_panel/color_picker.tsx | 10 +- .../xy_config_panel/dimension_editor.tsx | 16 +- .../xy_config_panel/reference_line_panel.tsx | 1 - .../visual_options_popover.test.tsx | 5 +- .../xy_config_panel/xy_config_panel.test.tsx | 11 +- .../xy_visualization/xy_suggestions.test.ts | 16 +- .../public/xy_visualization/xy_suggestions.ts | 8 +- .../lens/server/expressions/expressions.ts | 18 - 36 files changed, 184 insertions(+), 969 deletions(-) delete mode 100644 src/plugins/chart_expressions/expression_xy/public/definitions/fitting_functions.ts diff --git a/src/plugins/chart_expressions/expression_xy/common/index.ts b/src/plugins/chart_expressions/expression_xy/common/index.ts index 493d37c7d59b4..141aac83b2822 100755 --- a/src/plugins/chart_expressions/expression_xy/common/index.ts +++ b/src/plugins/chart_expressions/expression_xy/common/index.ts @@ -41,7 +41,6 @@ export type { IconPosition, YConfigResult, DataLayerArgs, - XYLayerConfig, LensMultiTable, ValueLabelMode, AxisExtentMode, @@ -50,6 +49,7 @@ export type { XYDataLayerConfig, LegendConfigResult, AxesSettingsConfig, + XYLayerConfigResult, GridlinesConfigResult, DataLayerConfigResult, TickLabelsConfigResult, diff --git a/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts b/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts index 377219c3cc21a..97f3438ef5bcc 100644 --- a/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts +++ b/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts @@ -156,7 +156,7 @@ export interface XYArgs { yRightExtent: AxisExtentConfigResult; legend: LegendConfigResult; valueLabels: ValueLabelMode; - layers: Array; + layers: XYLayerConfigResult[]; fittingFunction?: FittingFunction; axisTitlesVisibilitySettings?: AxisTitlesVisibilityConfigResult; tickLabelsVisibilitySettings?: TickLabelsConfigResult; @@ -174,11 +174,12 @@ export interface XYReferenceLineLayerConfig { accessors: string[]; yConfig?: YConfigResult[]; } + export type ReferenceLineLayerArgs = XYReferenceLineLayerConfig & { columnToLabel?: string; }; -export type XYLayerConfig = DataLayerConfigResult | ReferenceLineLayerConfigResult; +export type XYLayerConfigResult = DataLayerConfigResult | ReferenceLineLayerConfigResult; export interface LensMultiTable { type: typeof MULTITABLE; diff --git a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx index 06e333ddf1d08..03690a361a65e 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx @@ -44,7 +44,7 @@ import { RenderMode } from 'src/plugins/expressions'; import { FieldFormat } from 'src/plugins/field_formats/common'; import { EmptyPlaceholder } from '../../../../../plugins/charts/public'; import type { FilterEvent, BrushEvent, FormatFactory } from '../types'; -import type { DataLayerConfigResult, SeriesType, XYChartProps, XYLayerConfig } from '../../common'; +import type { DataLayerConfigResult, SeriesType, XYChartProps } from '../../common'; import { isHorizontalChart, getSeriesColor } from '../helpers'; import { ChartsPluginSetup, @@ -73,6 +73,7 @@ import { ReferenceLineAnnotations, } from './reference_lines'; import { visualizationDefinitions } from '../definitions'; +import { XYLayerConfigResult } from '../../common/types'; declare global { interface Window { @@ -164,7 +165,7 @@ export function XYChart({ const chartBaseTheme = chartsThemeService.useChartsBaseTheme(); const darkMode = chartsThemeService.useDarkMode(); const filteredLayers = getFilteredLayers(layers, data); - const layersById = filteredLayers.reduce>((memo, layer) => { + const layersById = filteredLayers.reduce>((memo, layer) => { memo[layer.layerId] = layer; return memo; }, {}); diff --git a/src/plugins/chart_expressions/expression_xy/public/definitions/fitting_functions.ts b/src/plugins/chart_expressions/expression_xy/public/definitions/fitting_functions.ts deleted file mode 100644 index c9b88c97d60a2..0000000000000 --- a/src/plugins/chart_expressions/expression_xy/public/definitions/fitting_functions.ts +++ /dev/null @@ -1,10 +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 { i18n } from '@kbn/i18n'; -import { FittingFunctions } from '../../common/constants'; diff --git a/src/plugins/chart_expressions/expression_xy/public/definitions/index.ts b/src/plugins/chart_expressions/expression_xy/public/definitions/index.ts index 4d9c039855cad..53af8ec397dcb 100644 --- a/src/plugins/chart_expressions/expression_xy/public/definitions/index.ts +++ b/src/plugins/chart_expressions/expression_xy/public/definitions/index.ts @@ -7,4 +7,3 @@ */ export { visualizationDefinitions } from './visualizations'; -export { fittingFunctionDefinitions } from './fitting_functions'; diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.ts index 5a04602bec04a..942b86452ee7b 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.ts @@ -14,7 +14,11 @@ import type { AccessorConfig, FramePublicAPI } from '../types'; import { getColumnToLabelMap } from './state'; import { FormatFactory } from '../types'; import { isDataLayer, isReferenceLayer } from './visualization'; -import { DataLayerConfigResult, ReferenceLineLayerConfigResult, XYLayerConfig } from '../../common'; +import { + DataLayerConfigResult, + ReferenceLineLayerConfigResult, + XYLayerConfigResult, +} from '../../common'; const isPrimitive = (value: unknown): boolean => value != null && typeof value !== 'object'; @@ -29,7 +33,7 @@ export type ColorAssignments = Record< >; export function getColorAssignments( - layers: XYLayerConfig[], + layers: XYLayerConfigResult[], data: { tables: Record }, formatFactory: FormatFactory ): ColorAssignments { @@ -109,7 +113,7 @@ const getReferenceLineAccessorColorConfig = (layer: ReferenceLineLayerConfigResu export function getAccessorColorConfig( colorAssignments: ColorAssignments, frame: Pick, - layer: XYLayerConfig, + layer: XYLayerConfigResult, paletteService: PaletteRegistry ): AccessorConfig[] { if (isReferenceLayer(layer)) { diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/layers.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/layers.ts index d855ba3096a02..28cea4a269829 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/layers.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/layers.ts @@ -6,10 +6,10 @@ * Side Public License, v 1. */ -import { DataLayerConfigResult, LensMultiTable, XYLayerConfig } from '../../common'; +import { DataLayerConfigResult, LensMultiTable, XYLayerConfigResult } from '../../common'; import { isDataLayer } from './visualization'; -export function getFilteredLayers(layers: XYLayerConfig[], data: LensMultiTable) { +export function getFilteredLayers(layers: XYLayerConfigResult[], data: LensMultiTable) { return layers.filter((layer): layer is DataLayerConfigResult => { if (!isDataLayer(layer)) { return false; diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/reference_lines.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/reference_lines.ts index 5d70d22f4e9d8..86a87e22157c7 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/reference_lines.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/reference_lines.ts @@ -6,147 +6,10 @@ * Side Public License, v 1. */ -import { groupBy, partition } from 'lodash'; -import { i18n } from '@kbn/i18n'; -import { Datatable } from 'src/plugins/expressions'; -import uuid from 'uuid/v4'; -import { LayerTypes, YAxisModes } from '../../common/constants'; -import type { DataLayerConfigResult, XYLayerConfig, YConfig } from '../../common'; -import type { XYState, AccessorConfig, DatasourcePublicAPI, FramePublicAPI } from '../types'; -import { groupAxesByType } from './axes_configuration'; -import { isHorizontalChart, isPercentageSeries, isStackedChart } from './state'; -import { checkScaleOperation, getAxisName, getDataLayers, isNumericMetric } from './visualization'; -import { BarReferenceLineIcon } from '../icons'; - -export interface ReferenceLineBase { - label: 'x' | 'yRight' | 'yLeft'; -} - -/** - * Return the reference layers groups to show based on multiple criteria: - * * what groups are current defined in data layers - * * what existing reference line are currently defined in reference layers - */ -export function getGroupsToShow( - referenceLayers: T[], - state: XYState | undefined, - datasourceLayers: Record, - tables: Record | undefined -): Array { - if (!state) { - return []; - } - const dataLayers = getDataLayers(state.layers); - const groupsAvailable = getGroupsAvailableInData(dataLayers, datasourceLayers, tables); - return referenceLayers - .filter(({ label, config }: T) => groupsAvailable[label] || config?.length) - .map((layer) => ({ ...layer, valid: groupsAvailable[layer.label] })); -} - -/** - * Returns the reference layers groups to show based on what groups are current defined in data layers. - */ -export function getGroupsRelatedToData( - referenceLayers: T[], - state: XYState | undefined, - datasourceLayers: Record, - tables: Record | undefined -): T[] { - if (!state) { - return []; - } - const dataLayers = getDataLayers(state.layers); - const groupsAvailable = getGroupsAvailableInData(dataLayers, datasourceLayers, tables); - return referenceLayers.filter(({ label }: T) => groupsAvailable[label]); -} -/** - * Returns a dictionary with the groups filled in all the data layers - */ -export function getGroupsAvailableInData( - dataLayers: DataLayerConfigResult[], - datasourceLayers: Record, - tables: Record | undefined -) { - const hasNumberHistogram = dataLayers.some( - checkScaleOperation('interval', 'number', datasourceLayers) - ); - const { right, left } = groupAxesByType(dataLayers, tables); - return { - x: dataLayers.some(({ xAccessor }) => xAccessor != null) && hasNumberHistogram, - yLeft: left.length > 0, - yRight: right.length > 0, - }; -} - -export function getStaticValue( - dataLayers: DataLayerConfigResult[], - groupId: 'x' | 'yLeft' | 'yRight', - { activeData }: Pick, - layerHasNumberHistogram: (layer: DataLayerConfigResult) => boolean -) { - const fallbackValue = 100; - if (!activeData) { - return fallbackValue; - } - - // filter and organize data dimensions into reference layer groups - // now pick the columnId in the active data - const { - dataLayers: filteredLayers, - untouchedDataLayers, - accessors, - } = getAccessorCriteriaForGroup(groupId, dataLayers, activeData); - if ( - groupId === 'x' && - filteredLayers.length && - !untouchedDataLayers.some(layerHasNumberHistogram) - ) { - return fallbackValue; - } - const computedValue = computeStaticValueForGroup( - filteredLayers, - accessors, - activeData, - groupId !== 'x', // histogram axis should compute the min based on the current data - groupId !== 'x' - ); - return computedValue ?? fallbackValue; -} - -function getAccessorCriteriaForGroup( - groupId: 'x' | 'yLeft' | 'yRight', - dataLayers: DataLayerConfigResult[], - activeData: FramePublicAPI['activeData'] -) { - switch (groupId) { - case 'x': { - const filteredDataLayers = dataLayers.filter(({ xAccessor }) => xAccessor); - // need to reshape the dataLayers to match the other accessors format - return { - dataLayers: filteredDataLayers.map(({ accessors, xAccessor, ...rest }) => ({ - ...rest, - accessors: [xAccessor] as string[], - })), - // need the untouched ones to check if there are invalid layers from the filtered ones - // to perform the checks the original accessor structure needs to be accessed - untouchedDataLayers: filteredDataLayers, - accessors: filteredDataLayers.map(({ xAccessor }) => xAccessor) as string[], - }; - } - case 'yLeft': - case 'yRight': { - const prop = groupId === 'yLeft' ? 'left' : 'right'; - const { [prop]: axis } = groupAxesByType(dataLayers, activeData); - const rightIds = new Set(axis.map(({ layer }) => layer)); - const filteredDataLayers = dataLayers.filter(({ layerId }) => rightIds.has(layerId)); - return { - dataLayers: filteredDataLayers, - untouchedDataLayers: filteredDataLayers, - accessors: axis.map(({ accessor }) => accessor), - }; - } - } -} +import { partition } from 'lodash'; +import type { DataLayerConfigResult } from '../../common'; +import type { FramePublicAPI } from '../types'; +import { isStackedChart } from './state'; export function computeOverallDataDomain( dataLayers: DataLayerConfigResult[], @@ -211,244 +74,3 @@ export function computeOverallDataDomain( return { min, max }; } - -function computeStaticValueForGroup( - dataLayers: DataLayerConfigResult[], - accessorIds: string[], - activeData: NonNullable, - minZeroOrNegativeBase: boolean = true, - allowStacking: boolean = true -) { - const defaultReferenceLineFactor = 3 / 4; - - if (dataLayers.length && accessorIds.length) { - if (dataLayers.some(({ seriesType }) => isPercentageSeries(seriesType))) { - return defaultReferenceLineFactor; - } - - const { min, max } = computeOverallDataDomain( - dataLayers, - accessorIds, - activeData, - allowStacking - ); - - if (min != null && max != null && isFinite(min) && isFinite(max)) { - // Custom axis bounds can go below 0, so consider also lower values than 0 - const finalMinValue = minZeroOrNegativeBase ? Math.min(0, min) : min; - const interval = max - finalMinValue; - return Number((finalMinValue + interval * defaultReferenceLineFactor).toFixed(2)); - } - } -} - -export const getReferenceSupportedLayer = ( - state?: XYState, - frame?: Pick -) => { - const referenceLineGroupIds = [ - { - id: 'yReferenceLineLeft', - label: 'yLeft' as const, - }, - { - id: 'yReferenceLineRight', - label: 'yRight' as const, - }, - { - id: 'xReferenceLine', - label: 'x' as const, - }, - ]; - const referenceLineGroups = getGroupsRelatedToData( - referenceLineGroupIds, - state, - frame?.datasourceLayers || {}, - frame?.activeData - ); - const dataLayers = getDataLayers(state?.layers || []); - const filledDataLayers = dataLayers.filter( - ({ accessors, xAccessor }) => accessors.length || xAccessor - ); - const layerHasNumberHistogram = checkScaleOperation( - 'interval', - 'number', - frame?.datasourceLayers || {} - ); - - const initialDimensions = state - ? referenceLineGroups.map(({ id, label }) => ({ - groupId: id, - columnId: uuid(), - dataType: 'number', - label: getAxisName(label, { isHorizontal: isHorizontalChart(state?.layers || []) }), - staticValue: getStaticValue( - dataLayers, - label, - { activeData: frame?.activeData }, - layerHasNumberHistogram - ), - })) - : undefined; - - return { - type: LayerTypes.REFERENCELINE, - label: i18n.translate('xpack.lens.xyChart.addReferenceLineLayerLabel', { - defaultMessage: 'Reference lines', - }), - icon: BarReferenceLineIcon, - disabled: - !filledDataLayers.length || - (!dataLayers.some(layerHasNumberHistogram) && - dataLayers.every(({ accessors }) => !accessors.length)), - toolTipContent: filledDataLayers.length - ? undefined - : i18n.translate('xpack.lens.xyChart.addReferenceLineLayerLabelDisabledHelp', { - defaultMessage: 'Add some data to enable reference layer', - }), - initialDimensions, - }; -}; -export const setReferenceDimension = ({ - prevState, - layerId, - columnId, - groupId, - previousColumn, -}: { - prevState: XYState; - layerId: string; - columnId: string; - groupId: string; - previousColumn: string; -}) => { - const foundLayer = prevState.layers.find((l) => l.layerId === layerId); - if (!foundLayer) { - return prevState; - } - const newLayer = { ...foundLayer }; - - newLayer.accessors = [...newLayer.accessors.filter((a) => a !== columnId), columnId]; - const hasYConfig = newLayer.yConfig?.some(({ forAccessor }) => forAccessor === columnId); - const previousYConfig = previousColumn - ? newLayer.yConfig?.find(({ forAccessor }) => forAccessor === previousColumn) - : false; - if (!hasYConfig) { - newLayer.yConfig = [ - ...(newLayer.yConfig || []), - ...(previousYConfig - ? [ - { - // override with previous styling, - ...previousYConfig, - // but keep the new group & id config - forAccessor: columnId, - axisMode: - groupId === 'xReferenceLine' - ? YAxisModes.BOTTOM - : groupId === 'yReferenceLineRight' - ? YAxisModes.RIGHT - : YAxisModes.LEFT, - }, - ] - : []), - ]; - } - return { - ...prevState, - layers: prevState.layers.map((l) => (l.layerId === layerId ? newLayer : l)), - }; -}; - -export const getReferenceConfiguration = ({ - state, - frame, - layer, - sortedAccessors, - mappedAccessors, -}: { - state: XYState; - frame: FramePublicAPI; - layer: XYLayerConfig; - sortedAccessors: string[]; - mappedAccessors: AccessorConfig[]; -}) => { - const idToIndex = sortedAccessors.reduce>((memo, id, index) => { - memo[id] = index; - return memo; - }, {}); - const { bottom, left, right } = groupBy( - [...(layer.yConfig || [])].sort( - ({ forAccessor: forA }, { forAccessor: forB }) => idToIndex[forA] - idToIndex[forB] - ), - ({ axisMode }) => { - return axisMode; - } - ); - const groupsToShow = getGroupsToShow( - [ - // When a reference layer panel is added, a static reference line should automatically be included by default - // in the first available axis, in the following order: vertical left, vertical right, horizontal. - { - config: left, - id: 'yReferenceLineLeft', - label: 'yLeft', - dataTestSubj: 'lnsXY_yReferenceLineLeftPanel', - }, - { - config: right, - id: 'yReferenceLineRight', - label: 'yRight', - dataTestSubj: 'lnsXY_yReferenceLineRightPanel', - }, - { - config: bottom, - id: 'xReferenceLine', - label: 'x', - dataTestSubj: 'lnsXY_xReferenceLinePanel', - }, - ], - state, - frame.datasourceLayers, - frame?.activeData - ); - const isHorizontal = isHorizontalChart(state.layers); - return { - // Each reference lines layer panel will have sections for each available axis - // (horizontal axis, vertical axis left, vertical axis right). - // Only axes that support numeric reference lines should be shown - groups: groupsToShow.map(({ config = [], id, label, dataTestSubj, valid }) => ({ - groupId: id, - groupLabel: getAxisName(label, { isHorizontal }), - accessors: config.map(({ forAccessor, color }) => ({ - columnId: forAccessor, - color: color || mappedAccessors.find(({ columnId }) => columnId === forAccessor)?.color, - triggerIcon: 'color' as const, - })), - filterOperations: isNumericMetric, - supportsMoreColumns: true, - required: false, - enableDimensionEditor: true, - supportStaticValue: true, - paramEditorCustomProps: { - label: i18n.translate('xpack.lens.indexPattern.staticValue.label', { - defaultMessage: 'Reference line value', - }), - }, - supportFieldFormat: false, - dataTestSubj, - invalid: !valid, - invalidMessage: - label === 'x' - ? i18n.translate('xpack.lens.configure.invalidBottomReferenceLineDimension', { - defaultMessage: - 'This reference line is assigned to an axis that no longer exists or is no longer valid. You may move this reference line to another available axis or remove it.', - }) - : i18n.translate('xpack.lens.configure.invalidReferenceLineDimension', { - defaultMessage: - 'This reference line is assigned to an axis that no longer exists. You may move this reference line to another available axis or remove it.', - }), - requiresPreviousColumnOnDuplicate: true, - })), - }; -}; diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/state.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/state.ts index 66c4be8d84974..973d2d1ca4bba 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/state.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/state.ts @@ -8,7 +8,7 @@ import { EuiIconType } from '@elastic/eui/src/components/icon/icon'; import type { FramePublicAPI, DatasourcePublicAPI } from '../types'; -import type { SeriesType, XYLayerConfig, YConfig, ValidLayer } from '../../common'; +import type { SeriesType, XYLayerConfigResult, YConfig, ValidLayer } from '../../common'; import { visualizationDefinitions } from '../definitions'; import { getDataLayers, isDataLayer } from './visualization'; @@ -32,7 +32,7 @@ export function isStackedChart(seriesType: SeriesType) { return seriesType.includes('stacked'); } -export function isHorizontalChart(layers: XYLayerConfig[]) { +export function isHorizontalChart(layers: XYLayerConfigResult[]) { return getDataLayers(layers).every((l) => isHorizontalSeries(l.seriesType)); } @@ -46,7 +46,7 @@ export function getIconForSeries(type: SeriesType): EuiIconType { return (definition.icon as EuiIconType) || 'empty'; } -export const getSeriesColor = (layer: XYLayerConfig, accessor: string) => { +export const getSeriesColor = (layer: XYLayerConfigResult, accessor: string) => { if (isDataLayer(layer) && layer.splitAccessor) { return null; } @@ -55,7 +55,10 @@ export const getSeriesColor = (layer: XYLayerConfig, accessor: string) => { ); }; -export const getColumnToLabelMap = (layer: XYLayerConfig, datasource: DatasourcePublicAPI) => { +export const getColumnToLabelMap = ( + layer: XYLayerConfigResult, + datasource: DatasourcePublicAPI +) => { const columnToLabel: Record = {}; layer.accessors .concat(isDataLayer(layer) && layer.splitAccessor ? [layer.splitAccessor] : []) diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/visualization.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/visualization.ts index 2f7a2c8e26fd6..6d0e19dd47086 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/visualization.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/visualization.ts @@ -6,312 +6,24 @@ * Side Public License, v 1. */ -import { i18n } from '@kbn/i18n'; -import { uniq } from 'lodash'; -import { - DatasourcePublicAPI, - OperationMetadata, - VisualizationType, - State, - XYState, -} from '../types'; -import { visualizationDefinitions } from '../definitions'; -import { isHorizontalChart } from './state'; import { DataLayerConfigResult, ReferenceLineLayerConfigResult, - SeriesType, - XYDataLayerConfig, - XYLayerConfig, + XYLayerConfigResult, } from '../../common'; import { LayerTypes } from '../../common/constants'; -import { BarHorizontalIcon, BarStackedIcon, MixedXyIcon } from '../icons'; -import { LayerType } from '../../common'; - -export function getAxisName( - axis: 'x' | 'y' | 'yLeft' | 'yRight', - { isHorizontal }: { isHorizontal: boolean } -) { - const vertical = i18n.translate('xpack.lens.xyChart.verticalAxisLabel', { - defaultMessage: 'Vertical axis', - }); - const horizontal = i18n.translate('xpack.lens.xyChart.horizontalAxisLabel', { - defaultMessage: 'Horizontal axis', - }); - if (axis === 'x') { - return isHorizontal ? vertical : horizontal; - } - if (axis === 'y') { - return isHorizontal ? horizontal : vertical; - } - const verticalLeft = i18n.translate('xpack.lens.xyChart.verticalLeftAxisLabel', { - defaultMessage: 'Vertical left axis', - }); - const verticalRight = i18n.translate('xpack.lens.xyChart.verticalRightAxisLabel', { - defaultMessage: 'Vertical right axis', - }); - const horizontalTop = i18n.translate('xpack.lens.xyChart.horizontalLeftAxisLabel', { - defaultMessage: 'Horizontal top axis', - }); - const horizontalBottom = i18n.translate('xpack.lens.xyChart.horizontalRightAxisLabel', { - defaultMessage: 'Horizontal bottom axis', - }); - if (axis === 'yLeft') { - return isHorizontal ? horizontalBottom : verticalLeft; - } - return isHorizontal ? horizontalTop : verticalRight; -} - -// min requirement for the bug: -// * 2 or more layers -// * at least one with date histogram -// * at least one with interval function -export function checkXAccessorCompatibility( - state: XYState, - datasourceLayers: Record -) { - const dataLayers = getDataLayers(state.layers); - const errors = []; - const hasDateHistogramSet = dataLayers.some( - checkScaleOperation('interval', 'date', datasourceLayers) - ); - const hasNumberHistogram = dataLayers.some( - checkScaleOperation('interval', 'number', datasourceLayers) - ); - const hasOrdinalAxis = dataLayers.some( - checkScaleOperation('ordinal', undefined, datasourceLayers) - ); - if (state.layers.length > 1 && hasDateHistogramSet && hasNumberHistogram) { - errors.push({ - shortMessage: i18n.translate('xpack.lens.xyVisualization.dataTypeFailureXShort', { - defaultMessage: `Wrong data type for {axis}.`, - values: { - axis: getAxisName('x', { isHorizontal: isHorizontalChart(state.layers) }), - }, - }), - longMessage: i18n.translate('xpack.lens.xyVisualization.dataTypeFailureXLong', { - defaultMessage: `Data type mismatch for the {axis}. Cannot mix date and number interval types.`, - values: { - axis: getAxisName('x', { isHorizontal: isHorizontalChart(state.layers) }), - }, - }), - }); - } - if (state.layers.length > 1 && (hasDateHistogramSet || hasNumberHistogram) && hasOrdinalAxis) { - errors.push({ - shortMessage: i18n.translate('xpack.lens.xyVisualization.dataTypeFailureXShort', { - defaultMessage: `Wrong data type for {axis}.`, - values: { - axis: getAxisName('x', { isHorizontal: isHorizontalChart(state.layers) }), - }, - }), - longMessage: i18n.translate('xpack.lens.xyVisualization.dataTypeFailureXOrdinalLong', { - defaultMessage: `Data type mismatch for the {axis}, use a different function.`, - values: { - axis: getAxisName('x', { isHorizontal: isHorizontalChart(state.layers) }), - }, - }), - }); - } - return errors; -} - -export function checkScaleOperation( - scaleType: 'ordinal' | 'interval' | 'ratio', - dataType: 'date' | 'number' | 'string' | undefined, - datasourceLayers: Record -) { - return (layer: XYDataLayerConfig) => { - const datasourceAPI = datasourceLayers[layer.layerId]; - if (!layer.xAccessor) { - return false; - } - const operation = datasourceAPI?.getOperationForColumnId(layer.xAccessor); - return Boolean( - operation && (!dataType || operation.dataType === dataType) && operation.scale === scaleType - ); - }; -} -export const isDataLayer = (layer: XYLayerConfig): layer is DataLayerConfigResult => +export const isDataLayer = (layer: XYLayerConfigResult): layer is DataLayerConfigResult => layer.layerType === LayerTypes.DATA || !layer.layerType; -export const getDataLayers = (layers: XYLayerConfig[]) => +export const getDataLayers = (layers: XYLayerConfigResult[]) => (layers || []).filter((layer): layer is DataLayerConfigResult => isDataLayer(layer)); -export const getFirstDataLayer = (layers: XYLayerConfig[]) => - (layers || []).find((layer): layer is DataLayerConfigResult => isDataLayer(layer)); +export const isReferenceLayer = ( + layer: XYLayerConfigResult +): layer is ReferenceLineLayerConfigResult => layer.layerType === LayerTypes.REFERENCELINE; -export const isReferenceLayer = (layer: XYLayerConfig): layer is ReferenceLineLayerConfigResult => - layer.layerType === LayerTypes.REFERENCELINE; - -export const getReferenceLayers = (layers: XYLayerConfig[]) => +export const getReferenceLayers = (layers: XYLayerConfigResult[]) => (layers || []).filter((layer): layer is ReferenceLineLayerConfigResult => isReferenceLayer(layer) ); - -export function getVisualizationType(state: State): VisualizationType | 'mixed' { - if (!state.layers.length) { - return ( - visualizationDefinitions.find((t) => t.id === state.preferredSeriesType) ?? - visualizationDefinitions[0] - ); - } - const dataLayers = getDataLayers(state?.layers); - const visualizationType = visualizationDefinitions.find( - (t) => t.id === dataLayers?.[0].seriesType - ); - const seriesTypes = uniq(dataLayers.map((l) => l.seriesType)); - - return visualizationType && seriesTypes.length === 1 ? visualizationType : 'mixed'; -} - -export function getDescription(state?: State) { - if (!state) { - return { - icon: defaultIcon, - label: i18n.translate('xpack.lens.xyVisualization.xyLabel', { - defaultMessage: 'XY', - }), - }; - } - - const visualizationType = getVisualizationType(state); - - if (visualizationType === 'mixed' && isHorizontalChart(state.layers)) { - return { - icon: BarHorizontalIcon, - label: i18n.translate('xpack.lens.xyVisualization.mixedBarHorizontalLabel', { - defaultMessage: 'Mixed bar horizontal', - }), - }; - } - - if (visualizationType === 'mixed') { - return { - icon: MixedXyIcon, - label: i18n.translate('xpack.lens.xyVisualization.mixedLabel', { - defaultMessage: 'Mixed XY', - }), - }; - } - - return { - icon: visualizationType.icon || defaultIcon, - label: visualizationType.fullLabel || visualizationType.label, - }; -} - -export const defaultIcon = BarStackedIcon; -export const defaultSeriesType = 'bar_stacked'; - -export const supportedDataLayer = { - type: LayerTypes.DATA, - label: i18n.translate('xpack.lens.xyChart.addDataLayerLabel', { - defaultMessage: 'Visualization', - }), - icon: MixedXyIcon, -}; - -// i18n ids cannot be dynamically generated, hence the function below -export function getMessageIdsForDimension( - dimension: string, - layers: number[], - isHorizontal: boolean -) { - const layersList = layers.map((i: number) => i + 1).join(', '); - switch (dimension) { - case 'Break down': - return { - shortMessage: i18n.translate('xpack.lens.xyVisualization.dataFailureSplitShort', { - defaultMessage: `Missing {axis}.`, - values: { axis: 'Break down by axis' }, - }), - longMessage: i18n.translate('xpack.lens.xyVisualization.dataFailureSplitLong', { - defaultMessage: `{layers, plural, one {Layer} other {Layers}} {layersList} {layers, plural, one {requires} other {require}} a field for the {axis}.`, - values: { layers: layers.length, layersList, axis: 'Break down by axis' }, - }), - }; - case 'Y': - return { - shortMessage: i18n.translate('xpack.lens.xyVisualization.dataFailureYShort', { - defaultMessage: `Missing {axis}.`, - values: { axis: getAxisName('y', { isHorizontal }) }, - }), - longMessage: i18n.translate('xpack.lens.xyVisualization.dataFailureYLong', { - defaultMessage: `{layers, plural, one {Layer} other {Layers}} {layersList} {layers, plural, one {requires} other {require}} a field for the {axis}.`, - values: { layers: layers.length, layersList, axis: getAxisName('y', { isHorizontal }) }, - }), - }; - } - return { shortMessage: '', longMessage: '' }; -} - -export function newLayerState( - seriesType: SeriesType, - layerId: string, - layerType: LayerType = LayerTypes.DATA -): XYLayerConfig { - if (layerType === 'data') { - return { - type: 'lens_xy_data_layer', - layerId, - seriesType, - accessors: [], - layerType, - yScaleType: 'linear', - xScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, - }; - } - - return { - type: 'lens_xy_referenceLine_layer', - layerId, - accessors: [], - layerType, - }; -} - -export function getLayersByType(state: State, byType?: string) { - return state.layers.filter(({ layerType = LayerTypes.DATA }) => - byType ? layerType === byType : true - ); -} - -export function validateLayersForDimension( - dimension: string, - layers: XYLayerConfig[], - missingCriteria: (layer: XYLayerConfig) => boolean -): - | { valid: true } - | { - valid: false; - payload: { shortMessage: string; longMessage: React.ReactNode }; - } { - // Multiple layers must be consistent: - // * either a dimension is missing in ALL of them - // * or should not miss on any - if (layers.every(missingCriteria) || !layers.some(missingCriteria)) { - return { valid: true }; - } - // otherwise it's an error and it has to be reported - const layerMissingAccessors = layers.reduce((missing: number[], layer, i) => { - if (missingCriteria(layer)) { - missing.push(i); - } - return missing; - }, []); - - return { - valid: false, - payload: getMessageIdsForDimension(dimension, layerMissingAccessors, isHorizontalChart(layers)), - }; -} - -export const isNumericMetric = (op: OperationMetadata) => - !op.isBucketed && op.dataType === 'number'; -export const isNumericDynamicMetric = (op: OperationMetadata) => - isNumericMetric(op) && !op.isStaticValue; -export const isBucketed = (op: OperationMetadata) => op.isBucketed; diff --git a/src/plugins/chart_expressions/expression_xy/public/types.ts b/src/plugins/chart_expressions/expression_xy/public/types.ts index 7dcf7bc79a309..9d803213a1302 100755 --- a/src/plugins/chart_expressions/expression_xy/public/types.ts +++ b/src/plugins/chart_expressions/expression_xy/public/types.ts @@ -14,17 +14,6 @@ import { ChartsPluginSetup } from 'src/plugins/charts/public'; import { IFieldFormat, SerializedFieldFormat } from '../../../../plugins/field_formats/common'; import type { RangeSelectContext, ValueClickContext } from '../../../../plugins/embeddable/public'; import { Datatable, ExpressionsServiceStart, ExpressionsSetup } from '../../../expressions/public'; -import { - AxesSettingsConfig, - AxisExtentConfig, - FittingFunction, - LabelsOrientationConfig, - LegendConfigResult, - SeriesType, - ValueLabelMode, - XYCurveType, - XYLayerConfig, -} from '../common'; export interface SetupDeps { expressions: ExpressionsSetup; @@ -155,29 +144,6 @@ export interface VisualizationType { showExperimentalBadge?: boolean; } -export interface XYState { - preferredSeriesType: SeriesType; - legend: LegendConfigResult; - valueLabels?: ValueLabelMode; - fittingFunction?: FittingFunction; - yLeftExtent?: AxisExtentConfig; - yRightExtent?: AxisExtentConfig; - layers: XYLayerConfig[]; - xTitle?: string; - yTitle?: string; - yRightTitle?: string; - axisTitlesVisibilitySettings?: AxesSettingsConfig; - tickLabelsVisibilitySettings?: AxesSettingsConfig; - gridlinesVisibilitySettings?: AxesSettingsConfig; - labelsOrientation?: LabelsOrientationConfig; - curveType?: XYCurveType; - fillOpacity?: number; - hideEndzones?: boolean; - valuesInLegend?: boolean; -} - -export type State = XYState; - export interface AccessorConfig { columnId: string; triggerIcon?: 'color' | 'disabled' | 'colorBy' | 'none' | 'invisible'; diff --git a/src/plugins/chart_expressions/expression_xy/server/index.ts b/src/plugins/chart_expressions/expression_xy/server/index.ts index ecfb289bb608b..e529b2a15fe34 100755 --- a/src/plugins/chart_expressions/expression_xy/server/index.ts +++ b/src/plugins/chart_expressions/expression_xy/server/index.ts @@ -6,11 +6,10 @@ * Side Public License, v 1. */ -import { PluginInitializerContext } from '../../../../core/server'; import { ExpressionXyPlugin } from './plugin'; -export function plugin(initializerContext: PluginInitializerContext) { - return new ExpressionXyPlugin(initializerContext); +export function plugin() { + return new ExpressionXyPlugin(); } export type { ExpressionXyPluginSetup, ExpressionXyPluginStart } from './types'; diff --git a/src/plugins/chart_expressions/expression_xy/server/plugin.ts b/src/plugins/chart_expressions/expression_xy/server/plugin.ts index e943277f8a119..53653a0d93c33 100755 --- a/src/plugins/chart_expressions/expression_xy/server/plugin.ts +++ b/src/plugins/chart_expressions/expression_xy/server/plugin.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from '../../../../core/server'; +import { CoreSetup, CoreStart, Plugin } from '../../../../core/server'; import { ExpressionXyPluginSetup, ExpressionXyPluginStart } from './types'; import { diff --git a/x-pack/plugins/lens/public/expressions.ts b/x-pack/plugins/lens/public/expressions.ts index 833fcc15762af..40fee07f5d95a 100644 --- a/x-pack/plugins/lens/public/expressions.ts +++ b/x-pack/plugins/lens/public/expressions.ts @@ -6,25 +6,8 @@ */ import type { ExpressionsSetup } from 'src/plugins/expressions/public'; - -import { - axisExtentConfig, - yAxisConfig, - axisTitlesVisibilityConfig, -} from '../common/expressions/xy_chart/axis_config'; -import { gridlinesConfig } from '../common/expressions/xy_chart/grid_lines_config'; -import { labelsOrientationConfig } from '../common/expressions/xy_chart/labels_orientation_config'; -import { - dataLayerConfig, - referenceLineLayerConfig, -} from '../common/expressions/xy_chart/layer_config'; -import { legendConfig } from '../common/expressions/xy_chart/legend_config'; -import { tickLabelsConfig } from '../common/expressions/xy_chart/tick_labels_config'; -import { xyChart } from '../common/expressions/xy_chart/xy_chart'; - import { getDatatable } from '../common/expressions/datatable/datatable'; import { datatableColumn } from '../common/expressions/datatable/datatable_column'; - import { mergeTables } from '../common/expressions/merge_tables'; import { renameColumns } from '../common/expressions/rename_columns/rename_columns'; import { formatColumn } from '../common/expressions/format_column'; @@ -40,21 +23,11 @@ export const setupExpressions = ( [lensMultitable].forEach((expressionType) => expressions.registerType(expressionType)); [ - xyChart, mergeTables, counterRate, - yAxisConfig, - dataLayerConfig, - referenceLineLayerConfig, formatColumn, - legendConfig, renameColumns, - gridlinesConfig, datatableColumn, - tickLabelsConfig, - axisTitlesVisibilityConfig, - axisExtentConfig, - labelsOrientationConfig, getDatatable(formatFactory), getTimeScale(getTimeZone), ].forEach((expressionFn) => expressions.registerFunction(expressionFn)); diff --git a/x-pack/plugins/lens/public/index.ts b/x-pack/plugins/lens/public/index.ts index e9a458f6e3a24..a785d12ac5785 100644 --- a/x-pack/plugins/lens/public/index.ts +++ b/x-pack/plugins/lens/public/index.ts @@ -11,17 +11,8 @@ export type { EmbeddableComponentProps, TypedLensByValueInput, } from './embeddable/embeddable_component'; -export type { XYState } from './xy_visualization/types'; +export type { XYState, XYLayerConfig } from './xy_visualization/types'; export type { DataType, OperationMetadata, Visualization } from './types'; -export type { - AxesSettingsConfig, - XYLayerConfig, - LegendConfig, - SeriesType, - YAxisMode, - XYCurveType, - YConfig, -} from '../common/expressions'; export type { ValueLabelConfig, PieVisualizationState, @@ -62,6 +53,43 @@ export type { FormulaPublicApi, StaticValueIndexPatternColumn, } from './indexpattern_datasource/types'; +export type { + XYArgs, + YConfig, + XYRender, + LayerType, + YAxisMode, + LineStyle, + FillStyle, + SeriesType, + YScaleType, + XScaleType, + AxisConfig, + ValidLayer, + XYCurveType, + XYChartProps, + LegendConfig, + IconPosition, + YConfigResult, + DataLayerArgs, + LensMultiTable, + ValueLabelMode, + AxisExtentMode, + FittingFunction, + AxisExtentConfig, + LegendConfigResult, + AxesSettingsConfig, + GridlinesConfigResult, + DataLayerConfigResult, + TickLabelsConfigResult, + AxisExtentConfigResult, + ReferenceLineLayerArgs, + LabelsOrientationConfig, + XYReferenceLineLayerConfig, + LabelsOrientationConfigResult, + ReferenceLineLayerConfigResult, + AxisTitlesVisibilityConfigResult, +} from '../../../../src/plugins/chart_expressions/expression_xy/common'; export type { LensEmbeddableInput } from './embeddable'; export { layerTypes } from '../common'; diff --git a/x-pack/plugins/lens/public/xy_visualization/axes_configuration.test.ts b/x-pack/plugins/lens/public/xy_visualization/axes_configuration.test.ts index 18cd670ec2ad6..e220db03113d6 100644 --- a/x-pack/plugins/lens/public/xy_visualization/axes_configuration.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/axes_configuration.test.ts @@ -275,7 +275,7 @@ describe('axes_configuration', () => { [ { ...sampleLayer, - yConfig: [{ type: 'lens_xy_yConfig', forAccessor: 'yAccessorId', axisMode: 'right' }], + yConfig: [{ forAccessor: 'yAccessorId', axisMode: 'right' }], }, ], false, @@ -295,7 +295,7 @@ describe('axes_configuration', () => { { ...sampleLayer, accessors: ['yAccessorId', 'yAccessorId3', 'yAccessorId4'], - yConfig: [{ type: 'lens_xy_yConfig', forAccessor: 'yAccessorId', axisMode: 'right' }], + yConfig: [{ forAccessor: 'yAccessorId', axisMode: 'right' }], }, ], false, @@ -319,7 +319,7 @@ describe('axes_configuration', () => { { ...sampleLayer, accessors: ['yAccessorId', 'yAccessorId3', 'yAccessorId4'], - yConfig: [{ type: 'lens_xy_yConfig', forAccessor: 'yAccessorId', axisMode: 'right' }], + yConfig: [{ forAccessor: 'yAccessorId', axisMode: 'right' }], }, ], false, diff --git a/x-pack/plugins/lens/public/xy_visualization/axes_configuration.ts b/x-pack/plugins/lens/public/xy_visualization/axes_configuration.ts index b032e7359d9fc..b9b2c2ae86e42 100644 --- a/x-pack/plugins/lens/public/xy_visualization/axes_configuration.ts +++ b/x-pack/plugins/lens/public/xy_visualization/axes_configuration.ts @@ -6,15 +6,13 @@ */ import { FormatFactory } from '../../common'; -import { - AxisExtentConfig, - DataLayerConfigResult, -} from '../../../../../src/plugins/chart_expressions/expression_xy/common'; +import { AxisExtentConfig } from '../../../../../src/plugins/chart_expressions/expression_xy/common'; import { Datatable } from '../../../../../src/plugins/expressions/public'; import type { IFieldFormat, SerializedFieldFormat, } from '../../../../../src/plugins/field_formats/common'; +import { XYDataLayerConfig } from './types'; interface FormattedMetric { layer: string; @@ -36,10 +34,7 @@ export function isFormatterCompatible( return formatter1.id === formatter2.id; } -export function groupAxesByType( - layers: DataLayerConfigResult[], - tables?: Record -) { +export function groupAxesByType(layers: XYDataLayerConfig[], tables?: Record) { const series: { auto: FormattedMetric[]; left: FormattedMetric[]; @@ -103,7 +98,7 @@ export function groupAxesByType( } export function getAxesConfiguration( - layers: DataLayerConfigResult[], + layers: XYDataLayerConfig[], shouldRotate: boolean, tables?: Record, formatFactory?: FormatFactory diff --git a/x-pack/plugins/lens/public/xy_visualization/color_assignment.ts b/x-pack/plugins/lens/public/xy_visualization/color_assignment.ts index 5efe5ab798e5a..331263c9b9b94 100644 --- a/x-pack/plugins/lens/public/xy_visualization/color_assignment.ts +++ b/x-pack/plugins/lens/public/xy_visualization/color_assignment.ts @@ -13,11 +13,8 @@ import type { AccessorConfig, FramePublicAPI } from '../types'; import { getColumnToLabelMap } from './state_helpers'; import { FormatFactory } from '../../common'; import { isDataLayer, isReferenceLayer } from './visualization_helpers'; -import { - DataLayerConfigResult, - ReferenceLineLayerConfigResult, - XYLayerConfig, -} from '../../../../../src/plugins/chart_expressions/expression_xy/common'; +import { DataLayerConfigResult } from '../../../../../src/plugins/chart_expressions/expression_xy/common'; +import { XYDataLayerConfig, XYLayerConfig, XYReferenceLineLayerConfig } from './types'; const isPrimitive = (value: unknown): boolean => value != null && typeof value !== 'object'; @@ -98,7 +95,7 @@ export function getColorAssignments( }); } -const getReferenceLineAccessorColorConfig = (layer: ReferenceLineLayerConfigResult) => { +const getReferenceLineAccessorColorConfig = (layer: XYReferenceLineLayerConfig) => { return layer.accessors.map((accessor) => { const currentYConfig = layer.yConfig?.find((yConfig) => yConfig.forAccessor === accessor); return { @@ -118,19 +115,20 @@ export function getAccessorColorConfig( if (isReferenceLayer(layer)) { return getReferenceLineAccessorColorConfig(layer); } + const dataLayer: XYDataLayerConfig = layer; - const layerContainsSplits = Boolean(layer.splitAccessor); - const currentPalette: PaletteOutput = layer.palette || { type: 'palette', name: 'default' }; + const layerContainsSplits = Boolean(dataLayer.splitAccessor); + const currentPalette: PaletteOutput = dataLayer.palette || { type: 'palette', name: 'default' }; const totalSeriesCount = colorAssignments[currentPalette.name]?.totalSeriesCount; - return layer.accessors.map((accessor) => { - const currentYConfig = layer.yConfig?.find((yConfig) => yConfig.forAccessor === accessor); + return dataLayer.accessors.map((accessor) => { + const currentYConfig = dataLayer.yConfig?.find((yConfig) => yConfig.forAccessor === accessor); if (layerContainsSplits) { return { columnId: accessor as string, triggerIcon: 'disabled', }; } - const columnToLabel = getColumnToLabelMap(layer, frame.datasourceLayers[layer.layerId]); + const columnToLabel = getColumnToLabelMap(layer, frame.datasourceLayers[dataLayer.layerId]); const rank = colorAssignments[currentPalette.name].getRank( layer, columnToLabel[accessor] || accessor, diff --git a/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx b/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx index 5cdb110098987..f987f4a0c8828 100644 --- a/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx @@ -9,9 +9,6 @@ import { groupBy, partition } from 'lodash'; import { i18n } from '@kbn/i18n'; import { layerTypes } from '../../common'; import type { - DataLayerConfigResult, - XYDataLayerConfig, - XYLayerConfig, YAxisMode, YConfig, } from '../../../../../src/plugins/chart_expressions/expression_xy/common'; @@ -19,7 +16,7 @@ import { Datatable } from '../../../../../src/plugins/expressions/public'; import type { AccessorConfig, DatasourcePublicAPI, FramePublicAPI, Visualization } from '../types'; import { groupAxesByType } from './axes_configuration'; import { isHorizontalChart, isPercentageSeries, isStackedChart } from './state_helpers'; -import type { XYState } from './types'; +import type { XYState, XYLayerConfig, XYDataLayerConfig } from './types'; import { checkScaleOperation, getAxisName, @@ -74,7 +71,7 @@ export function getGroupsRelatedToData( * Returns a dictionary with the groups filled in all the data layers */ export function getGroupsAvailableInData( - dataLayers: DataLayerConfigResult[], + dataLayers: XYDataLayerConfig[], datasourceLayers: Record, tables: Record | undefined ) { @@ -90,7 +87,7 @@ export function getGroupsAvailableInData( } export function getStaticValue( - dataLayers: DataLayerConfigResult[], + dataLayers: XYDataLayerConfig[], groupId: 'x' | 'yLeft' | 'yRight', { activeData }: Pick, layerHasNumberHistogram: (layer: XYDataLayerConfig) => boolean @@ -126,7 +123,7 @@ export function getStaticValue( function getAccessorCriteriaForGroup( groupId: 'x' | 'yLeft' | 'yRight', - dataLayers: DataLayerConfigResult[], + dataLayers: XYDataLayerConfig[], activeData: FramePublicAPI['activeData'] ) { switch (groupId) { diff --git a/x-pack/plugins/lens/public/xy_visualization/state_helpers.ts b/x-pack/plugins/lens/public/xy_visualization/state_helpers.ts index bda0317e9f7ea..f1a154b167f46 100644 --- a/x-pack/plugins/lens/public/xy_visualization/state_helpers.ts +++ b/x-pack/plugins/lens/public/xy_visualization/state_helpers.ts @@ -9,11 +9,10 @@ import { EuiIconType } from '@elastic/eui/src/components/icon/icon'; import type { FramePublicAPI, DatasourcePublicAPI } from '../types'; import type { SeriesType, - XYLayerConfig, YConfig, ValidLayer, } from '../../../../../src/plugins/chart_expressions/expression_xy/common'; -import { visualizationTypes } from './types'; +import { visualizationTypes, XYLayerConfig } from './types'; import { getDataLayers, isDataLayer } from './visualization_helpers'; export function isHorizontalSeries(seriesType: SeriesType) { diff --git a/x-pack/plugins/lens/public/xy_visualization/to_expression.test.ts b/x-pack/plugins/lens/public/xy_visualization/to_expression.test.ts index bbfe911bdcd67..0f04f47bc76fb 100644 --- a/x-pack/plugins/lens/public/xy_visualization/to_expression.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/to_expression.test.ts @@ -78,7 +78,6 @@ describe('#toExpression', () => { xScaleType: 'linear', yScaleType: 'linear', isHistogram: false, - type: 'lens_xy_data_layer', palette: { type: 'palette', name: 'default' }, }, ], @@ -107,7 +106,6 @@ describe('#toExpression', () => { xScaleType: 'linear', yScaleType: 'linear', isHistogram: false, - type: 'lens_xy_data_layer', palette: { type: 'palette', name: 'default' }, }, ], @@ -135,7 +133,6 @@ describe('#toExpression', () => { xScaleType: 'linear', yScaleType: 'linear', isHistogram: false, - type: 'lens_xy_data_layer', palette: { type: 'palette', name: 'default' }, }, ], @@ -168,7 +165,6 @@ describe('#toExpression', () => { xScaleType: 'linear', yScaleType: 'linear', isHistogram: false, - type: 'lens_xy_data_layer', palette: { type: 'palette', name: 'default' }, }, ], @@ -198,7 +194,6 @@ describe('#toExpression', () => { xScaleType: 'linear', yScaleType: 'linear', isHistogram: false, - type: 'lens_xy_data_layer', palette: { type: 'palette', name: 'default' }, }, ], @@ -225,7 +220,6 @@ describe('#toExpression', () => { xScaleType: 'linear', yScaleType: 'linear', isHistogram: false, - type: 'lens_xy_data_layer', palette: { type: 'palette', name: 'default' }, }, ], @@ -267,7 +261,6 @@ describe('#toExpression', () => { xScaleType: 'linear', yScaleType: 'linear', isHistogram: false, - type: 'lens_xy_data_layer', palette: { type: 'palette', name: 'default' }, }, ], @@ -300,7 +293,6 @@ describe('#toExpression', () => { xScaleType: 'linear', yScaleType: 'linear', isHistogram: false, - type: 'lens_xy_data_layer', palette: { type: 'palette', name: 'default' }, }, ], @@ -331,7 +323,6 @@ describe('#toExpression', () => { xScaleType: 'linear', yScaleType: 'linear', isHistogram: false, - type: 'lens_xy_data_layer', palette: { type: 'palette', name: 'default' }, }, ], @@ -364,7 +355,6 @@ describe('#toExpression', () => { xScaleType: 'linear', yScaleType: 'linear', isHistogram: false, - type: 'lens_xy_data_layer', palette: { type: 'palette', name: 'default' }, }, ], @@ -388,19 +378,17 @@ describe('#toExpression', () => { splitAccessor: 'd', xAccessor: 'a', accessors: ['b', 'c'], - yConfig: [{ forAccessor: 'a', type: 'lens_xy_yConfig' }], + yConfig: [{ forAccessor: 'a' }], xScaleType: 'linear', yScaleType: 'linear', isHistogram: false, - type: 'lens_xy_data_layer', palette: { type: 'palette', name: 'default' }, }, { layerId: 'referenceLine', layerType: layerTypes.REFERENCELINE, accessors: ['b', 'c'], - yConfig: [{ forAccessor: 'a', type: 'lens_xy_yConfig' }], - type: 'lens_xy_referenceLine_layer', + yConfig: [{ forAccessor: 'a' }], }, ], }, diff --git a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts index 4701fec2268c8..143801d793218 100644 --- a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts +++ b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts @@ -8,13 +8,12 @@ import { Ast } from '@kbn/interpreter'; import { ScaleType } from '@elastic/charts'; import { PaletteRegistry } from 'src/plugins/charts/public'; -import { State } from './types'; +import type { State, XYLayerConfig } from './types'; import { OperationMetadata, DatasourcePublicAPI } from '../types'; import { getColumnToLabelMap } from './state_helpers'; import type { ReferenceLineLayerConfigResult, ValidLayer, - XYLayerConfig, XYReferenceLineLayerConfig, YConfig, } from '../../../../../src/plugins/chart_expressions/expression_xy/common'; diff --git a/x-pack/plugins/lens/public/xy_visualization/types.ts b/x-pack/plugins/lens/public/xy_visualization/types.ts index 69d34da25c05e..a2d2d2ad1208e 100644 --- a/x-pack/plugins/lens/public/xy_visualization/types.ts +++ b/x-pack/plugins/lens/public/xy_visualization/types.ts @@ -22,14 +22,29 @@ import type { SeriesType, LegendConfig, AxisExtentConfig, - XYLayerConfig, XYCurveType, AxesSettingsConfig, FittingFunction, LabelsOrientationConfig, + DataLayerArgs, + LayerType, + ReferenceLineLayerArgs, + YConfig, } from '../../../../../src/plugins/chart_expressions/expression_xy/common'; import type { ValueLabelConfig } from '../../common/types'; +export interface XYDataLayerConfig extends Omit { + layerType: LayerType; + yConfig?: YConfig[]; +} + +export interface XYReferenceLineLayerConfig extends Omit { + layerType: LayerType; + yConfig?: YConfig[]; +} + +export type XYLayerConfig = XYDataLayerConfig | XYReferenceLineLayerConfig; + // Persisted parts of the state export interface XYState { preferredSeriesType: SeriesType; diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts b/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts index f7409ef87277c..4590dc6ad8c38 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts @@ -8,12 +8,10 @@ import { getXyVisualization } from './visualization'; import { Position } from '@elastic/charts'; import { Operation, VisualizeEditorContext, Suggestion, OperationDescriptor } from '../types'; -import type { State, XYState, XYSuggestion } from './types'; +import type { State, XYState, XYSuggestion, XYLayerConfig, XYDataLayerConfig } from './types'; import type { DataLayerConfigResult, SeriesType, - XYDataLayerConfig, - XYLayerConfig, } from '../../../../../src/plugins/chart_expressions/expression_xy/common'; import { layerTypes } from '../../common'; import { createMockDatasource, createMockFramePublicAPI } from '../mocks'; @@ -40,7 +38,6 @@ function exampleState(): XYState { xScaleType: 'linear', yScaleType: 'linear', isHistogram: false, - type: 'lens_xy_data_layer', palette: { type: 'palette', name: 'default' }, }, ], @@ -200,7 +197,6 @@ describe('xy_visualization', () => { xScaleType: 'linear', yScaleType: 'linear', isHistogram: false, - type: 'lens_xy_data_layer', palette: { type: 'palette', name: 'default' }, }, ], @@ -296,7 +292,6 @@ describe('xy_visualization', () => { xScaleType: 'linear', yScaleType: 'linear', isHistogram: false, - type: 'lens_xy_data_layer', palette: { type: 'palette', name: 'default' }, }, ], @@ -330,7 +325,6 @@ describe('xy_visualization', () => { xScaleType: 'linear', yScaleType: 'linear', isHistogram: false, - type: 'lens_xy_data_layer', palette: { type: 'palette', name: 'default' }, }, ], @@ -359,7 +353,6 @@ describe('xy_visualization', () => { layerId: 'referenceLine', layerType: layerTypes.REFERENCELINE, accessors: [], - type: 'lens_xy_referenceLine_layer', }, ], }, @@ -444,7 +437,6 @@ describe('xy_visualization', () => { xScaleType: 'linear', yScaleType: 'linear', isHistogram: false, - type: 'lens_xy_data_layer', palette: { type: 'palette', name: 'default' }, }, ], @@ -667,7 +659,6 @@ describe('xy_visualization', () => { xScaleType: 'linear', yScaleType: 'linear', isHistogram: false, - type: 'lens_xy_data_layer', palette: { type: 'palette', name: 'default' }, }, ], @@ -1047,15 +1038,13 @@ describe('xy_visualization', () => { xScaleType: 'linear', yScaleType: 'linear', isHistogram: false, - type: 'lens_xy_data_layer', palette: { type: 'palette', name: 'default' }, }, { layerId: 'referenceLine', layerType: layerTypes.REFERENCELINE, accessors: [], - yConfig: [{ axisMode: 'left', forAccessor: 'a', type: 'lens_xy_yConfig' }], - type: 'lens_xy_referenceLine_layer', + yConfig: [{ axisMode: 'left', forAccessor: 'a' }], }, ], }; @@ -1101,9 +1090,7 @@ describe('xy_visualization', () => { it('should return a group for the vertical right axis', () => { const state = getStateWithBaseReferenceLine(); - state.layers[0].yConfig = [ - { axisMode: 'right', forAccessor: 'a', type: 'lens_xy_yConfig' }, - ]; + state.layers[0].yConfig = [{ axisMode: 'right', forAccessor: 'a' }]; state.layers[1].yConfig![0].axisMode = 'right'; const options = xyVisualization.getConfiguration({ @@ -1185,13 +1172,13 @@ describe('xy_visualization', () => { (state.layers[0] as XYDataLayerConfig).accessors = ['a', 'b']; // invert them on purpose (state.layers[0] as XYDataLayerConfig).yConfig = [ - { axisMode: 'right', forAccessor: 'b', type: 'lens_xy_yConfig' }, - { axisMode: 'left', forAccessor: 'a', type: 'lens_xy_yConfig' }, + { axisMode: 'right', forAccessor: 'b' }, + { axisMode: 'left', forAccessor: 'a' }, ]; state.layers[1].yConfig = [ - { forAccessor: 'c', axisMode: 'bottom', type: 'lens_xy_yConfig' }, - { forAccessor: 'b', axisMode: 'right', type: 'lens_xy_yConfig' }, - { forAccessor: 'a', axisMode: 'left', type: 'lens_xy_yConfig' }, + { forAccessor: 'c', axisMode: 'bottom' }, + { forAccessor: 'b', axisMode: 'right' }, + { forAccessor: 'a', axisMode: 'left' }, ]; // set the xAccessor as number histogram frame.datasourceLayers.referenceLine.getOperationForColumnId = jest.fn((accessor) => { @@ -1402,7 +1389,6 @@ describe('xy_visualization', () => { { forAccessor: 'b', color: 'red', - type: 'lens_xy_yConfig', }, ], }, @@ -1551,7 +1537,6 @@ describe('xy_visualization', () => { xScaleType: 'linear', yScaleType: 'linear', isHistogram: false, - type: 'lens_xy_data_layer', palette: { type: 'palette', name: 'default' }, }, ], @@ -1572,7 +1557,6 @@ describe('xy_visualization', () => { xScaleType: 'linear', yScaleType: 'linear', isHistogram: false, - type: 'lens_xy_data_layer', palette: { type: 'palette', name: 'default' }, }, { @@ -1584,7 +1568,6 @@ describe('xy_visualization', () => { xScaleType: 'linear', yScaleType: 'linear', isHistogram: false, - type: 'lens_xy_data_layer', palette: { type: 'palette', name: 'default' }, }, ], @@ -1605,7 +1588,6 @@ describe('xy_visualization', () => { xScaleType: 'linear', yScaleType: 'linear', isHistogram: false, - type: 'lens_xy_data_layer', palette: { type: 'palette', name: 'default' }, }, { @@ -1618,7 +1600,6 @@ describe('xy_visualization', () => { xScaleType: 'linear', yScaleType: 'linear', isHistogram: false, - type: 'lens_xy_data_layer', palette: { type: 'palette', name: 'default' }, }, ], @@ -1640,7 +1621,6 @@ describe('xy_visualization', () => { xScaleType: 'linear', yScaleType: 'linear', isHistogram: false, - type: 'lens_xy_data_layer', palette: { type: 'palette', name: 'default' }, }, ], @@ -1661,7 +1641,6 @@ describe('xy_visualization', () => { xScaleType: 'linear', yScaleType: 'linear', isHistogram: false, - type: 'lens_xy_data_layer', palette: { type: 'palette', name: 'default' }, }, { @@ -1674,7 +1653,6 @@ describe('xy_visualization', () => { xScaleType: 'linear', yScaleType: 'linear', isHistogram: false, - type: 'lens_xy_data_layer', palette: { type: 'palette', name: 'default' }, }, ], @@ -1695,7 +1673,6 @@ describe('xy_visualization', () => { xScaleType: 'linear', yScaleType: 'linear', isHistogram: false, - type: 'lens_xy_data_layer', palette: { type: 'palette', name: 'default' }, }, { @@ -1707,7 +1684,6 @@ describe('xy_visualization', () => { xScaleType: 'linear', yScaleType: 'linear', isHistogram: false, - type: 'lens_xy_data_layer', palette: { type: 'palette', name: 'default' }, }, ], @@ -1733,7 +1709,6 @@ describe('xy_visualization', () => { xScaleType: 'linear', yScaleType: 'linear', isHistogram: false, - type: 'lens_xy_data_layer', palette: { type: 'palette', name: 'default' }, }, { @@ -1746,7 +1721,6 @@ describe('xy_visualization', () => { xScaleType: 'linear', yScaleType: 'linear', isHistogram: false, - type: 'lens_xy_data_layer', palette: { type: 'palette', name: 'default' }, }, { @@ -1759,7 +1733,6 @@ describe('xy_visualization', () => { xScaleType: 'linear', yScaleType: 'linear', isHistogram: false, - type: 'lens_xy_data_layer', palette: { type: 'palette', name: 'default' }, }, ], @@ -1785,7 +1758,6 @@ describe('xy_visualization', () => { xScaleType: 'linear', yScaleType: 'linear', isHistogram: false, - type: 'lens_xy_data_layer', palette: { type: 'palette', name: 'default' }, }, { @@ -1797,7 +1769,6 @@ describe('xy_visualization', () => { xScaleType: 'linear', yScaleType: 'linear', isHistogram: false, - type: 'lens_xy_data_layer', palette: { type: 'palette', name: 'default' }, }, { @@ -1809,7 +1780,6 @@ describe('xy_visualization', () => { xScaleType: 'linear', yScaleType: 'linear', isHistogram: false, - type: 'lens_xy_data_layer', palette: { type: 'palette', name: 'default' }, }, ], @@ -1838,7 +1808,6 @@ describe('xy_visualization', () => { xScaleType: 'linear', yScaleType: 'linear', isHistogram: false, - type: 'lens_xy_data_layer', palette: { type: 'palette', name: 'default' }, }, ], @@ -1891,7 +1860,6 @@ describe('xy_visualization', () => { xScaleType: 'linear', yScaleType: 'linear', isHistogram: false, - type: 'lens_xy_data_layer', palette: { type: 'palette', name: 'default' }, }, { @@ -1904,7 +1872,6 @@ describe('xy_visualization', () => { xScaleType: 'linear', yScaleType: 'linear', isHistogram: false, - type: 'lens_xy_data_layer', palette: { type: 'palette', name: 'default' }, }, ], @@ -1957,7 +1924,6 @@ describe('xy_visualization', () => { xScaleType: 'linear', yScaleType: 'linear', isHistogram: false, - type: 'lens_xy_data_layer', palette: { type: 'palette', name: 'default' }, }, { @@ -1970,7 +1936,6 @@ describe('xy_visualization', () => { xScaleType: 'linear', yScaleType: 'linear', isHistogram: false, - type: 'lens_xy_data_layer', palette: { type: 'palette', name: 'default' }, }, ], @@ -2038,7 +2003,6 @@ describe('xy_visualization', () => { xScaleType: 'linear', yScaleType: 'linear', isHistogram: false, - type: 'lens_xy_data_layer', palette: { type: 'palette', name: 'default' }, }, ], diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx index 01d928201705f..1dfd1a0148786 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx @@ -20,11 +20,9 @@ import { XyToolbar } from './xy_config_panel'; import { DimensionEditor } from './xy_config_panel/dimension_editor'; import { LayerHeader } from './xy_config_panel/layer_header'; import type { Visualization, AccessorConfig, FramePublicAPI } from '../types'; -import { State, visualizationTypes, XYSuggestion } from './types'; +import { State, visualizationTypes, XYSuggestion, XYLayerConfig } from './types'; import { SeriesType, - XYDataLayerConfig, - XYLayerConfig, YAxisMode, YConfigResult, } from '../../../../../src/plugins/chart_expressions/expression_xy/common'; @@ -57,7 +55,7 @@ import { validateLayersForDimension, } from './visualization_helpers'; import { groupAxesByType } from './axes_configuration'; -import { XYState } from '..'; +import { XYState, XYDataLayerConfig } from './types'; export const getXyVisualization = ({ paletteService, @@ -183,13 +181,16 @@ export const getXyVisualization = ({ if (isReferenceLayer(layer)) { return getReferenceConfiguration({ state, frame, layer, sortedAccessors, mappedAccessors }); } + + const dataLayer: XYDataLayerConfig = layer; + const dataLayers = getDataLayers(state.layers); const isHorizontal = isHorizontalChart(state.layers); const { left, right } = groupAxesByType([layer], frame.activeData); // Check locally if it has one accessor OR one accessor per axis const layerHasOnlyOneAccessor = Boolean( - layer.accessors.length < 2 || + dataLayer.accessors.length < 2 || (left.length && left.length < 2) || (right.length && right.length < 2) ); @@ -201,9 +202,10 @@ export const getXyVisualization = ({ // check that the other layers are compatible with this one (l) => { if ( - l.seriesType === layer.seriesType && - Boolean(l.xAccessor) === Boolean(layer.xAccessor) && - Boolean(l.splitAccessor) === Boolean(layer.splitAccessor) + isDataLayer(l) && + l.seriesType === dataLayer.seriesType && + Boolean(l.xAccessor) === Boolean(dataLayer.xAccessor) && + Boolean(l.splitAccessor) === Boolean(dataLayer.splitAccessor) ) { const { left: localLeft, right: localRight } = groupAxesByType([l], frame.activeData); // return true only if matching axis are found @@ -222,9 +224,9 @@ export const getXyVisualization = ({ { groupId: 'x', groupLabel: getAxisName('x', { isHorizontal }), - accessors: layer.xAccessor ? [{ columnId: layer.xAccessor }] : [], + accessors: dataLayer.xAccessor ? [{ columnId: dataLayer.xAccessor }] : [], filterOperations: isBucketed, - supportsMoreColumns: !layer.xAccessor, + supportsMoreColumns: !dataLayer.xAccessor, dataTestSubj: 'lnsXY_xDimensionPanel', }, { @@ -242,21 +244,21 @@ export const getXyVisualization = ({ groupLabel: i18n.translate('xpack.lens.xyChart.splitSeries', { defaultMessage: 'Break down by', }), - accessors: layer.splitAccessor + accessors: dataLayer.splitAccessor ? [ { - columnId: layer.splitAccessor, + columnId: dataLayer.splitAccessor, triggerIcon: 'colorBy' as const, palette: paletteService - .get(layer.palette?.name || 'default') - .getCategoricalColors(10, layer.palette?.params), + .get(dataLayer.palette?.name || 'default') + .getCategoricalColors(10, dataLayer.palette?.params), }, ] : [], filterOperations: isBucketed, - supportsMoreColumns: !layer.splitAccessor, + supportsMoreColumns: !dataLayer.splitAccessor, dataTestSubj: 'lnsXY_splitDimensionPanel', - required: layer.seriesType.includes('percentage') && hasOnlyOneAccessor, + required: dataLayer.seriesType.includes('percentage') && hasOnlyOneAccessor, enableDimensionEditor: true, }, ], @@ -279,7 +281,7 @@ export const getXyVisualization = ({ return setReferenceDimension(props); } - const newLayer = { ...foundLayer }; + const newLayer: XYDataLayerConfig = Object.assign(foundLayer); if (groupId === 'x') { newLayer.xAccessor = columnId; } @@ -386,7 +388,7 @@ export const getXyVisualization = ({ } else if (newLayer.splitAccessor === columnId) { delete newLayer.splitAccessor; // as the palette is associated with the break down by dimension, remove it together with the dimension - delete newLayer.palette; + newLayer.palette = { type: 'palette', name: '' }; } } if (newLayer.accessors.includes(columnId)) { diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization_helpers.tsx b/x-pack/plugins/lens/public/xy_visualization/visualization_helpers.tsx index b327508a143fa..5903da6083ead 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization_helpers.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/visualization_helpers.tsx @@ -8,15 +8,16 @@ import { i18n } from '@kbn/i18n'; import { uniq } from 'lodash'; import { DatasourcePublicAPI, OperationMetadata, VisualizationType } from '../types'; -import { State, visualizationTypes, XYState } from './types'; -import { isHorizontalChart } from './state_helpers'; import { - DataLayerConfigResult, - ReferenceLineLayerConfigResult, - SeriesType, - XYDataLayerConfig, + State, + visualizationTypes, + XYState, XYLayerConfig, -} from '../../../../../src/plugins/chart_expressions/expression_xy/common'; + XYDataLayerConfig, + XYReferenceLineLayerConfig, +} from './types'; +import { isHorizontalChart } from './state_helpers'; +import { SeriesType } from '../../../../../src/plugins/chart_expressions/expression_xy/common'; import { layerTypes } from '..'; import { LensIconChartBarHorizontal } from '../assets/chart_bar_horizontal'; import { LensIconChartMixedXy } from '../assets/chart_mixed_xy'; @@ -128,22 +129,20 @@ export function checkScaleOperation( }; } -export const isDataLayer = (layer: XYLayerConfig): layer is DataLayerConfigResult => +export const isDataLayer = (layer: XYLayerConfig): layer is XYDataLayerConfig => layer.layerType === layerTypes.DATA || !layer.layerType; export const getDataLayers = (layers: XYLayerConfig[]) => - (layers || []).filter((layer): layer is DataLayerConfigResult => isDataLayer(layer)); + (layers || []).filter((layer): layer is XYDataLayerConfig => isDataLayer(layer)); export const getFirstDataLayer = (layers: XYLayerConfig[]) => - (layers || []).find((layer): layer is DataLayerConfigResult => isDataLayer(layer)); + (layers || []).find((layer): layer is XYDataLayerConfig => isDataLayer(layer)); -export const isReferenceLayer = (layer: XYLayerConfig): layer is ReferenceLineLayerConfigResult => +export const isReferenceLayer = (layer: XYLayerConfig): layer is XYReferenceLineLayerConfig => layer.layerType === layerTypes.REFERENCELINE; export const getReferenceLayers = (layers: XYLayerConfig[]) => - (layers || []).filter((layer): layer is ReferenceLineLayerConfigResult => - isReferenceLayer(layer) - ); + (layers || []).filter((layer): layer is XYReferenceLineLayerConfig => isReferenceLayer(layer)); export function getVisualizationType(state: State): VisualizationType | 'mixed' { if (!state.layers.length) { @@ -246,7 +245,6 @@ export function newLayerState( ): XYLayerConfig { if (layerType === 'data') { return { - type: 'lens_xy_data_layer', layerId, seriesType, accessors: [], @@ -259,7 +257,6 @@ export function newLayerState( } return { - type: 'lens_xy_referenceLine_layer', layerId, accessors: [], layerType, @@ -274,7 +271,7 @@ export function getLayersByType(state: State, byType?: string) { export function validateLayersForDimension( dimension: string, - layers: DataLayerConfigResult[], + layers: XYDataLayerConfig[], missingCriteria: (layer: XYDataLayerConfig) => boolean ): | { valid: true } diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/axis_settings_popover.test.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/axis_settings_popover.test.tsx index a57298787b2e3..77985556efb56 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/axis_settings_popover.test.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/axis_settings_popover.test.tsx @@ -23,6 +23,10 @@ describe('Axes Settings', () => { splitAccessor: 'baz', xAccessor: 'foo', accessors: ['bar'], + xScaleType: 'linear', + yScaleType: 'linear', + isHistogram: false, + palette: { type: 'palette', name: 'default' }, }, ], updateTitleState: jest.fn(), diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/axis_settings_popover.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/axis_settings_popover.tsx index 06b8f3d399256..d41e3c8df5bab 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/axis_settings_popover.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/axis_settings_popover.tsx @@ -20,7 +20,6 @@ import { import { i18n } from '@kbn/i18n'; import { isEqual } from 'lodash'; import { - XYLayerConfig, AxesSettingsConfig, AxisExtentConfig, } from '../../../../../../src/plugins/chart_expressions/expression_xy/common'; @@ -37,6 +36,7 @@ import { EuiIconAxisRight } from '../../assets/axis_right'; import { EuiIconAxisTop } from '../../assets/axis_top'; import { ToolbarButtonProps } from '../../../../../../src/plugins/kibana_react/public'; import { validateExtent } from '../axes_configuration'; +import { XYLayerConfig } from '../types'; type AxesSettingsConfigKeys = keyof AxesSettingsConfig; diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/color_picker.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/color_picker.tsx index b3fad0e7dcfdf..08dc7ab8f15ae 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/color_picker.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/color_picker.tsx @@ -11,7 +11,7 @@ import { debounce } from 'lodash'; import { EuiFormRow, EuiColorPicker, EuiColorPickerProps, EuiToolTip, EuiIcon } from '@elastic/eui'; import type { PaletteRegistry } from 'src/plugins/charts/public'; import type { VisualizationDimensionEditorProps } from '../../types'; -import { State } from '../types'; +import { State, XYDataLayerConfig } from '../types'; import { FormatFactory } from '../../../common'; import { getSeriesColor } from '../state_helpers'; import { @@ -63,7 +63,8 @@ export const ColorPicker = ({ return defaultReferenceLineColor; } - const datasource = frame.datasourceLayers[layer.layerId]; + const dataLayer: XYDataLayerConfig = layer; + const datasource = frame.datasourceLayers[dataLayer.layerId]; const sortedAccessors: string[] = getSortedAccessors(datasource, layer); const colorAssignments = getColorAssignments( @@ -75,8 +76,8 @@ export const ColorPicker = ({ colorAssignments, frame, { - ...layer, - accessors: sortedAccessors.filter((sorted) => layer.accessors.includes(sorted)), + ...dataLayer, + accessors: sortedAccessors.filter((sorted) => dataLayer.accessors.includes(sorted)), }, paletteService ); @@ -109,7 +110,6 @@ export const ColorPicker = ({ newYConfigs.push({ forAccessor: accessor, color: output.hex, - type: 'lens_xy_yConfig', }); } setState(updateLayer(state, { ...layer, yConfig: newYConfigs }, index)); diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/dimension_editor.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/dimension_editor.tsx index 5f67941b970bb..8c21d6e8408fa 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/dimension_editor.tsx @@ -10,7 +10,7 @@ import { i18n } from '@kbn/i18n'; import { EuiButtonGroup, EuiFormRow, htmlIdGenerator } from '@elastic/eui'; import type { PaletteRegistry } from 'src/plugins/charts/public'; import type { VisualizationDimensionEditorProps } from '../../types'; -import { State } from '../types'; +import { State, XYDataLayerConfig } from '../types'; import { FormatFactory } from '../../../common'; import { YAxisMode } from '../../../../../../src/plugins/chart_expressions/expression_xy/common'; import { isHorizontalChart } from '../state_helpers'; @@ -50,10 +50,11 @@ export function DimensionEditor( if (isReferenceLayer(layer)) { return ; } + const dataLayer: XYDataLayerConfig = layer; const axisMode = - (layer.yConfig && - layer.yConfig?.find((yAxisConfig) => yAxisConfig.forAccessor === accessor)?.axisMode) || + (dataLayer.yConfig && + dataLayer.yConfig?.find((yAxisConfig) => yAxisConfig.forAccessor === accessor)?.axisMode) || 'auto'; if (props.groupId === 'breakdown') { @@ -61,9 +62,9 @@ export function DimensionEditor( <> { - setState(updateLayer(state, { ...layer, palette: newPalette }, index)); + setState(updateLayer(state, { ...dataLayer, palette: newPalette }, index)); }} /> @@ -125,7 +126,7 @@ export function DimensionEditor( idSelected={`${idPrefix}${axisMode}`} onChange={(id) => { const newMode = id.replace(idPrefix, '') as YAxisMode; - const newYAxisConfigs = [...(layer.yConfig || [])]; + const newYAxisConfigs = [...(dataLayer.yConfig || [])]; const existingIndex = newYAxisConfigs.findIndex( (yAxisConfig) => yAxisConfig.forAccessor === accessor ); @@ -138,10 +139,9 @@ export function DimensionEditor( newYAxisConfigs.push({ forAccessor: accessor, axisMode: newMode, - type: 'lens_xy_yConfig', }); } - setState(updateLayer(state, { ...layer, yConfig: newYAxisConfigs }, index)); + setState(updateLayer(state, { ...dataLayer, yConfig: newYAxisConfigs }, index)); }} /> diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_panel.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_panel.tsx index 2842ef96d7d7c..fd83ad493757b 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_panel.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_panel.tsx @@ -57,7 +57,6 @@ export const ReferenceLinePanel = ( newYConfigs.push({ forAccessor: accessor, ...yConfig, - type: 'lens_xy_yConfig', }); } setLocalState(updateLayer(localState, { ...layer, yConfig: newYConfigs }, index)); diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/visual_options_popover.test.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/visual_options_popover.test.tsx index a484bc08d37a1..1f8ec4bea4f7a 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/visual_options_popover.test.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/visual_options_popover.test.tsx @@ -10,13 +10,12 @@ import { shallowWithIntl as shallow } from '@kbn/test-jest-helpers'; import { Position } from '@elastic/charts'; import type { FramePublicAPI } from '../../../types'; import { createMockDatasource, createMockFramePublicAPI } from '../../../mocks'; -import { State } from '../../types'; +import { State, XYLayerConfig } from '../../types'; import { VisualOptionsPopover } from '.'; import { ToolbarPopover, ValueLabelsSettings } from '../../../shared_components'; import { MissingValuesOptions } from './missing_values_option'; import { FillOpacityOption } from './fill_opacity_option'; import { layerTypes } from '../../../../common'; -import { XYLayerConfig } from '../../../../../../../src/plugins/chart_expressions/expression_xy/common'; describe('Visual options popover', () => { let frame: FramePublicAPI; @@ -38,7 +37,6 @@ describe('Visual options popover', () => { yScaleType: 'linear', isHistogram: false, palette: { type: 'palette', name: 'default' }, - type: 'lens_xy_data_layer', }, ], }; @@ -248,7 +246,6 @@ describe('Visual options popover', () => { yScaleType: 'linear', isHistogram: false, palette: { type: 'palette', name: 'default' }, - type: 'lens_xy_data_layer', }, ], fittingFunction: 'Carry', diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/xy_config_panel.test.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/xy_config_panel.test.tsx index 4036d6d3b2eb0..6b43a4ed51d4f 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/xy_config_panel.test.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/xy_config_panel.test.tsx @@ -39,7 +39,6 @@ describe('XY Config panels', () => { xScaleType: 'linear', yScaleType: 'linear', isHistogram: false, - type: 'lens_xy_data_layer', palette: { type: 'palette', name: 'default' }, }, ], @@ -72,7 +71,7 @@ describe('XY Config panels', () => { layers: [ { ...state.layers[0], - yConfig: [{ axisMode: 'right', forAccessor: 'bar', type: 'lens_xy_yConfig' }], + yConfig: [{ axisMode: 'right', forAccessor: 'bar' }], }, ], }} @@ -93,7 +92,7 @@ describe('XY Config panels', () => { layers: [ { ...state.layers[0], - yConfig: [{ axisMode: 'right', forAccessor: 'foo', type: 'lens_xy_yConfig' }], + yConfig: [{ axisMode: 'right', forAccessor: 'foo' }], }, ], }} @@ -118,7 +117,7 @@ describe('XY Config panels', () => { layers: [ { ...state.layers[0], - yConfig: [{ axisMode: 'right', forAccessor: 'foo', type: 'lens_xy_yConfig' }], + yConfig: [{ axisMode: 'right', forAccessor: 'foo' }], }, ], }} @@ -299,7 +298,6 @@ describe('XY Config panels', () => { xScaleType: 'linear', yScaleType: 'linear', isHistogram: false, - type: 'lens_xy_data_layer', palette: { type: 'palette', name: 'default' }, }, ], @@ -341,11 +339,10 @@ describe('XY Config panels', () => { splitAccessor: undefined, xAccessor: 'foo', accessors: ['bar'], - yConfig: [{ forAccessor: 'bar', color: 'red', type: 'lens_xy_yConfig' }], + yConfig: [{ forAccessor: 'bar', color: 'red' }], xScaleType: 'linear', yScaleType: 'linear', isHistogram: false, - type: 'lens_xy_data_layer', palette: { type: 'palette', name: 'default' }, }, ], diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.test.ts b/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.test.ts index 920fcd7a7c5a2..365b77ce63ff6 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.test.ts @@ -15,7 +15,7 @@ import { PaletteOutput } from 'src/plugins/charts/public'; import { layerTypes } from '../../common'; import { fieldFormatsServiceMock } from '../../../../../src/plugins/field_formats/public/mocks'; import { themeServiceMock } from '../../../../../src/core/public/mocks'; -import { XYDataLayerConfig } from '../../../../../src/plugins/chart_expressions/expression_xy/common'; +import { XYDataLayerConfig } from './types'; jest.mock('../id_generator'); @@ -203,7 +203,6 @@ describe('xy_suggestions', () => { xScaleType: 'linear', isHistogram: false, palette: { type: 'palette', name: 'default' }, - type: 'lens_xy_data_layer', }, { layerId: 'second', @@ -215,7 +214,6 @@ describe('xy_suggestions', () => { xScaleType: 'linear', isHistogram: false, palette: { type: 'palette', name: 'default' }, - type: 'lens_xy_data_layer', }, ], }, @@ -320,7 +318,6 @@ describe('xy_suggestions', () => { xScaleType: 'linear', isHistogram: false, palette: { type: 'palette', name: 'default' }, - type: 'lens_xy_data_layer', }, ], }, @@ -367,7 +364,6 @@ describe('xy_suggestions', () => { xScaleType: 'linear', isHistogram: false, palette: { type: 'palette', name: 'default' }, - type: 'lens_xy_data_layer', }, { layerId: 'second', @@ -380,7 +376,6 @@ describe('xy_suggestions', () => { xScaleType: 'linear', isHistogram: false, palette: { type: 'palette', name: 'default' }, - type: 'lens_xy_data_layer', }, ], }, @@ -614,7 +609,6 @@ describe('xy_suggestions', () => { xScaleType: 'linear', isHistogram: false, palette: { type: 'palette', name: 'default' }, - type: 'lens_xy_data_layer', }, ], }, @@ -674,7 +668,6 @@ describe('xy_suggestions', () => { xScaleType: 'linear', isHistogram: false, palette: { type: 'palette', name: 'default' }, - type: 'lens_xy_data_layer', }, ], }; @@ -718,7 +711,6 @@ describe('xy_suggestions', () => { xScaleType: 'linear', isHistogram: false, palette: { type: 'palette', name: 'default' }, - type: 'lens_xy_data_layer', }, ], }; @@ -766,7 +758,6 @@ describe('xy_suggestions', () => { xScaleType: 'linear', isHistogram: false, palette: { type: 'palette', name: 'default' }, - type: 'lens_xy_data_layer', }, ], }; @@ -815,7 +806,6 @@ describe('xy_suggestions', () => { xScaleType: 'linear', isHistogram: false, palette: { type: 'palette', name: 'default' }, - type: 'lens_xy_data_layer', }, ], }; @@ -858,7 +848,6 @@ describe('xy_suggestions', () => { xScaleType: 'linear', isHistogram: false, palette: { type: 'palette', name: 'default' }, - type: 'lens_xy_data_layer', }, ], }; @@ -904,7 +893,6 @@ describe('xy_suggestions', () => { xScaleType: 'linear', isHistogram: false, palette: { type: 'palette', name: 'default' }, - type: 'lens_xy_data_layer', }, ], }; @@ -954,7 +942,6 @@ describe('xy_suggestions', () => { xScaleType: 'linear', isHistogram: false, palette: { type: 'palette', name: 'default' }, - type: 'lens_xy_data_layer', }, ], }; @@ -1005,7 +992,6 @@ describe('xy_suggestions', () => { xScaleType: 'linear', isHistogram: false, palette: { type: 'palette', name: 'default' }, - type: 'lens_xy_data_layer', }, ], }; diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.ts b/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.ts index c6cefff276c16..5ab1a322cd574 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.ts +++ b/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.ts @@ -16,11 +16,10 @@ import { TableSuggestion, TableChangeType, } from '../types'; -import { State, XYState, visualizationTypes } from './types'; +import { State, XYState, visualizationTypes, XYLayerConfig, XYDataLayerConfig } from './types'; import type { DataLayerConfigResult, SeriesType, - XYLayerConfig, } from '../../../../../src/plugins/chart_expressions/expression_xy/common'; import { layerTypes } from '../../common'; import { getIconForSeries } from './state_helpers'; @@ -504,12 +503,12 @@ function buildSuggestion({ } const existingLayer = getExistingLayer(currentState, layerId) || null; const accessors = yValues.map((col) => col.columnId); - const newLayer: DataLayerConfigResult = { + const newLayer: XYDataLayerConfig = { ...(existingLayer || {}), palette: mainPalette || (existingLayer && 'palette' in existingLayer - ? (existingLayer as DataLayerConfigResult).palette + ? (existingLayer as XYDataLayerConfig).palette : { type: 'palette', name: '' }), layerId, seriesType, @@ -524,7 +523,6 @@ function buildSuggestion({ xScaleType: (existingLayer as DataLayerConfigResult).xScaleType ?? 'linear', yScaleType: (existingLayer as DataLayerConfigResult).yScaleType ?? 'linear', isHistogram: (existingLayer as DataLayerConfigResult).isHistogram ?? false, - type: 'lens_xy_data_layer', }; // Maintain consistent order for any layers that were saved diff --git a/x-pack/plugins/lens/server/expressions/expressions.ts b/x-pack/plugins/lens/server/expressions/expressions.ts index 84e238b3eb15e..9b23c1741556a 100644 --- a/x-pack/plugins/lens/server/expressions/expressions.ts +++ b/x-pack/plugins/lens/server/expressions/expressions.ts @@ -7,18 +7,9 @@ import type { CoreSetup } from 'kibana/server'; import { - xyChart, counterRate, - yAxisConfig, - dataLayerConfig, - referenceLineLayerConfig, formatColumn, - legendConfig, renameColumns, - gridlinesConfig, - datatableColumn, - tickLabelsConfig, - axisTitlesVisibilityConfig, getTimeScale, getDatatable, lensMultitable, @@ -35,18 +26,9 @@ export const setupExpressions = ( [lensMultitable].forEach((expressionType) => expressions.registerType(expressionType)); [ - xyChart, counterRate, - yAxisConfig, - dataLayerConfig, - referenceLineLayerConfig, formatColumn, - legendConfig, renameColumns, - gridlinesConfig, - datatableColumn, - tickLabelsConfig, - axisTitlesVisibilityConfig, getDatatable(getFormatFactory(core)), getTimeScale(getTimeZoneFactory(core)), ].forEach((expressionFn) => expressions.registerFunction(expressionFn)); From 663e0932ebbd9f649c8f7e55c5d59d33a821bc9b Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Mon, 14 Mar 2022 12:42:37 +0200 Subject: [PATCH 007/133] Fixed import of scss. --- .../expression_xy/public/components/xy_chart.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx index 70eb522a528d6..80a77959be6a3 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx @@ -6,8 +6,6 @@ * Side Public License, v 1. */ -import './expression.scss'; - import React, { useRef } from 'react'; import { Chart, @@ -75,6 +73,8 @@ import { import { visualizationDefinitions } from '../definitions'; import { XYLayerConfigResult } from '../../common/types'; +import './xy_chart.scss'; + declare global { interface Window { /** From cbffe77ff150242260629c56de72647453806e82 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Mon, 14 Mar 2022 12:51:07 +0200 Subject: [PATCH 008/133] Fixed imports. --- .../public/components/reference_lines.tsx | 4 ++-- .../expression_xy/public/components/xy_chart.tsx | 10 +++++----- .../public/expression_renderers/xy_chart_renderer.tsx | 10 +++++----- .../expression_xy/public/helpers/color_assignment.ts | 4 ++-- .../expression_xy/public/helpers/interval.ts | 2 +- .../chart_expressions/expression_xy/public/plugin.ts | 8 ++++---- .../chart_expressions/expression_xy/public/types.ts | 8 ++++---- 7 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.tsx b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.tsx index 566c71782ef2b..3398226d7f74b 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.tsx @@ -12,9 +12,9 @@ import React from 'react'; import { groupBy } from 'lodash'; import { EuiIcon } from '@elastic/eui'; import { RectAnnotation, AnnotationDomainType, LineAnnotation, Position } from '@elastic/charts'; -import type { PaletteRegistry } from 'src/plugins/charts/public'; -import type { FieldFormat } from 'src/plugins/field_formats/common'; import { euiLightVars } from '@kbn/ui-theme'; +import type { FieldFormat } from '../../../../field_formats/common'; +import type { PaletteRegistry } from '../../../../charts/public'; import type { ReferenceLineLayerConfigResult, YConfig } from '../../common'; import type { LensMultiTable } from '../../common/types'; import { hasIcon } from '../helpers'; diff --git a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx index 80a77959be6a3..377f54e9f89e5 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx @@ -7,6 +7,8 @@ */ import React, { useRef } from 'react'; +import { IconType } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; import { Chart, Settings, @@ -35,11 +37,9 @@ import { BarSeriesProps, LineSeriesProps, } from '@elastic/charts'; -import type { Datatable, DatatableRow, DatatableColumn } from 'src/plugins/expressions/public'; -import { IconType } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { RenderMode } from 'src/plugins/expressions'; -import { FieldFormat } from 'src/plugins/field_formats/common'; +import type { Datatable, DatatableRow, DatatableColumn } from '../../../../expressions/public'; +import { RenderMode } from '../../../../expressions/common'; +import { FieldFormat } from '../../../../field_formats/common'; import { EmptyPlaceholder } from '../../../../../plugins/charts/public'; import type { FilterEvent, BrushEvent, FormatFactory } from '../types'; import type { DataLayerConfigResult, SeriesType, XYChartProps } from '../../common'; diff --git a/src/plugins/chart_expressions/expression_xy/public/expression_renderers/xy_chart_renderer.tsx b/src/plugins/chart_expressions/expression_xy/public/expression_renderers/xy_chart_renderer.tsx index 40e4e6c706bcc..2b686b92e219c 100644 --- a/src/plugins/chart_expressions/expression_xy/public/expression_renderers/xy_chart_renderer.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/expression_renderers/xy_chart_renderer.tsx @@ -8,13 +8,13 @@ import { i18n } from '@kbn/i18n'; import { I18nProvider } from '@kbn/i18n-react'; -import { ThemeServiceStart } from 'kibana/public'; import React from 'react'; import ReactDOM from 'react-dom'; -import { ChartsPluginStart, PaletteRegistry } from 'src/plugins/charts/public'; -import { ExpressionRenderDefinition } from 'src/plugins/expressions'; -import { FormatFactory } from 'src/plugins/field_formats/common'; -import { KibanaThemeProvider } from 'src/plugins/kibana_react/public'; +import { ThemeServiceStart } from '../../../../kibana/public'; +import { ChartsPluginStart, PaletteRegistry } from '../../../../charts/public'; +import { ExpressionRenderDefinition } from '../../../../expressions'; +import { FormatFactory } from '../../../../field_formats/common'; +import { KibanaThemeProvider } from '../../../../kibana_react/public'; import { XYChartProps } from '../../common'; import { XYChartReportable } from '../components'; import { calculateMinInterval } from '../helpers'; diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.ts index 942b86452ee7b..18bf0b7f67dc5 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.ts @@ -7,9 +7,9 @@ */ import { uniq, mapValues } from 'lodash'; -import type { PaletteOutput, PaletteRegistry } from 'src/plugins/charts/public'; -import type { Datatable } from 'src/plugins/expressions'; import { euiLightVars } from '@kbn/ui-theme'; +import type { PaletteOutput, PaletteRegistry } from '../../../../charts/public'; +import type { Datatable } from '../../../../expressions'; import type { AccessorConfig, FramePublicAPI } from '../types'; import { getColumnToLabelMap } from './state'; import { FormatFactory } from '../types'; diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/interval.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/interval.ts index dbf01d214564d..7e15b49c311d4 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/interval.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/interval.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { search } from 'src/plugins/data/public'; +import { search } from '../../../../data/public'; import { XYChartProps } from '../../common'; import { getFilteredLayers } from './layers'; import { isDataLayer } from './visualization'; diff --git a/src/plugins/chart_expressions/expression_xy/public/plugin.ts b/src/plugins/chart_expressions/expression_xy/public/plugin.ts index 7c6d523cffea9..a5af4ce62f1ac 100755 --- a/src/plugins/chart_expressions/expression_xy/public/plugin.ts +++ b/src/plugins/chart_expressions/expression_xy/public/plugin.ts @@ -6,11 +6,11 @@ * Side Public License, v 1. */ -import { LEGACY_TIME_AXIS } from 'src/plugins/charts/common'; -import { DataPublicPluginStart } from 'src/plugins/data/public'; -import { FieldFormatsStart } from 'src/plugins/field_formats/public'; -import { ChartsPluginStart } from 'src/plugins/charts/public'; import moment from 'moment'; +import { LEGACY_TIME_AXIS } from '../../../charts/common'; +import { DataPublicPluginStart } from '../../../data/public'; +import { FieldFormatsStart } from '../../../field_formats/public'; +import { ChartsPluginStart } from '../../../charts/public'; import { CoreSetup, CoreStart, IUiSettingsClient } from '../../../../core/public'; import { ExpressionXyPluginSetup, ExpressionXyPluginStart, SetupDeps } from './types'; import { diff --git a/src/plugins/chart_expressions/expression_xy/public/types.ts b/src/plugins/chart_expressions/expression_xy/public/types.ts index 9d803213a1302..6fb2371c2aaf5 100755 --- a/src/plugins/chart_expressions/expression_xy/public/types.ts +++ b/src/plugins/chart_expressions/expression_xy/public/types.ts @@ -6,11 +6,11 @@ * Side Public License, v 1. */ -import { Query } from 'src/plugins/data/common'; import { IconType } from '@elastic/eui'; -import { DataPublicPluginSetup } from 'src/plugins/data/public'; -import { FieldFormatsSetup } from 'src/plugins/field_formats/public'; -import { ChartsPluginSetup } from 'src/plugins/charts/public'; +import { Query } from '../../../data/common'; +import { DataPublicPluginSetup } from '../../../data/public'; +import { FieldFormatsSetup } from '../../../field_formats/public'; +import { ChartsPluginSetup } from '../../../charts/public'; import { IFieldFormat, SerializedFieldFormat } from '../../../../plugins/field_formats/common'; import type { RangeSelectContext, ValueClickContext } from '../../../../plugins/embeddable/public'; import { Datatable, ExpressionsServiceStart, ExpressionsSetup } from '../../../expressions/public'; From 389f9336c6218eb411227494f72cc54b96ec8698 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Mon, 14 Mar 2022 13:46:31 +0200 Subject: [PATCH 009/133] Added required plugins. --- src/plugins/chart_expressions/expression_xy/kibana.json | 1 + src/plugins/chart_expressions/expression_xy/tsconfig.json | 1 + 2 files changed, 2 insertions(+) diff --git a/src/plugins/chart_expressions/expression_xy/kibana.json b/src/plugins/chart_expressions/expression_xy/kibana.json index eb34097f9c1f6..7f24173b071b1 100755 --- a/src/plugins/chart_expressions/expression_xy/kibana.json +++ b/src/plugins/chart_expressions/expression_xy/kibana.json @@ -10,5 +10,6 @@ "server": true, "ui": true, "requiredPlugins": ["expressions", "charts", "data", "fieldFormats", "uiActions"], + "requiredBundles": ["kibanaReact"], "optionalPlugins": [] } diff --git a/src/plugins/chart_expressions/expression_xy/tsconfig.json b/src/plugins/chart_expressions/expression_xy/tsconfig.json index be36bbcaf78a2..ce0fa553196fb 100644 --- a/src/plugins/chart_expressions/expression_xy/tsconfig.json +++ b/src/plugins/chart_expressions/expression_xy/tsconfig.json @@ -19,5 +19,6 @@ { "path": "../../data/tsconfig.json"}, { "path": "../../ui_actions/tsconfig.json" }, { "path": "../../field_formats/tsconfig.json"}, + { "path": "../../kibana_utils/tsconfig.json" }, ] } From 90aa97af27734c60bbce93a4b7e4217af15106b3 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Mon, 14 Mar 2022 13:51:26 +0200 Subject: [PATCH 010/133] Fixed import --- .../public/expression_renderers/xy_chart_renderer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/chart_expressions/expression_xy/public/expression_renderers/xy_chart_renderer.tsx b/src/plugins/chart_expressions/expression_xy/public/expression_renderers/xy_chart_renderer.tsx index 2b686b92e219c..ebfc03dfa5b52 100644 --- a/src/plugins/chart_expressions/expression_xy/public/expression_renderers/xy_chart_renderer.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/expression_renderers/xy_chart_renderer.tsx @@ -8,9 +8,9 @@ import { i18n } from '@kbn/i18n'; import { I18nProvider } from '@kbn/i18n-react'; +import { ThemeServiceStart } from 'kibana/public'; import React from 'react'; import ReactDOM from 'react-dom'; -import { ThemeServiceStart } from '../../../../kibana/public'; import { ChartsPluginStart, PaletteRegistry } from '../../../../charts/public'; import { ExpressionRenderDefinition } from '../../../../expressions'; import { FormatFactory } from '../../../../field_formats/common'; From 1d3a264ce32e57bc410666dc6c2dd0302654480b Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Mon, 14 Mar 2022 14:32:55 +0200 Subject: [PATCH 011/133] Fixed types. --- .../public/xy_visualization/color_assignment.test.ts | 6 ++---- .../lens/public/xy_visualization/color_assignment.ts | 9 ++++----- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/x-pack/plugins/lens/public/xy_visualization/color_assignment.test.ts b/x-pack/plugins/lens/public/xy_visualization/color_assignment.test.ts index aa9e73c712fb1..d1d03ca62376d 100644 --- a/x-pack/plugins/lens/public/xy_visualization/color_assignment.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/color_assignment.test.ts @@ -7,13 +7,12 @@ import { getColorAssignments } from './color_assignment'; import type { FormatFactory, LensMultiTable } from '../../common'; -import type { DataLayerConfigResult } from '../../../../../src/plugins/chart_expressions/expression_xy/common'; import { layerTypes } from '../../common'; +import { XYDataLayerConfig } from './types'; describe('color_assignment', () => { - const layers: DataLayerConfigResult[] = [ + const layers: XYDataLayerConfig[] = [ { - type: 'lens_xy_data_layer', yScaleType: 'linear', xScaleType: 'linear', isHistogram: true, @@ -25,7 +24,6 @@ describe('color_assignment', () => { accessors: ['y1', 'y2'], }, { - type: 'lens_xy_data_layer', yScaleType: 'linear', xScaleType: 'linear', isHistogram: true, diff --git a/x-pack/plugins/lens/public/xy_visualization/color_assignment.ts b/x-pack/plugins/lens/public/xy_visualization/color_assignment.ts index 331263c9b9b94..7cdc9a1ec6f48 100644 --- a/x-pack/plugins/lens/public/xy_visualization/color_assignment.ts +++ b/x-pack/plugins/lens/public/xy_visualization/color_assignment.ts @@ -13,7 +13,6 @@ import type { AccessorConfig, FramePublicAPI } from '../types'; import { getColumnToLabelMap } from './state_helpers'; import { FormatFactory } from '../../common'; import { isDataLayer, isReferenceLayer } from './visualization_helpers'; -import { DataLayerConfigResult } from '../../../../../src/plugins/chart_expressions/expression_xy/common'; import { XYDataLayerConfig, XYLayerConfig, XYReferenceLineLayerConfig } from './types'; const isPrimitive = (value: unknown): boolean => value != null && typeof value !== 'object'; @@ -24,7 +23,7 @@ export type ColorAssignments = Record< string, { totalSeriesCount: number; - getRank(sortedLayer: DataLayerConfigResult, seriesKey: string, yAccessor: string): number; + getRank(sortedLayer: XYDataLayerConfig, seriesKey: string, yAccessor: string): number; } >; @@ -33,10 +32,10 @@ export function getColorAssignments( data: { tables: Record }, formatFactory: FormatFactory ): ColorAssignments { - const layersPerPalette: Record = {}; + const layersPerPalette: Record = {}; layers - .filter((layer): layer is DataLayerConfigResult => isDataLayer(layer)) + .filter((layer): layer is XYDataLayerConfig => isDataLayer(layer)) .forEach((layer) => { const palette = layer.palette?.name || 'default'; if (!layersPerPalette[palette]) { @@ -75,7 +74,7 @@ export function getColorAssignments( ); return { totalSeriesCount, - getRank(sortedLayer: DataLayerConfigResult, seriesKey: string, yAccessor: string) { + getRank(sortedLayer: XYDataLayerConfig, seriesKey: string, yAccessor: string) { const layerIndex = paletteLayers.findIndex((l) => sortedLayer.layerId === l.layerId); const currentSeriesPerLayer = seriesPerLayer[layerIndex]; const splitRank = currentSeriesPerLayer.splits.indexOf(seriesKey); From a78d45da89ad4612ef02ed1bfe0e22048c3f9362 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Mon, 14 Mar 2022 21:18:48 +0200 Subject: [PATCH 012/133] Changed expression names. --- .../expression_xy/common/__mocks__/index.ts | 16 +- .../expression_xy/common/constants.ts | 22 +- .../expression_functions/expression.test.tsx | 16 +- .../expression_xy/public/__mocks__/index.tsx | 4 +- .../__snapshots__/xy_chart.test.tsx.snap | 1705 +++++++++++++++++ .../public/components/xy_chart.test.tsx | 139 +- .../xy_chart_renderer.tsx | 2 +- .../public/helpers/axes_configuration.test.ts | 8 +- .../public/helpers/color_assignment.test.ts | 4 +- .../editor_frame/config_panel/layer_panel.tsx | 3 +- .../__snapshots__/to_expression.test.ts.snap | 18 +- .../axes_configuration.test.ts | 2 +- .../xy_visualization/color_assignment.ts | 1 + .../reference_line_helpers.test.ts | 4 +- .../public/xy_visualization/to_expression.ts | 25 +- .../saved_object_migrations.test.ts | 8 +- 16 files changed, 1842 insertions(+), 135 deletions(-) create mode 100644 src/plugins/chart_expressions/expression_xy/public/components/__snapshots__/xy_chart.test.tsx.snap diff --git a/src/plugins/chart_expressions/expression_xy/common/__mocks__/index.ts b/src/plugins/chart_expressions/expression_xy/common/__mocks__/index.ts index 605ab3c2d2728..4bafffc065835 100644 --- a/src/plugins/chart_expressions/expression_xy/common/__mocks__/index.ts +++ b/src/plugins/chart_expressions/expression_xy/common/__mocks__/index.ts @@ -47,7 +47,7 @@ export const createSampleDatatableWithRows = (rows: DatatableRow[]): Datatable = }); export const sampleLayer: DataLayerConfigResult = { - type: 'lens_xy_data_layer', + type: 'dataLayer', layerId: 'first', layerType: LayerTypes.DATA, seriesType: 'line', @@ -66,43 +66,43 @@ export const createArgsWithLayers = (layers: DataLayerConfigResult[] = [sampleLa yTitle: '', yRightTitle: '', legend: { - type: 'lens_xy_legendConfig', + type: 'legendConfig', isVisible: false, position: Position.Top, }, valueLabels: 'hide', valuesInLegend: false, axisTitlesVisibilitySettings: { - type: 'lens_xy_axisTitlesVisibilityConfig', + type: 'axisTitlesVisibilityConfig', x: true, yLeft: true, yRight: true, }, tickLabelsVisibilitySettings: { - type: 'lens_xy_tickLabelsConfig', + type: 'tickLabelsConfig', x: true, yLeft: false, yRight: false, }, labelsOrientation: { - type: 'lens_xy_labelsOrientationConfig', + type: 'labelsOrientationConfig', x: 0, yLeft: -90, yRight: -45, }, gridlinesVisibilitySettings: { - type: 'lens_xy_gridlinesConfig', + type: 'gridlinesConfig', x: true, yLeft: false, yRight: false, }, yLeftExtent: { mode: 'full', - type: 'lens_xy_axisExtentConfig', + type: 'axisExtentConfig', }, yRightExtent: { mode: 'full', - type: 'lens_xy_axisExtentConfig', + type: 'axisExtentConfig', }, layers, }); diff --git a/src/plugins/chart_expressions/expression_xy/common/constants.ts b/src/plugins/chart_expressions/expression_xy/common/constants.ts index 0bf0f61959bfa..f77f8950f78bf 100644 --- a/src/plugins/chart_expressions/expression_xy/common/constants.ts +++ b/src/plugins/chart_expressions/expression_xy/common/constants.ts @@ -6,18 +6,18 @@ * Side Public License, v 1. */ -export const XY_CHART = 'lens_xy_chart'; -export const Y_CONFIG = 'lens_xy_yConfig'; +export const XY_CHART = 'xyVis'; +export const Y_CONFIG = 'yConfig'; export const MULTITABLE = 'lens_multitable'; -export const DATA_LAYER = 'lens_xy_data_layer'; -export const LEGEND_CONFIG = 'lens_xy_legendConfig'; -export const XY_CHART_RENDERER = 'lens_xy_chart_renderer'; -export const GRID_LINES_CONFIG = 'lens_xy_gridlinesConfig'; -export const TICK_LABELS_CONFIG = 'lens_xy_tickLabelsConfig'; -export const AXIS_EXTENT_CONFIG = 'lens_xy_axisExtentConfig'; -export const REFERENCE_LINE_LAYER = 'lens_xy_referenceLine_layer'; -export const LABELS_ORIENTATION_CONFIG = 'lens_xy_labelsOrientationConfig'; -export const AXIS_TITLES_VISIBILITY_CONFIG = 'lens_xy_axisTitlesVisibilityConfig'; +export const DATA_LAYER = 'dataLayer'; +export const LEGEND_CONFIG = 'legendConfig'; +export const XY_CHART_RENDERER = 'xyVis'; +export const GRID_LINES_CONFIG = 'gridlinesConfig'; +export const TICK_LABELS_CONFIG = 'tickLabelsConfig'; +export const AXIS_EXTENT_CONFIG = 'axisExtentConfig'; +export const REFERENCE_LINE_LAYER = 'referenceLineLayer'; +export const LABELS_ORIENTATION_CONFIG = 'labelsOrientationConfig'; +export const AXIS_TITLES_VISIBILITY_CONFIG = 'axisTitlesVisibilityConfig'; export const LayerTypes = { DATA: 'data', diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/expression.test.tsx b/src/plugins/chart_expressions/expression_xy/common/expression_functions/expression.test.tsx index 9ec9e5416ab62..c9b92583ae9f5 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/expression.test.tsx +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/expression.test.tsx @@ -18,6 +18,7 @@ import { } from '../expression_functions'; import { createMockExecutionContext } from '../../../../../plugins/expressions/common/mocks'; import { mockPaletteOutput, sampleArgs } from '../__mocks__'; +import { LayerTypes } from '../constants'; describe('xy_expression', () => { describe('configs', () => { @@ -30,12 +31,12 @@ describe('xy_expression', () => { const result = legendConfigFunction.fn(null, args, createMockExecutionContext()); expect(result).toEqual({ - type: 'lens_xy_legendConfigFunction', + type: 'legendConfig', ...args, }); }); - test('dataLayerConfigFunction produces the correct arguments', () => { + test('dataLayerConfig produces the correct arguments', () => { const args: DataLayerArgs = { layerId: 'first', seriesType: 'line', @@ -51,7 +52,8 @@ describe('xy_expression', () => { const result = dataLayerConfigFunction.fn(null, args, createMockExecutionContext()); expect(result).toEqual({ - type: 'lens_xy_data_layer', + type: 'dataLayer', + layerType: LayerTypes.DATA, ...args, }); }); @@ -67,7 +69,7 @@ describe('xy_expression', () => { const result = tickLabelsConfigFunction.fn(null, args, createMockExecutionContext()); expect(result).toEqual({ - type: 'lens_xy_tickLabelsConfigFunction', + type: 'tickLabelsConfig', ...args, }); }); @@ -82,7 +84,7 @@ describe('xy_expression', () => { const result = gridlinesConfigFunction.fn(null, args, createMockExecutionContext()); expect(result).toEqual({ - type: 'lens_xy_gridlinesConfigFunction', + type: 'gridlinesConfig', ...args, }); }); @@ -97,7 +99,7 @@ describe('xy_expression', () => { const result = labelsOrientationConfigFunction.fn(null, args, createMockExecutionContext()); expect(result).toEqual({ - type: 'lens_xy_labelsOrientationConfigFunction', + type: 'labelsOrientationConfig', ...args, }); }); @@ -109,7 +111,7 @@ describe('xy_expression', () => { expect(result).toEqual({ type: 'render', - as: 'lens_xy_chart_renderer', + as: 'xyVis', value: { data, args }, }); }); diff --git a/src/plugins/chart_expressions/expression_xy/public/__mocks__/index.tsx b/src/plugins/chart_expressions/expression_xy/public/__mocks__/index.tsx index 073126c3f7ba7..cc73950438f38 100644 --- a/src/plugins/chart_expressions/expression_xy/public/__mocks__/index.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/__mocks__/index.tsx @@ -167,7 +167,7 @@ export const dateHistogramData: LensMultiTable = { }; export const dateHistogramLayer: DataLayerConfigResult = { - type: 'lens_xy_data_layer', + type: 'dataLayer', layerId: 'timeLayer', layerType: LayerTypes.DATA, hide: false, @@ -216,7 +216,7 @@ export function sampleArgsWithReferenceLine(value: number = 150) { palette: mockPaletteOutput, isHistogram: false, hide: true, - yConfig: [{ axisMode: 'left', forAccessor: 'referenceLine-a', type: 'lens_xy_yConfig' }], + yConfig: [{ axisMode: 'left', forAccessor: 'referenceLine-a', type: 'yConfig' }], }, ], } as XYArgs, diff --git a/src/plugins/chart_expressions/expression_xy/public/components/__snapshots__/xy_chart.test.tsx.snap b/src/plugins/chart_expressions/expression_xy/public/components/__snapshots__/xy_chart.test.tsx.snap new file mode 100644 index 0000000000000..210b02f984284 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/public/components/__snapshots__/xy_chart.test.tsx.snap @@ -0,0 +1,1705 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`XYChart component it renders area 1`] = ` + + + + + + + + +`; + +exports[`XYChart component it renders bar 1`] = ` + + + + + + + + +`; + +exports[`XYChart component it renders horizontal bar 1`] = ` + + + + + + + + +`; + +exports[`XYChart component it renders line 1`] = ` + + + + + + + + +`; + +exports[`XYChart component it renders stacked area 1`] = ` + + + + + + + + +`; + +exports[`XYChart component it renders stacked bar 1`] = ` + + + + + + + + +`; + +exports[`XYChart component it renders stacked horizontal bar 1`] = ` + + + + + + + + +`; diff --git a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx index 067e0a5503d34..7abd103bf10d0 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx @@ -128,7 +128,7 @@ describe('XYChart component', () => { { ...args.layers[0], seriesType: 'line', - type: 'lens_xy_data_layer', + type: 'dataLayer', } as DataLayerConfigResult, ], }} @@ -142,7 +142,7 @@ describe('XYChart component', () => { describe('date range', () => { const timeSampleLayer: DataLayerConfigResult = { - type: 'lens_xy_data_layer', + type: 'dataLayer', layerId: 'first', layerType: LayerTypes.DATA, seriesType: 'line', @@ -204,7 +204,7 @@ describe('XYChart component', () => { ...args.layers[0], seriesType: 'line', xScaleType: 'time', - type: 'lens_xy_data_layer', + type: 'dataLayer', } as DataLayerConfigResult, ], }} @@ -243,7 +243,7 @@ describe('XYChart component', () => { describe('axis time', () => { const defaultTimeLayer: DataLayerConfigResult = { - type: 'lens_xy_data_layer', + type: 'dataLayer', layerId: 'first', layerType: LayerTypes.DATA, seriesType: 'line', @@ -397,7 +397,7 @@ describe('XYChart component', () => { layers: [ { ...args.layers[0], - type: 'lens_xy_data_layer', + type: 'dataLayer', seriesType: 'line', xScaleType: 'time', isHistogram: true, @@ -474,7 +474,7 @@ describe('XYChart component', () => { layers: [ { ...args.layers[0], - type: 'lens_xy_data_layer', + type: 'dataLayer', layerType: 'data', seriesType: 'bar', xScaleType: 'time', @@ -521,7 +521,7 @@ describe('XYChart component', () => { args={{ ...args, yLeftExtent: { - type: 'lens_xy_axisExtentConfig', + type: 'axisExtentConfig', mode: 'custom', lowerBound: 123, upperBound: 456, @@ -546,7 +546,7 @@ describe('XYChart component', () => { args={{ ...args, yLeftExtent: { - type: 'lens_xy_axisExtentConfig', + type: 'axisExtentConfig', mode: 'dataBounds', }, }} @@ -569,7 +569,7 @@ describe('XYChart component', () => { args={{ ...args, yLeftExtent: { - type: 'lens_xy_axisExtentConfig', + type: 'axisExtentConfig', mode: 'dataBounds', }, layers: [ @@ -577,7 +577,7 @@ describe('XYChart component', () => { ...args.layers[0], layerType: 'data', seriesType: 'area', - type: 'lens_xy_data_layer', + type: 'dataLayer', xScaleType: 'linear', yScaleType: 'linear', isHistogram: false, @@ -604,7 +604,7 @@ describe('XYChart component', () => { args={{ ...args, yLeftExtent: { - type: 'lens_xy_axisExtentConfig', + type: 'axisExtentConfig', mode: 'custom', lowerBound: 123, upperBound: 456, @@ -613,7 +613,7 @@ describe('XYChart component', () => { { ...args.layers[0], seriesType: 'bar', - type: 'lens_xy_data_layer', + type: 'dataLayer', layerType: 'data', xScaleType: 'linear', yScaleType: 'linear', @@ -652,7 +652,7 @@ describe('XYChart component', () => { args={{ ...args, yLeftExtent: { - type: 'lens_xy_axisExtentConfig', + type: 'axisExtentConfig', mode: 'custom', lowerBound: 123, upperBound: 456, @@ -699,7 +699,7 @@ describe('XYChart component', () => { ...args.layers[0], seriesType: 'line', xScaleType: 'linear', - type: 'lens_xy_data_layer', + type: 'dataLayer', layerType: 'data', yScaleType: 'linear', isHistogram: false, @@ -729,7 +729,7 @@ describe('XYChart component', () => { seriesType: 'line', xScaleType: 'linear', isHistogram: true, - type: 'lens_xy_data_layer', + type: 'dataLayer', layerType: 'data', yScaleType: 'linear', palette: { type: 'palette', name: 'default' }, @@ -787,7 +787,7 @@ describe('XYChart component', () => { { ...args.layers[0], seriesType: 'bar', - type: 'lens_xy_data_layer', + type: 'dataLayer', layerType: 'data', xScaleType: 'linear', yScaleType: 'linear', @@ -816,7 +816,7 @@ describe('XYChart component', () => { { ...args.layers[0], seriesType: 'area', - type: 'lens_xy_data_layer', + type: 'dataLayer', layerType: 'data', xScaleType: 'linear', yScaleType: 'linear', @@ -845,7 +845,7 @@ describe('XYChart component', () => { { ...args.layers[0], seriesType: 'bar_horizontal', - type: 'lens_xy_data_layer', + type: 'dataLayer', layerType: 'data', xScaleType: 'linear', yScaleType: 'linear', @@ -902,7 +902,7 @@ describe('XYChart component', () => { const numberLayer: DataLayerConfigResult = { layerId: 'numberLayer', - type: 'lens_xy_data_layer', + type: 'dataLayer', layerType: LayerTypes.DATA, hide: false, xAccessor: 'xAccessorId', @@ -1024,7 +1024,7 @@ describe('XYChart component', () => { layers: [ { layerId: 'first', - type: 'lens_xy_data_layer', + type: 'dataLayer', layerType: LayerTypes.DATA, isHistogram: true, seriesType: 'bar_stacked', @@ -1112,7 +1112,7 @@ describe('XYChart component', () => { const { args } = sampleArgs(); const numberLayer: DataLayerConfigResult = { - type: 'lens_xy_data_layer', + type: 'dataLayer', layerId: 'numberLayer', layerType: LayerTypes.DATA, hide: false, @@ -1233,7 +1233,7 @@ describe('XYChart component', () => { layers: [ { layerId: 'first', - type: 'lens_xy_data_layer', + type: 'dataLayer', layerType: LayerTypes.DATA, seriesType: 'line', xAccessor: 'd', @@ -1281,7 +1281,7 @@ describe('XYChart component', () => { layers: [ { layerId: 'first', - type: 'lens_xy_data_layer', + type: 'dataLayer', layerType: LayerTypes.DATA, seriesType: 'line', xAccessor: 'd', @@ -1312,7 +1312,7 @@ describe('XYChart component', () => { layers: [ { layerId: 'first', - type: 'lens_xy_data_layer', + type: 'dataLayer', layerType: LayerTypes.DATA, seriesType: 'line', xAccessor: 'd', @@ -1363,7 +1363,7 @@ describe('XYChart component', () => { { ...args.layers[0], seriesType: 'bar_stacked', - type: 'lens_xy_data_layer', + type: 'dataLayer', layerType: LayerTypes.DATA, xScaleType: 'ordinal', yScaleType: 'linear', @@ -1392,7 +1392,7 @@ describe('XYChart component', () => { { ...args.layers[0], seriesType: 'area_stacked', - type: 'lens_xy_data_layer', + type: 'dataLayer', layerType: LayerTypes.DATA, xScaleType: 'ordinal', yScaleType: 'linear', @@ -1421,7 +1421,7 @@ describe('XYChart component', () => { { ...args.layers[0], seriesType: 'bar_horizontal_stacked', - type: 'lens_xy_data_layer', + type: 'dataLayer', layerType: LayerTypes.DATA, xScaleType: 'ordinal', yScaleType: 'linear', @@ -1451,7 +1451,7 @@ describe('XYChart component', () => { layers: [ { ...args.layers[0], - type: 'lens_xy_data_layer', + type: 'dataLayer', xAccessor: undefined, splitAccessor: 'e', seriesType: 'bar_stacked', @@ -1486,7 +1486,7 @@ describe('XYChart component', () => { accessors: ['b'], seriesType: 'bar', isHistogram: true, - type: 'lens_xy_data_layer', + type: 'dataLayer', layerType: LayerTypes.DATA, xScaleType: 'ordinal', yScaleType: 'linear', @@ -1505,7 +1505,7 @@ describe('XYChart component', () => { ...args.layers[0], seriesType: 'bar', isHistogram: true, - type: 'lens_xy_data_layer', + type: 'dataLayer', layerType: LayerTypes.DATA, xScaleType: 'ordinal', yScaleType: 'linear', @@ -1525,7 +1525,7 @@ describe('XYChart component', () => { ...args.layers[0], seriesType: 'line', isHistogram: true, - type: 'lens_xy_data_layer', + type: 'dataLayer', layerType: LayerTypes.DATA, xScaleType: 'ordinal', yScaleType: 'linear', @@ -1536,7 +1536,7 @@ describe('XYChart component', () => { ...args.layers[0], seriesType: 'line', isHistogram: true, - type: 'lens_xy_data_layer', + type: 'dataLayer', layerType: LayerTypes.DATA, xScaleType: 'ordinal', yScaleType: 'linear', @@ -1567,7 +1567,7 @@ describe('XYChart component', () => { ...args.layers[0], seriesType: 'bar_stacked', isHistogram: true, - type: 'lens_xy_data_layer', + type: 'dataLayer', layerType: LayerTypes.DATA, xScaleType: 'ordinal', yScaleType: 'linear', @@ -1594,7 +1594,7 @@ describe('XYChart component', () => { ...args.layers[0], seriesType: 'bar', isHistogram: true, - type: 'lens_xy_data_layer', + type: 'dataLayer', layerType: LayerTypes.DATA, xScaleType: 'ordinal', yScaleType: 'linear', @@ -1995,7 +1995,7 @@ describe('XYChart component', () => { ...args.layers[0], xScaleType: 'ordinal', seriesType: 'bar_stacked', - type: 'lens_xy_data_layer', + type: 'dataLayer', layerType: LayerTypes.DATA, yScaleType: 'linear', isHistogram: false, @@ -2023,7 +2023,7 @@ describe('XYChart component', () => { ...args.layers[0], yScaleType: 'sqrt', seriesType: 'bar_stacked', - type: 'lens_xy_data_layer', + type: 'dataLayer', layerType: LayerTypes.DATA, xScaleType: 'ordinal', isHistogram: false, @@ -2033,6 +2033,7 @@ describe('XYChart component', () => { }} /> ); + expect(component.find(LineSeries).at(0).prop('yScaleType')).toEqual(ScaleType.Sqrt); expect(component.find(LineSeries).at(1).prop('yScaleType')).toEqual(ScaleType.Sqrt); }); @@ -2084,7 +2085,7 @@ describe('XYChart component', () => { x: false, yLeft: true, yRight: true, - type: 'lens_xy_tickLabelsConfig', + type: 'tickLabelsConfig', }; const instance = shallow(); @@ -2105,7 +2106,7 @@ describe('XYChart component', () => { x: true, yLeft: false, yRight: false, - type: 'lens_xy_tickLabelsConfig', + type: 'tickLabelsConfig', }; const instance = shallow(); @@ -2126,7 +2127,7 @@ describe('XYChart component', () => { x: true, yLeft: true, yRight: true, - type: 'lens_xy_tickLabelsConfig', + type: 'tickLabelsConfig', }; const instance = shallow(); @@ -2147,7 +2148,7 @@ describe('XYChart component', () => { x: -45, yLeft: 0, yRight: -90, - type: 'lens_xy_labelsOrientationConfig', + type: 'labelsOrientationConfig', }; const instance = shallow(); @@ -2168,7 +2169,7 @@ describe('XYChart component', () => { x: false, yLeft: true, yRight: true, - type: 'lens_xy_tickLabelsConfig', + type: 'tickLabelsConfig', }; const instance = shallow(); @@ -2189,7 +2190,7 @@ describe('XYChart component', () => { x: -45, yLeft: -90, yRight: -90, - type: 'lens_xy_labelsOrientationConfig', + type: 'labelsOrientationConfig', }; const instance = shallow(); @@ -2238,38 +2239,38 @@ describe('XYChart component', () => { xTitle: '', yTitle: '', yRightTitle: '', - legend: { type: 'lens_xy_legendConfig', isVisible: false, position: Position.Top }, + legend: { type: 'legendConfig', isVisible: false, position: Position.Top }, valueLabels: 'hide', tickLabelsVisibilitySettings: { - type: 'lens_xy_tickLabelsConfig', + type: 'tickLabelsConfig', x: true, yLeft: true, yRight: true, }, gridlinesVisibilitySettings: { - type: 'lens_xy_gridlinesConfig', + type: 'gridlinesConfig', x: true, yLeft: false, yRight: false, }, labelsOrientation: { - type: 'lens_xy_labelsOrientationConfig', + type: 'labelsOrientationConfig', x: 0, yLeft: 0, yRight: 0, }, yLeftExtent: { mode: 'full', - type: 'lens_xy_axisExtentConfig', + type: 'axisExtentConfig', }, yRightExtent: { mode: 'full', - type: 'lens_xy_axisExtentConfig', + type: 'axisExtentConfig', }, layers: [ { layerId: 'first', - type: 'lens_xy_data_layer', + type: 'dataLayer', layerType: LayerTypes.DATA, seriesType: 'line', xAccessor: 'a', @@ -2283,7 +2284,7 @@ describe('XYChart component', () => { }, { layerId: 'second', - type: 'lens_xy_data_layer', + type: 'dataLayer', layerType: LayerTypes.DATA, seriesType: 'line', xAccessor: 'a', @@ -2330,38 +2331,38 @@ describe('XYChart component', () => { xTitle: '', yTitle: '', yRightTitle: '', - legend: { type: 'lens_xy_legendConfig', isVisible: false, position: Position.Top }, + legend: { type: 'legendConfig', isVisible: false, position: Position.Top }, valueLabels: 'hide', tickLabelsVisibilitySettings: { - type: 'lens_xy_tickLabelsConfig', + type: 'tickLabelsConfig', x: true, yLeft: false, yRight: false, }, gridlinesVisibilitySettings: { - type: 'lens_xy_gridlinesConfig', + type: 'gridlinesConfig', x: true, yLeft: false, yRight: false, }, labelsOrientation: { - type: 'lens_xy_labelsOrientationConfig', + type: 'labelsOrientationConfig', x: 0, yLeft: 0, yRight: 0, }, yLeftExtent: { mode: 'full', - type: 'lens_xy_axisExtentConfig', + type: 'axisExtentConfig', }, yRightExtent: { mode: 'full', - type: 'lens_xy_axisExtentConfig', + type: 'axisExtentConfig', }, layers: [ { layerId: 'first', - type: 'lens_xy_data_layer', + type: 'dataLayer', layerType: LayerTypes.DATA, seriesType: 'line', xAccessor: 'a', @@ -2406,38 +2407,38 @@ describe('XYChart component', () => { xTitle: '', yTitle: '', yRightTitle: '', - legend: { type: 'lens_xy_legendConfig', isVisible: true, position: Position.Top }, + legend: { type: 'legendConfig', isVisible: true, position: Position.Top }, valueLabels: 'hide', tickLabelsVisibilitySettings: { - type: 'lens_xy_tickLabelsConfig', + type: 'tickLabelsConfig', x: true, yLeft: false, yRight: false, }, gridlinesVisibilitySettings: { - type: 'lens_xy_gridlinesConfig', + type: 'gridlinesConfig', x: true, yLeft: false, yRight: false, }, labelsOrientation: { - type: 'lens_xy_labelsOrientationConfig', + type: 'labelsOrientationConfig', x: 0, yLeft: 0, yRight: 0, }, yLeftExtent: { mode: 'full', - type: 'lens_xy_axisExtentConfig', + type: 'axisExtentConfig', }, yRightExtent: { mode: 'full', - type: 'lens_xy_axisExtentConfig', + type: 'axisExtentConfig', }, layers: [ { layerId: 'first', - type: 'lens_xy_data_layer', + type: 'dataLayer', layerType: LayerTypes.DATA, seriesType: 'line', xAccessor: 'a', @@ -2472,7 +2473,7 @@ describe('XYChart component', () => { accessors: ['a'], splitAccessor: undefined, seriesType: 'bar_stacked', - type: 'lens_xy_data_layer', + type: 'dataLayer', layerType: LayerTypes.DATA, xScaleType: 'ordinal', yScaleType: 'linear', @@ -2503,7 +2504,7 @@ describe('XYChart component', () => { accessors: ['a'], splitAccessor: undefined, seriesType: 'bar_stacked', - type: 'lens_xy_data_layer', + type: 'dataLayer', layerType: LayerTypes.DATA, xScaleType: 'ordinal', yScaleType: 'linear', @@ -2616,7 +2617,7 @@ describe('XYChart component', () => { x: false, yLeft: true, yRight: true, - type: 'lens_xy_axisTitlesVisibilityConfig', + type: 'axisTitlesVisibilityConfig', }; const component = shallow(); @@ -2637,7 +2638,7 @@ describe('XYChart component', () => { x: true, yLeft: false, yRight: false, - type: 'lens_xy_gridlinesConfig', + type: 'gridlinesConfig', }; const component = shallow(); @@ -2686,7 +2687,7 @@ describe('XYChart component', () => { }; const timeSampleLayer: DataLayerConfigResult = { layerId: 'first', - type: 'lens_xy_data_layer', + type: 'dataLayer', layerType: LayerTypes.DATA, seriesType: 'line', xAccessor: 'c', diff --git a/src/plugins/chart_expressions/expression_xy/public/expression_renderers/xy_chart_renderer.tsx b/src/plugins/chart_expressions/expression_xy/public/expression_renderers/xy_chart_renderer.tsx index ebfc03dfa5b52..3c4a8559d88a5 100644 --- a/src/plugins/chart_expressions/expression_xy/public/expression_renderers/xy_chart_renderer.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/expression_renderers/xy_chart_renderer.tsx @@ -37,7 +37,7 @@ interface XyChartRendererDeps { export const getXyChartRenderer = ({ getStartDeps, }: XyChartRendererDeps): ExpressionRenderDefinition => ({ - name: 'lens_xy_chart_renderer', + name: 'xyVis', displayName: 'XY chart', help: i18n.translate('xpack.lens.xyChart.renderer.help', { defaultMessage: 'X/Y chart renderer', diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/axes_configuration.test.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/axes_configuration.test.ts index 5b492d2db0f2f..201b0198087da 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/axes_configuration.test.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/axes_configuration.test.ts @@ -221,7 +221,7 @@ describe('axes_configuration', () => { }; const sampleLayer: DataLayerConfigResult = { - type: 'lens_xy_data_layer', + type: 'dataLayer', layerId: 'first', layerType: LayerTypes.DATA, seriesType: 'line', @@ -276,7 +276,7 @@ describe('axes_configuration', () => { [ { ...sampleLayer, - yConfig: [{ type: 'lens_xy_yConfig', forAccessor: 'yAccessorId', axisMode: 'right' }], + yConfig: [{ type: 'yConfig', forAccessor: 'yAccessorId', axisMode: 'right' }], }, ], false, @@ -296,7 +296,7 @@ describe('axes_configuration', () => { { ...sampleLayer, accessors: ['yAccessorId', 'yAccessorId3', 'yAccessorId4'], - yConfig: [{ type: 'lens_xy_yConfig', forAccessor: 'yAccessorId', axisMode: 'right' }], + yConfig: [{ type: 'yConfig', forAccessor: 'yAccessorId', axisMode: 'right' }], }, ], false, @@ -320,7 +320,7 @@ describe('axes_configuration', () => { { ...sampleLayer, accessors: ['yAccessorId', 'yAccessorId3', 'yAccessorId4'], - yConfig: [{ type: 'lens_xy_yConfig', forAccessor: 'yAccessorId', axisMode: 'right' }], + yConfig: [{ type: 'yConfig', forAccessor: 'yAccessorId', axisMode: 'right' }], }, ], false, diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.test.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.test.ts index df763f5d5dd66..bd13e3217c2af 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.test.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.test.ts @@ -14,7 +14,7 @@ import { LayerTypes } from '../../common/constants'; describe('color_assignment', () => { const layers: DataLayerConfigResult[] = [ { - type: 'lens_xy_data_layer', + type: 'dataLayer', yScaleType: 'linear', xScaleType: 'linear', isHistogram: true, @@ -26,7 +26,7 @@ describe('color_assignment', () => { accessors: ['y1', 'y2'], }, { - type: 'lens_xy_data_layer', + type: 'dataLayer', yScaleType: 'linear', xScaleType: 'linear', isHistogram: true, 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 404a40832fc2f..6afd210ba6645 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 @@ -137,6 +137,7 @@ export function LayerPanel( activeVisualization, ] ); + const isEmptyLayer = !groups.some((d) => d.accessors.length > 0); const { activeId, activeGroup } = activeDimension; @@ -207,6 +208,7 @@ export function LayerPanel( previousColumn = typeof dropResult === 'object' ? dropResult.deleted : undefined; } } + const newVisState = setDimension({ columnId, groupId, @@ -445,7 +447,6 @@ export function LayerPanel( {group.accessors.map((accessorConfig, accessorIndex) => { const { columnId } = accessorConfig; - return ( { }; const sampleLayer: DataLayerConfigResult = { - type: 'lens_xy_data_layer', + type: 'dataLayer', layerId: 'first', layerType: layerTypes.DATA, seriesType: 'line', diff --git a/x-pack/plugins/lens/public/xy_visualization/color_assignment.ts b/x-pack/plugins/lens/public/xy_visualization/color_assignment.ts index 7cdc9a1ec6f48..c6b0d1285b56a 100644 --- a/x-pack/plugins/lens/public/xy_visualization/color_assignment.ts +++ b/x-pack/plugins/lens/public/xy_visualization/color_assignment.ts @@ -127,6 +127,7 @@ export function getAccessorColorConfig( triggerIcon: 'disabled', }; } + const columnToLabel = getColumnToLabelMap(layer, frame.datasourceLayers[dataLayer.layerId]); const rank = colorAssignments[currentPalette.name].getRank( layer, diff --git a/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.test.ts b/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.test.ts index d847e6f1afdc1..4448d14576f5a 100644 --- a/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.test.ts @@ -277,7 +277,7 @@ describe('reference_line helpers', () => { layerType: 'data', xAccessor: 'a', accessors: [], - type: 'lens_xy_data_layer', + type: 'dataLayer', yScaleType: 'linear', xScaleType: 'linear', isHistogram: false, @@ -305,7 +305,7 @@ describe('reference_line helpers', () => { layerType: 'data', xAccessor: 'a', accessors: [], - type: 'lens_xy_data_layer', + type: 'dataLayer', yScaleType: 'linear', xScaleType: 'linear', isHistogram: false, diff --git a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts index d519e2b675946..5292bc458ab33 100644 --- a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts +++ b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts @@ -17,7 +17,6 @@ import type { XYReferenceLineLayerConfig, YConfig, } from '../../../../../src/plugins/chart_expressions/expression_xy/common'; -import { layerTypes } from '../../common'; import { hasIcon } from './xy_config_panel/shared/icon_select'; import { defaultReferenceLineColor } from './color_assignment'; import { getDefaultVisualValuesForLayer } from '../shared_components/datasource_default_values'; @@ -147,7 +146,7 @@ export const buildExpression = ( chain: [ { type: 'function', - function: 'lens_xy_chart', + function: 'xyVis', arguments: { title: [attributes?.title || ''], description: [attributes?.description || ''], @@ -160,7 +159,7 @@ export const buildExpression = ( chain: [ { type: 'function', - function: 'lens_xy_legendConfig', + function: 'legendConfig', arguments: { isVisible: [state.legend.isVisible], showSingleSeries: state.legend.showSingleSeries @@ -199,7 +198,7 @@ export const buildExpression = ( chain: [ { type: 'function', - function: 'lens_xy_axisExtentConfig', + function: 'axisExtentConfig', arguments: { mode: [state?.yLeftExtent?.mode || 'full'], lowerBound: @@ -221,7 +220,7 @@ export const buildExpression = ( chain: [ { type: 'function', - function: 'lens_xy_axisExtentConfig', + function: 'axisExtentConfig', arguments: { mode: [state?.yRightExtent?.mode || 'full'], lowerBound: @@ -243,7 +242,7 @@ export const buildExpression = ( chain: [ { type: 'function', - function: 'lens_xy_axisTitlesVisibilityConfig', + function: 'axisTitlesVisibilityConfig', arguments: { x: [state?.axisTitlesVisibilitySettings?.x ?? true], yLeft: [state?.axisTitlesVisibilitySettings?.yLeft ?? true], @@ -259,7 +258,7 @@ export const buildExpression = ( chain: [ { type: 'function', - function: 'lens_xy_tickLabelsConfig', + function: 'tickLabelsConfig', arguments: { x: [state?.tickLabelsVisibilitySettings?.x ?? true], yLeft: [state?.tickLabelsVisibilitySettings?.yLeft ?? true], @@ -275,7 +274,7 @@ export const buildExpression = ( chain: [ { type: 'function', - function: 'lens_xy_gridlinesConfig', + function: 'gridlinesConfig', arguments: { x: [state?.gridlinesVisibilitySettings?.x ?? true], yLeft: [state?.gridlinesVisibilitySettings?.yLeft ?? true], @@ -291,7 +290,7 @@ export const buildExpression = ( chain: [ { type: 'function', - function: 'lens_xy_labelsOrientationConfig', + function: 'labelsOrientationConfig', arguments: { x: [state?.labelsOrientation?.x ?? 0], yLeft: [state?.labelsOrientation?.yLeft ?? 0], @@ -333,7 +332,7 @@ const referenceLineLayerToExpression = ( chain: [ { type: 'function', - function: 'lens_xy_referenceLine_layer', + function: 'referenceLineLayer', arguments: { layerId: [layer.layerId], yConfig: layer.yConfig @@ -341,7 +340,6 @@ const referenceLineLayerToExpression = ( yConfigToExpression(yConfig, defaultReferenceLineColor) ) : [], - layerType: [layerTypes.REFERENCELINE], accessors: layer.accessors, columnToLabel: [JSON.stringify(getColumnToLabelMap(layer, datasourceLayer))], }, @@ -372,7 +370,7 @@ const dataLayerToExpression = ( chain: [ { type: 'function', - function: 'lens_xy_data_layer', + function: 'dataLayer', arguments: { layerId: [layer.layerId], hide: [Boolean(layer.hide)], @@ -387,7 +385,6 @@ const dataLayerToExpression = ( ? layer.yConfig.map((yConfig) => yConfigToExpression(yConfig)) : [], seriesType: [layer.seriesType], - layerType: [layerTypes.DATA], accessors: layer.accessors, columnToLabel: [JSON.stringify(columnToLabel)], ...(layer.palette @@ -425,7 +422,7 @@ const yConfigToExpression = (yConfig: YConfig, defaultColor?: string): Ast => { chain: [ { type: 'function', - function: 'lens_xy_yConfig', + function: 'yConfig', arguments: { forAccessor: [yConfig.forAccessor], axisMode: yConfig.axisMode ? [yConfig.axisMode] : [], 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 a051c24742694..e882c324397e8 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 @@ -200,10 +200,10 @@ describe('Lens migrations', () => { } | lens_rename_columns idMap="{\\"col-0-1d9cc16c-1460-41de-88f8-471932ecbc97\\":{\\"label\\":\\"products.created_on\\",\\"dataType\\":\\"date\\",\\"operationType\\":\\"date_histogram\\",\\"sourceField\\":\\"products.created_on\\",\\"isBucketed\\":true,\\"scale\\":\\"interval\\",\\"params\\":{\\"interval\\":\\"auto\\"},\\"id\\":\\"1d9cc16c-1460-41de-88f8-471932ecbc97\\"},\\"col-1-66115819-8481-4917-a6dc-8ffb10dd02df\\":{\\"label\\":\\"Count of records\\",\\"dataType\\":\\"number\\",\\"operationType\\":\\"count\\",\\"suggestedPriority\\":0,\\"isBucketed\\":false,\\"scale\\":\\"ratio\\",\\"sourceField\\":\\"Records\\",\\"id\\":\\"66115819-8481-4917-a6dc-8ffb10dd02df\\"}}" } - | lens_xy_chart + | xyVis xTitle="products.created_on" yTitle="Count of records" - legend={lens_xy_legendConfig isVisible=true position="right"} + legend={legendConfig isVisible=true position="right"} layers={lens_xy_layer layerId="bd09dc71-a7e2-42d0-83bd-85df8291f03c" hide=false @@ -293,7 +293,7 @@ describe('Lens migrations', () => { | kibana_context query="{\\"query\\":\\"\\",\\"language\\":\\"kuery\\"}" filters="[]" | lens_merge_tables layerIds="bd09dc71-a7e2-42d0-83bd-85df8291f03c" tables={esaggs index="ff959d40-b880-11e8-a6d9-e546fe2bba5f" metricsAtAllLevels=false partialRows=false includeFormatHints=true aggConfigs="[{\\"id\\":\\"1d9cc16c-1460-41de-88f8-471932ecbc97\\",\\"enabled\\":true,\\"type\\":\\"date_histogram\\",\\"schema\\":\\"segment\\",\\"params\\":{\\"field\\":\\"products.created_on\\",\\"useNormalizedEsInterval\\":true,\\"interval\\":\\"auto\\",\\"drop_partials\\":false,\\"min_doc_count\\":0,\\"extended_bounds\\":{}}},{\\"id\\":\\"66115819-8481-4917-a6dc-8ffb10dd02df\\",\\"enabled\\":true,\\"type\\":\\"count\\",\\"schema\\":\\"metric\\",\\"params\\":{}}]" timeFields=\"products.created_on\"} -| lens_xy_chart xTitle="products.created_on" yTitle="Count of records" legend={lens_xy_legendConfig isVisible=true position="right"} layers={}`, +| xyVis xTitle="products.created_on" yTitle="Count of records" legend={legendConfig isVisible=true position="right"} layers={}`, }, }; const result = migrations['7.8.0'](input, context); @@ -309,7 +309,7 @@ describe('Lens migrations', () => { attributes: { description: '', expression: - 'kibana\n| kibana_context query="{\\"query\\":\\"NOT bytes > 5000\\",\\"language\\":\\"kuery\\"}" \n filters="[{\\"meta\\":{\\"index\\":\\"90943e30-9a47-11e8-b64d-95841ca0b247\\",\\"alias\\":null,\\"negate\\":true,\\"disabled\\":false,\\"type\\":\\"phrase\\",\\"key\\":\\"geo.src\\",\\"params\\":{\\"query\\":\\"CN\\"}},\\"query\\":{\\"match_phrase\\":{\\"geo.src\\":\\"CN\\"}},\\"$state\\":{\\"store\\":\\"appState\\"}},{\\"meta\\":{\\"index\\":\\"ff959d40-b880-11e8-a6d9-e546fe2bba5f\\",\\"alias\\":null,\\"negate\\":true,\\"disabled\\":false,\\"type\\":\\"phrase\\",\\"key\\":\\"geoip.country_iso_code\\",\\"params\\":{\\"query\\":\\"US\\"}},\\"query\\":{\\"match_phrase\\":{\\"geoip.country_iso_code\\":\\"US\\"}},\\"$state\\":{\\"store\\":\\"appState\\"}}]"\n| lens_merge_tables layerIds="9a27f85d-35a9-4246-81b2-48e7ee9b0707"\n layerIds="3b7791e9-326e-40d5-a787-b7594e48d906" \n tables={esaggs index="90943e30-9a47-11e8-b64d-95841ca0b247" metricsAtAllLevels=true partialRows=true includeFormatHints=true aggConfigs="[{\\"id\\":\\"96352896-c508-4fca-90d8-66e9ebfce621\\",\\"enabled\\":true,\\"type\\":\\"terms\\",\\"schema\\":\\"segment\\",\\"params\\":{\\"field\\":\\"geo.src\\",\\"orderBy\\":\\"4ce9b4c7-2ebf-4d48-8669-0ea69d973353\\",\\"order\\":\\"desc\\",\\"size\\":5,\\"otherBucket\\":false,\\"otherBucketLabel\\":\\"Other\\",\\"missingBucket\\":false,\\"missingBucketLabel\\":\\"Missing\\"}},{\\"id\\":\\"4ce9b4c7-2ebf-4d48-8669-0ea69d973353\\",\\"enabled\\":true,\\"type\\":\\"count\\",\\"schema\\":\\"metric\\",\\"params\\":{}}]" | lens_rename_columns idMap="{\\"col-0-96352896-c508-4fca-90d8-66e9ebfce621\\":{\\"label\\":\\"Top values of geo.src\\",\\"dataType\\":\\"string\\",\\"operationType\\":\\"terms\\",\\"scale\\":\\"ordinal\\",\\"sourceField\\":\\"geo.src\\",\\"isBucketed\\":true,\\"params\\":{\\"size\\":5,\\"orderBy\\":{\\"type\\":\\"column\\",\\"columnId\\":\\"4ce9b4c7-2ebf-4d48-8669-0ea69d973353\\"},\\"orderDirection\\":\\"desc\\"},\\"id\\":\\"96352896-c508-4fca-90d8-66e9ebfce621\\"},\\"col-1-4ce9b4c7-2ebf-4d48-8669-0ea69d973353\\":{\\"label\\":\\"Count of records\\",\\"dataType\\":\\"number\\",\\"operationType\\":\\"count\\",\\"isBucketed\\":false,\\"scale\\":\\"ratio\\",\\"sourceField\\":\\"Records\\",\\"id\\":\\"4ce9b4c7-2ebf-4d48-8669-0ea69d973353\\"}}"}\n tables={esaggs index="ff959d40-b880-11e8-a6d9-e546fe2bba5f" metricsAtAllLevels=true partialRows=true includeFormatHints=true aggConfigs="[{\\"id\\":\\"77d8383e-f66e-471e-ae50-c427feedb5ba\\",\\"enabled\\":true,\\"type\\":\\"terms\\",\\"schema\\":\\"segment\\",\\"params\\":{\\"field\\":\\"geoip.country_iso_code\\",\\"orderBy\\":\\"a5c1b82d-51de-4448-a99d-6391432c3a03\\",\\"order\\":\\"desc\\",\\"size\\":5,\\"otherBucket\\":false,\\"otherBucketLabel\\":\\"Other\\",\\"missingBucket\\":false,\\"missingBucketLabel\\":\\"Missing\\"}},{\\"id\\":\\"a5c1b82d-51de-4448-a99d-6391432c3a03\\",\\"enabled\\":true,\\"type\\":\\"count\\",\\"schema\\":\\"metric\\",\\"params\\":{}}]" | lens_rename_columns idMap="{\\"col-0-77d8383e-f66e-471e-ae50-c427feedb5ba\\":{\\"label\\":\\"Top values of geoip.country_iso_code\\",\\"dataType\\":\\"string\\",\\"operationType\\":\\"terms\\",\\"scale\\":\\"ordinal\\",\\"sourceField\\":\\"geoip.country_iso_code\\",\\"isBucketed\\":true,\\"params\\":{\\"size\\":5,\\"orderBy\\":{\\"type\\":\\"column\\",\\"columnId\\":\\"a5c1b82d-51de-4448-a99d-6391432c3a03\\"},\\"orderDirection\\":\\"desc\\"},\\"id\\":\\"77d8383e-f66e-471e-ae50-c427feedb5ba\\"},\\"col-1-a5c1b82d-51de-4448-a99d-6391432c3a03\\":{\\"label\\":\\"Count of records\\",\\"dataType\\":\\"number\\",\\"operationType\\":\\"count\\",\\"isBucketed\\":false,\\"scale\\":\\"ratio\\",\\"sourceField\\":\\"Records\\",\\"id\\":\\"a5c1b82d-51de-4448-a99d-6391432c3a03\\"}}"}\n| lens_xy_chart xTitle="Top values of geo.src" yTitle="Count of records" legend={lens_xy_legendConfig isVisible=true position="right"} fittingFunction="None" \n layers={lens_xy_layer layerId="9a27f85d-35a9-4246-81b2-48e7ee9b0707" hide=false xAccessor="96352896-c508-4fca-90d8-66e9ebfce621" yScaleType="linear" xScaleType="ordinal" isHistogram=false seriesType="bar" accessors="4ce9b4c7-2ebf-4d48-8669-0ea69d973353" columnToLabel="{\\"4ce9b4c7-2ebf-4d48-8669-0ea69d973353\\":\\"Count of records\\"}"}\n layers={lens_xy_layer layerId="3b7791e9-326e-40d5-a787-b7594e48d906" hide=false xAccessor="77d8383e-f66e-471e-ae50-c427feedb5ba" yScaleType="linear" xScaleType="ordinal" isHistogram=false seriesType="bar" accessors="a5c1b82d-51de-4448-a99d-6391432c3a03" columnToLabel="{\\"a5c1b82d-51de-4448-a99d-6391432c3a03\\":\\"Count of records [1]\\"}"}', + 'kibana\n| kibana_context query="{\\"query\\":\\"NOT bytes > 5000\\",\\"language\\":\\"kuery\\"}" \n filters="[{\\"meta\\":{\\"index\\":\\"90943e30-9a47-11e8-b64d-95841ca0b247\\",\\"alias\\":null,\\"negate\\":true,\\"disabled\\":false,\\"type\\":\\"phrase\\",\\"key\\":\\"geo.src\\",\\"params\\":{\\"query\\":\\"CN\\"}},\\"query\\":{\\"match_phrase\\":{\\"geo.src\\":\\"CN\\"}},\\"$state\\":{\\"store\\":\\"appState\\"}},{\\"meta\\":{\\"index\\":\\"ff959d40-b880-11e8-a6d9-e546fe2bba5f\\",\\"alias\\":null,\\"negate\\":true,\\"disabled\\":false,\\"type\\":\\"phrase\\",\\"key\\":\\"geoip.country_iso_code\\",\\"params\\":{\\"query\\":\\"US\\"}},\\"query\\":{\\"match_phrase\\":{\\"geoip.country_iso_code\\":\\"US\\"}},\\"$state\\":{\\"store\\":\\"appState\\"}}]"\n| lens_merge_tables layerIds="9a27f85d-35a9-4246-81b2-48e7ee9b0707"\n layerIds="3b7791e9-326e-40d5-a787-b7594e48d906" \n tables={esaggs index="90943e30-9a47-11e8-b64d-95841ca0b247" metricsAtAllLevels=true partialRows=true includeFormatHints=true aggConfigs="[{\\"id\\":\\"96352896-c508-4fca-90d8-66e9ebfce621\\",\\"enabled\\":true,\\"type\\":\\"terms\\",\\"schema\\":\\"segment\\",\\"params\\":{\\"field\\":\\"geo.src\\",\\"orderBy\\":\\"4ce9b4c7-2ebf-4d48-8669-0ea69d973353\\",\\"order\\":\\"desc\\",\\"size\\":5,\\"otherBucket\\":false,\\"otherBucketLabel\\":\\"Other\\",\\"missingBucket\\":false,\\"missingBucketLabel\\":\\"Missing\\"}},{\\"id\\":\\"4ce9b4c7-2ebf-4d48-8669-0ea69d973353\\",\\"enabled\\":true,\\"type\\":\\"count\\",\\"schema\\":\\"metric\\",\\"params\\":{}}]" | lens_rename_columns idMap="{\\"col-0-96352896-c508-4fca-90d8-66e9ebfce621\\":{\\"label\\":\\"Top values of geo.src\\",\\"dataType\\":\\"string\\",\\"operationType\\":\\"terms\\",\\"scale\\":\\"ordinal\\",\\"sourceField\\":\\"geo.src\\",\\"isBucketed\\":true,\\"params\\":{\\"size\\":5,\\"orderBy\\":{\\"type\\":\\"column\\",\\"columnId\\":\\"4ce9b4c7-2ebf-4d48-8669-0ea69d973353\\"},\\"orderDirection\\":\\"desc\\"},\\"id\\":\\"96352896-c508-4fca-90d8-66e9ebfce621\\"},\\"col-1-4ce9b4c7-2ebf-4d48-8669-0ea69d973353\\":{\\"label\\":\\"Count of records\\",\\"dataType\\":\\"number\\",\\"operationType\\":\\"count\\",\\"isBucketed\\":false,\\"scale\\":\\"ratio\\",\\"sourceField\\":\\"Records\\",\\"id\\":\\"4ce9b4c7-2ebf-4d48-8669-0ea69d973353\\"}}"}\n tables={esaggs index="ff959d40-b880-11e8-a6d9-e546fe2bba5f" metricsAtAllLevels=true partialRows=true includeFormatHints=true aggConfigs="[{\\"id\\":\\"77d8383e-f66e-471e-ae50-c427feedb5ba\\",\\"enabled\\":true,\\"type\\":\\"terms\\",\\"schema\\":\\"segment\\",\\"params\\":{\\"field\\":\\"geoip.country_iso_code\\",\\"orderBy\\":\\"a5c1b82d-51de-4448-a99d-6391432c3a03\\",\\"order\\":\\"desc\\",\\"size\\":5,\\"otherBucket\\":false,\\"otherBucketLabel\\":\\"Other\\",\\"missingBucket\\":false,\\"missingBucketLabel\\":\\"Missing\\"}},{\\"id\\":\\"a5c1b82d-51de-4448-a99d-6391432c3a03\\",\\"enabled\\":true,\\"type\\":\\"count\\",\\"schema\\":\\"metric\\",\\"params\\":{}}]" | lens_rename_columns idMap="{\\"col-0-77d8383e-f66e-471e-ae50-c427feedb5ba\\":{\\"label\\":\\"Top values of geoip.country_iso_code\\",\\"dataType\\":\\"string\\",\\"operationType\\":\\"terms\\",\\"scale\\":\\"ordinal\\",\\"sourceField\\":\\"geoip.country_iso_code\\",\\"isBucketed\\":true,\\"params\\":{\\"size\\":5,\\"orderBy\\":{\\"type\\":\\"column\\",\\"columnId\\":\\"a5c1b82d-51de-4448-a99d-6391432c3a03\\"},\\"orderDirection\\":\\"desc\\"},\\"id\\":\\"77d8383e-f66e-471e-ae50-c427feedb5ba\\"},\\"col-1-a5c1b82d-51de-4448-a99d-6391432c3a03\\":{\\"label\\":\\"Count of records\\",\\"dataType\\":\\"number\\",\\"operationType\\":\\"count\\",\\"isBucketed\\":false,\\"scale\\":\\"ratio\\",\\"sourceField\\":\\"Records\\",\\"id\\":\\"a5c1b82d-51de-4448-a99d-6391432c3a03\\"}}"}\n| xyVis xTitle="Top values of geo.src" yTitle="Count of records" legend={legendConfig isVisible=true position="right"} fittingFunction="None" \n layers={lens_xy_layer layerId="9a27f85d-35a9-4246-81b2-48e7ee9b0707" hide=false xAccessor="96352896-c508-4fca-90d8-66e9ebfce621" yScaleType="linear" xScaleType="ordinal" isHistogram=false seriesType="bar" accessors="4ce9b4c7-2ebf-4d48-8669-0ea69d973353" columnToLabel="{\\"4ce9b4c7-2ebf-4d48-8669-0ea69d973353\\":\\"Count of records\\"}"}\n layers={lens_xy_layer layerId="3b7791e9-326e-40d5-a787-b7594e48d906" hide=false xAccessor="77d8383e-f66e-471e-ae50-c427feedb5ba" yScaleType="linear" xScaleType="ordinal" isHistogram=false seriesType="bar" accessors="a5c1b82d-51de-4448-a99d-6391432c3a03" columnToLabel="{\\"a5c1b82d-51de-4448-a99d-6391432c3a03\\":\\"Count of records [1]\\"}"}', state: { datasourceMetaData: { filterableIndexPatterns: [ From 35cab9103ac2ee331f807402a0b5b836c6b9e37c Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Mon, 14 Mar 2022 21:19:18 +0200 Subject: [PATCH 013/133] Fixed bugs, caused by the refactoring process. --- .../reference_line_helpers.tsx | 20 +++++++++---------- .../lens/public/xy_visualization/types.ts | 14 +++++++++++-- .../public/xy_visualization/visualization.tsx | 13 +++++------- .../public/xy_visualization/xy_suggestions.ts | 10 ++-------- 4 files changed, 28 insertions(+), 29 deletions(-) diff --git a/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx b/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx index f987f4a0c8828..cb6db4d5024ee 100644 --- a/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx @@ -46,6 +46,7 @@ export function getGroupsToShow groupsAvailable[label] || config?.length) .map((layer) => ({ ...layer, valid: groupsAvailable[layer.label] })); @@ -345,19 +346,16 @@ export const setReferenceDimension: Visualization['setDimension'] = ({ newLayer.yConfig = [ ...(newLayer.yConfig || []), - ...(previousYConfig - ? [ - { - // override with previous styling, - ...previousYConfig, - // but keep the new group & id config - forAccessor: columnId, - axisMode, - }, - ] - : []), + { + // override with previous styling, + ...previousYConfig, + // but keep the new group & id config + forAccessor: columnId, + axisMode, + }, ]; } + return { ...prevState, layers: prevState.layers.map((l) => (l.layerId === layerId ? newLayer : l)), diff --git a/x-pack/plugins/lens/public/xy_visualization/types.ts b/x-pack/plugins/lens/public/xy_visualization/types.ts index a2d2d2ad1208e..ed24766025a63 100644 --- a/x-pack/plugins/lens/public/xy_visualization/types.ts +++ b/x-pack/plugins/lens/public/xy_visualization/types.ts @@ -30,17 +30,27 @@ import type { LayerType, ReferenceLineLayerArgs, YConfig, + XScaleType, + YScaleType, } from '../../../../../src/plugins/chart_expressions/expression_xy/common'; +import { PaletteOutput } from '../../../../../src/plugins/charts/common'; import type { ValueLabelConfig } from '../../common/types'; -export interface XYDataLayerConfig extends Omit { +export interface XYDataLayerConfig + extends Omit { layerType: LayerType; yConfig?: YConfig[]; + palette?: PaletteOutput; + yScaleType?: YScaleType; + xScaleType?: XScaleType; + isHistogram?: boolean; } -export interface XYReferenceLineLayerConfig extends Omit { +export interface XYReferenceLineLayerConfig + extends Omit { layerType: LayerType; yConfig?: YConfig[]; + palette?: PaletteOutput; } export type XYLayerConfig = XYDataLayerConfig | XYReferenceLineLayerConfig; diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx index 7cf8e055907f1..4c73144d296d3 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx @@ -141,11 +141,6 @@ export const getXyVisualization = ({ seriesType: defaultSeriesType, showGridlines: false, layerType: layerTypes.DATA, - type: 'lens_xy_data_layer', - xScaleType: 'linear', - yScaleType: 'linear', - isHistogram: false, - palette: { name: 'default', type: 'palette' }, }, ], } @@ -273,7 +268,9 @@ export const getXyVisualization = ({ setDimension(props) { const { prevState, layerId, columnId, groupId } = props; - const foundLayer = prevState.layers.find((l) => l.layerId === layerId); + const foundLayer: XYLayerConfig | undefined = prevState.layers.find( + (l) => l.layerId === layerId + ); if (!foundLayer) { return prevState; } @@ -282,7 +279,7 @@ export const getXyVisualization = ({ return setReferenceDimension(props); } - const newLayer: XYDataLayerConfig = Object.assign(foundLayer); + const newLayer: XYDataLayerConfig = Object.assign({}, foundLayer); if (groupId === 'x') { newLayer.xAccessor = columnId; } @@ -391,7 +388,7 @@ export const getXyVisualization = ({ } else if (newLayer.splitAccessor === columnId) { delete newLayer.splitAccessor; // as the palette is associated with the break down by dimension, remove it together with the dimension - newLayer.palette = { type: 'palette', name: '' }; + delete newLayer.palette; } } if (newLayer.accessors.includes(columnId)) { diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.ts b/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.ts index db0f1979d519d..4e391d862495c 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.ts +++ b/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.ts @@ -17,10 +17,7 @@ import { TableChangeType, } from '../types'; import { State, XYState, visualizationTypes, XYLayerConfig, XYDataLayerConfig } from './types'; -import type { - DataLayerConfigResult, - SeriesType, -} from '../../../../../src/plugins/chart_expressions/expression_xy/common'; +import type { SeriesType } from '../../../../../src/plugins/chart_expressions/expression_xy/common'; import { layerTypes } from '../../common'; import { getIconForSeries } from './state_helpers'; import { getDataLayers, isDataLayer } from './visualization_helpers'; @@ -510,7 +507,7 @@ function buildSuggestion({ mainPalette || (existingLayer && 'palette' in existingLayer ? (existingLayer as XYDataLayerConfig).palette - : { type: 'palette', name: '' }), + : undefined), layerId, seriesType, xAccessor: xValue?.columnId, @@ -521,9 +518,6 @@ function buildSuggestion({ ? existingLayer.yConfig.filter(({ forAccessor }) => accessors.indexOf(forAccessor) !== -1) : undefined, layerType: layerTypes.DATA, - xScaleType: (existingLayer as DataLayerConfigResult).xScaleType ?? 'linear', - yScaleType: (existingLayer as DataLayerConfigResult).yScaleType ?? 'linear', - isHistogram: (existingLayer as DataLayerConfigResult).isHistogram ?? false, }; // Maintain consistent order for any layers that were saved From ed7f8eaa6d891c0407a8d43dfd65f3e3d39a7eb4 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Tue, 15 Mar 2022 15:11:40 +0200 Subject: [PATCH 014/133] Fixed lens snapshots. --- .../__snapshots__/expression.test.tsx.snap | 1705 ----------------- .../__snapshots__/to_expression.test.ts.snap | 7 +- .../xy_visualization/to_expression.test.ts | 44 - .../xy_visualization/visualization.test.ts | 117 +- 4 files changed, 3 insertions(+), 1870 deletions(-) delete mode 100644 x-pack/plugins/lens/public/xy_visualization/__snapshots__/expression.test.tsx.snap diff --git a/x-pack/plugins/lens/public/xy_visualization/__snapshots__/expression.test.tsx.snap b/x-pack/plugins/lens/public/xy_visualization/__snapshots__/expression.test.tsx.snap deleted file mode 100644 index b34d5e8639382..0000000000000 --- a/x-pack/plugins/lens/public/xy_visualization/__snapshots__/expression.test.tsx.snap +++ /dev/null @@ -1,1705 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`xy_expression XYChart component it renders area 1`] = ` - - - - - - - - -`; - -exports[`xy_expression XYChart component it renders bar 1`] = ` - - - - - - - - -`; - -exports[`xy_expression XYChart component it renders horizontal bar 1`] = ` - - - - - - - - -`; - -exports[`xy_expression XYChart component it renders line 1`] = ` - - - - - - - - -`; - -exports[`xy_expression XYChart component it renders stacked area 1`] = ` - - - - - - - - -`; - -exports[`xy_expression XYChart component it renders stacked bar 1`] = ` - - - - - - - - -`; - -exports[`xy_expression XYChart component it renders stacked horizontal bar 1`] = ` - - - - - - - - -`; diff --git a/x-pack/plugins/lens/public/xy_visualization/__snapshots__/to_expression.test.ts.snap b/x-pack/plugins/lens/public/xy_visualization/__snapshots__/to_expression.test.ts.snap index acda343ca8888..ebbc489a2d51c 100644 --- a/x-pack/plugins/lens/public/xy_visualization/__snapshots__/to_expression.test.ts.snap +++ b/x-pack/plugins/lens/public/xy_visualization/__snapshots__/to_expression.test.ts.snap @@ -107,9 +107,6 @@ Object { "layerId": Array [ "first", ], - "layerType": Array [ - "data", - ], "seriesType": Array [ "area", ], @@ -208,7 +205,7 @@ Object { ], "upperBound": Array [], }, - "function": "axisExtent", + "function": "axisExtentConfig", "type": "function", }, ], @@ -230,7 +227,7 @@ Object { 456, ], }, - "function": "axisExtent", + "function": "axisExtentConfig", "type": "function", }, ], diff --git a/x-pack/plugins/lens/public/xy_visualization/to_expression.test.ts b/x-pack/plugins/lens/public/xy_visualization/to_expression.test.ts index 0f04f47bc76fb..ac3fdcf30a4ad 100644 --- a/x-pack/plugins/lens/public/xy_visualization/to_expression.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/to_expression.test.ts @@ -75,10 +75,6 @@ describe('#toExpression', () => { splitAccessor: 'd', xAccessor: 'a', accessors: ['b', 'c'], - xScaleType: 'linear', - yScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, ], }, @@ -103,10 +99,6 @@ describe('#toExpression', () => { splitAccessor: 'd', xAccessor: 'a', accessors: ['b', 'c'], - xScaleType: 'linear', - yScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, ], }, @@ -130,10 +122,6 @@ describe('#toExpression', () => { splitAccessor: 'd', xAccessor: 'a', accessors: ['b', 'c'], - xScaleType: 'linear', - yScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, ], }, @@ -162,10 +150,6 @@ describe('#toExpression', () => { splitAccessor: undefined, xAccessor: undefined, accessors: ['a'], - xScaleType: 'linear', - yScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, ], }, @@ -191,10 +175,6 @@ describe('#toExpression', () => { splitAccessor: undefined, xAccessor: 'a', accessors: [], - xScaleType: 'linear', - yScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, ], }, @@ -217,10 +197,6 @@ describe('#toExpression', () => { splitAccessor: 'd', xAccessor: 'a', accessors: ['b', 'c'], - xScaleType: 'linear', - yScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, ], }, @@ -258,10 +234,6 @@ describe('#toExpression', () => { splitAccessor: 'd', xAccessor: 'a', accessors: ['b', 'c'], - xScaleType: 'linear', - yScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, ], }, @@ -290,10 +262,6 @@ describe('#toExpression', () => { splitAccessor: 'd', xAccessor: 'a', accessors: ['b', 'c'], - xScaleType: 'linear', - yScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, ], }, @@ -320,10 +288,6 @@ describe('#toExpression', () => { splitAccessor: 'd', xAccessor: 'a', accessors: ['b', 'c'], - xScaleType: 'linear', - yScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, ], }, @@ -352,10 +316,6 @@ describe('#toExpression', () => { splitAccessor: 'd', xAccessor: 'a', accessors: ['b', 'c'], - xScaleType: 'linear', - yScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, ], }, @@ -379,10 +339,6 @@ describe('#toExpression', () => { xAccessor: 'a', accessors: ['b', 'c'], yConfig: [{ forAccessor: 'a' }], - xScaleType: 'linear', - yScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, { layerId: 'referenceLine', diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts b/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts index e20412eb4ae6b..c1c67baef6d7f 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts @@ -35,10 +35,7 @@ function exampleState(): XYState { splitAccessor: 'd', xAccessor: 'a', accessors: ['b', 'c'], - xScaleType: 'linear', - yScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, + }, ], }; @@ -194,10 +191,6 @@ describe('xy_visualization', () => { splitAccessor: 'e', xAccessor: 'f', accessors: ['g', 'h'], - xScaleType: 'linear', - yScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, ], }; @@ -289,10 +282,6 @@ describe('xy_visualization', () => { seriesType: 'area', xAccessor: undefined, accessors: [], - xScaleType: 'linear', - yScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, ], }, @@ -322,10 +311,6 @@ describe('xy_visualization', () => { seriesType: 'area', xAccessor: 'a', accessors: [], - xScaleType: 'linear', - yScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, ], }, @@ -434,10 +419,6 @@ describe('xy_visualization', () => { seriesType: 'line', xAccessor: undefined, accessors: ['a'], - xScaleType: 'linear', - yScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, ], }, @@ -699,10 +680,6 @@ describe('xy_visualization', () => { seriesType: 'area', xAccessor: 'a', accessors: [], - xScaleType: 'linear', - yScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, ], }, @@ -1078,10 +1055,6 @@ describe('xy_visualization', () => { splitAccessor: undefined, xAccessor: undefined, accessors: ['a'], - xScaleType: 'linear', - yScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, { layerId: 'referenceLine', @@ -1577,10 +1550,6 @@ describe('xy_visualization', () => { seriesType: 'area', xAccessor: 'a', accessors: [], - xScaleType: 'linear', - yScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, ], }) @@ -1597,10 +1566,6 @@ describe('xy_visualization', () => { seriesType: 'area', xAccessor: 'a', accessors: [], - xScaleType: 'linear', - yScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, { layerId: 'second', @@ -1608,10 +1573,6 @@ describe('xy_visualization', () => { seriesType: 'area', xAccessor: 'a', accessors: [], - xScaleType: 'linear', - yScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, ], }) @@ -1628,10 +1589,6 @@ describe('xy_visualization', () => { seriesType: 'area', xAccessor: 'a', accessors: ['a'], - xScaleType: 'linear', - yScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, { layerId: 'second', @@ -1640,10 +1597,6 @@ describe('xy_visualization', () => { xAccessor: undefined, accessors: ['a'], splitAccessor: 'a', - xScaleType: 'linear', - yScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, ], }) @@ -1661,10 +1614,6 @@ describe('xy_visualization', () => { xAccessor: undefined, accessors: [], splitAccessor: 'a', - xScaleType: 'linear', - yScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, ], }) @@ -1681,10 +1630,6 @@ describe('xy_visualization', () => { xAccessor: undefined, accessors: [], splitAccessor: 'a', - xScaleType: 'linear', - yScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, { layerId: 'second', @@ -1693,10 +1638,6 @@ describe('xy_visualization', () => { xAccessor: undefined, accessors: [], splitAccessor: 'a', - xScaleType: 'linear', - yScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, ], }) @@ -1713,10 +1654,6 @@ describe('xy_visualization', () => { seriesType: 'area', xAccessor: 'a', accessors: [], - xScaleType: 'linear', - yScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, { layerId: 'second', @@ -1724,10 +1661,6 @@ describe('xy_visualization', () => { seriesType: 'area', xAccessor: undefined, accessors: ['a'], - xScaleType: 'linear', - yScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, ], }) @@ -1749,10 +1682,6 @@ describe('xy_visualization', () => { seriesType: 'area', xAccessor: 'a', accessors: ['a'], - xScaleType: 'linear', - yScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, { layerId: 'second', @@ -1761,10 +1690,6 @@ describe('xy_visualization', () => { xAccessor: undefined, accessors: [], splitAccessor: 'a', - xScaleType: 'linear', - yScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, { layerId: 'third', @@ -1773,10 +1698,6 @@ describe('xy_visualization', () => { xAccessor: undefined, accessors: [], splitAccessor: 'a', - xScaleType: 'linear', - yScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, ], }) @@ -1798,10 +1719,6 @@ describe('xy_visualization', () => { seriesType: 'area', xAccessor: 'a', accessors: [], - xScaleType: 'linear', - yScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, { layerId: 'second', @@ -1809,10 +1726,6 @@ describe('xy_visualization', () => { seriesType: 'area', xAccessor: 'a', accessors: ['a'], - xScaleType: 'linear', - yScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, { layerId: 'third', @@ -1820,10 +1733,6 @@ describe('xy_visualization', () => { seriesType: 'area', xAccessor: 'a', accessors: ['a'], - xScaleType: 'linear', - yScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, ], }) @@ -1848,10 +1757,6 @@ describe('xy_visualization', () => { splitAccessor: 'd', xAccessor: 'a', accessors: ['b'], // just use a single accessor to avoid too much noise - xScaleType: 'linear', - yScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, ], }, @@ -1900,10 +1805,6 @@ describe('xy_visualization', () => { splitAccessor: 'd', xAccessor: 'a', accessors: ['b'], - xScaleType: 'linear', - yScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, { layerId: 'second', @@ -1912,10 +1813,6 @@ describe('xy_visualization', () => { splitAccessor: 'd', xAccessor: 'e', accessors: ['b'], - xScaleType: 'linear', - yScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, ], }, @@ -1964,10 +1861,6 @@ describe('xy_visualization', () => { splitAccessor: 'd', xAccessor: 'a', accessors: ['b'], - xScaleType: 'linear', - yScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, { layerId: 'second', @@ -1976,10 +1869,6 @@ describe('xy_visualization', () => { splitAccessor: 'd', xAccessor: 'e', accessors: ['b'], - xScaleType: 'linear', - yScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, ], }, @@ -2043,10 +1932,6 @@ describe('xy_visualization', () => { seriesType: 'area', xAccessor: 'a', accessors: ['b'], - xScaleType: 'linear', - yScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, ], }, From b6daa5d9834a25c8bd2aa57fabfd0ef34918fac3 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Tue, 15 Mar 2022 15:12:11 +0200 Subject: [PATCH 015/133] Removed new line. --- .../plugins/lens/public/xy_visualization/visualization.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts b/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts index c1c67baef6d7f..730832db364dc 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts @@ -35,7 +35,6 @@ function exampleState(): XYState { splitAccessor: 'd', xAccessor: 'a', accessors: ['b', 'c'], - }, ], }; From c8d40fe700f41c0e8b62b97b668a9b5c84be59a2 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Tue, 15 Mar 2022 15:38:53 +0200 Subject: [PATCH 016/133] Fixed xy_chart tests. --- .../public/components/xy_chart.test.tsx | 16 ++-------------- .../expression_xy/public/components/xy_chart.tsx | 4 ++-- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx index 7abd103bf10d0..e99a85b33c1f0 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx @@ -1994,13 +1994,7 @@ describe('XYChart component', () => { { ...args.layers[0], xScaleType: 'ordinal', - seriesType: 'bar_stacked', - type: 'dataLayer', - layerType: LayerTypes.DATA, - yScaleType: 'linear', - isHistogram: false, - palette: mockPaletteOutput, - }, + } as DataLayerConfigResult, ], }} /> @@ -2022,13 +2016,7 @@ describe('XYChart component', () => { { ...args.layers[0], yScaleType: 'sqrt', - seriesType: 'bar_stacked', - type: 'dataLayer', - layerType: LayerTypes.DATA, - xScaleType: 'ordinal', - isHistogram: false, - palette: mockPaletteOutput, - }, + } as DataLayerConfigResult, ], }} /> diff --git a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx index 377f54e9f89e5..f85b9dc441820 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx @@ -7,8 +7,6 @@ */ import React, { useRef } from 'react'; -import { IconType } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; import { Chart, Settings, @@ -37,6 +35,8 @@ import { BarSeriesProps, LineSeriesProps, } from '@elastic/charts'; +import { IconType } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; import type { Datatable, DatatableRow, DatatableColumn } from '../../../../expressions/public'; import { RenderMode } from '../../../../expressions/common'; import { FieldFormat } from '../../../../field_formats/common'; From 5fed60c6a3ef96a3bec967fc12b32bf7dc18e866 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Tue, 15 Mar 2022 16:01:32 +0200 Subject: [PATCH 017/133] Added lazy loading for xy chart. --- .../expression_xy/kibana.json | 2 +- .../public/components/xy_chart.tsx | 22 ++++++++++--------- .../xy_chart_renderer.tsx | 9 +++++--- .../expression_xy/tsconfig.json | 1 + 4 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/kibana.json b/src/plugins/chart_expressions/expression_xy/kibana.json index 7f24173b071b1..58b1623e3ce31 100755 --- a/src/plugins/chart_expressions/expression_xy/kibana.json +++ b/src/plugins/chart_expressions/expression_xy/kibana.json @@ -9,7 +9,7 @@ "description": "Expression XY plugin adds a `xy` renderer and function to the expression plugin. The renderer will display the `xy` chart.", "server": true, "ui": true, - "requiredPlugins": ["expressions", "charts", "data", "fieldFormats", "uiActions"], + "requiredPlugins": ["expressions", "charts", "data", "fieldFormats", "uiActions", "presentationUtil"], "requiredBundles": ["kibanaReact"], "optionalPlugins": [] } diff --git a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx index f85b9dc441820..a41140e285631 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import React, { useRef } from 'react'; +import React, { memo, useRef } from 'react'; import { Chart, Settings, @@ -128,11 +128,7 @@ function getIconForSeriesType(seriesType: SeriesType): IconType { return visualizationDefinitions.find((c) => c.id === seriesType)!.icon || 'empty'; } -const MemoizedChart = React.memo(XYChart); - -export function XYChartReportable(props: XYChartRenderProps) { - return ; -} +export const XYChartReportable = React.memo(XYChart); export function XYChart({ data, @@ -165,10 +161,13 @@ export function XYChart({ const chartBaseTheme = chartsThemeService.useChartsBaseTheme(); const darkMode = chartsThemeService.useDarkMode(); const filteredLayers = getFilteredLayers(layers, data); - const layersById = filteredLayers.reduce>((memo, layer) => { - memo[layer.layerId] = layer; - return memo; - }, {}); + const layersById = filteredLayers.reduce>( + (hashMap, layer) => { + hashMap[layer.layerId] = layer; + return hashMap; + }, + {} + ); const handleCursorUpdate = useActiveCursor(chartsActiveCursorService, chartRef, { datatables: Object.values(data.tables), @@ -908,3 +907,6 @@ export function XYChart({ function assertNever(x: never): never { throw new Error('Unexpected series type: ' + x); } + +// eslint-disable-next-line import/no-default-export +export default memo(XYChart); diff --git a/src/plugins/chart_expressions/expression_xy/public/expression_renderers/xy_chart_renderer.tsx b/src/plugins/chart_expressions/expression_xy/public/expression_renderers/xy_chart_renderer.tsx index 3c4a8559d88a5..b37c9157ffede 100644 --- a/src/plugins/chart_expressions/expression_xy/public/expression_renderers/xy_chart_renderer.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/expression_renderers/xy_chart_renderer.tsx @@ -9,14 +9,14 @@ import { i18n } from '@kbn/i18n'; import { I18nProvider } from '@kbn/i18n-react'; import { ThemeServiceStart } from 'kibana/public'; -import React from 'react'; +import React, { lazy } from 'react'; import ReactDOM from 'react-dom'; import { ChartsPluginStart, PaletteRegistry } from '../../../../charts/public'; import { ExpressionRenderDefinition } from '../../../../expressions'; import { FormatFactory } from '../../../../field_formats/common'; import { KibanaThemeProvider } from '../../../../kibana_react/public'; +import { withSuspense } from '../../../../presentation_util/public'; import { XYChartProps } from '../../common'; -import { XYChartReportable } from '../components'; import { calculateMinInterval } from '../helpers'; import { BrushEvent, FilterEvent } from '../types'; @@ -34,6 +34,9 @@ interface XyChartRendererDeps { getStartDeps: GetStartDepsFn; } +const LazyXYChart = lazy(() => import('../components/xy_chart')); +const XYChartComponent = withSuspense(LazyXYChart); + export const getXyChartRenderer = ({ getStartDeps, }: XyChartRendererDeps): ExpressionRenderDefinition => ({ @@ -56,7 +59,7 @@ export const getXyChartRenderer = ({ ReactDOM.render( - Date: Tue, 15 Mar 2022 21:55:31 +0200 Subject: [PATCH 018/133] Fixed xy chart test. --- .../public/components/xy_chart.tsx | 736 +++++++++--------- .../test/functional/apps/lens/chart_data.ts | 4 +- 2 files changed, 371 insertions(+), 369 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx index a41140e285631..e4815736280ce 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx @@ -521,386 +521,388 @@ export function XYChart({ }; return ( - - safeXAccessorLabelRenderer(d.value), - }} - allowBrushingLastHistogramBin={isTimeViz} - rotation={shouldRotate ? 90 : 0} - xDomain={xDomain} - onBrushEnd={interactive ? (brushHandler as BrushEndListener) : undefined} - onElementClick={interactive ? clickHandler : undefined} - legendAction={ - interactive - ? getLegendAction( - filteredLayers, - data.tables, - onClickValue, - formatFactory, - layersAlreadyFormatted - ) - : undefined - } - showLegendExtra={isHistogramViz && valuesInLegend} - ariaLabel={args.ariaLabel} - ariaUseDefaultSummary={!args.ariaLabel} - /> - - safeXAccessorLabelRenderer(d)} - style={xAxisStyle} - timeAxisLayerCount={shouldUseNewTimeAxis ? 3 : 0} - /> - - {yAxesConfiguration.map((axis) => { - return ( - axis.formatter?.convert(d) || ''} - style={getYAxesStyle(axis.groupId as 'left' | 'right')} - domain={getYAxisDomain(axis)} - ticks={5} - /> - ); - })} - - {!hideEndzones && ( - - layer.isHistogram && - (layer.seriesType.includes('stacked') || !layer.splitAccessor) && - (layer.seriesType.includes('stacked') || - !layer.seriesType.includes('bar') || - !chartHasMoreThanOneBarSeries) - )} +
+ + safeXAccessorLabelRenderer(d.value), + }} + allowBrushingLastHistogramBin={isTimeViz} + rotation={shouldRotate ? 90 : 0} + xDomain={xDomain} + onBrushEnd={interactive ? (brushHandler as BrushEndListener) : undefined} + onElementClick={interactive ? clickHandler : undefined} + legendAction={ + interactive + ? getLegendAction( + filteredLayers, + data.tables, + onClickValue, + formatFactory, + layersAlreadyFormatted + ) + : undefined + } + showLegendExtra={isHistogramViz && valuesInLegend} + ariaLabel={args.ariaLabel} + ariaUseDefaultSummary={!args.ariaLabel} /> - )} - - {filteredLayers.flatMap((layer, layerIndex) => - layer.accessors.map((accessor, accessorIndex) => { - const { - splitAccessor, - seriesType, - accessors, - xAccessor, - layerId, - columnToLabel, - yScaleType, - xScaleType, - isHistogram, - palette, - } = layer; - const columnToLabelMap: Record = columnToLabel - ? JSON.parse(columnToLabel) - : {}; - const table = data.tables[layerId]; + safeXAccessorLabelRenderer(d)} + style={xAxisStyle} + timeAxisLayerCount={shouldUseNewTimeAxis ? 3 : 0} + /> - const formatterPerColumn = new Map(); - for (const column of table.columns) { - formatterPerColumn.set(column, formatFactory(column.meta.params)); - } + {yAxesConfiguration.map((axis) => { + return ( + axis.formatter?.convert(d) || ''} + style={getYAxesStyle(axis.groupId as 'left' | 'right')} + domain={getYAxisDomain(axis)} + ticks={5} + /> + ); + })} + + {!hideEndzones && ( + + layer.isHistogram && + (layer.seriesType.includes('stacked') || !layer.splitAccessor) && + (layer.seriesType.includes('stacked') || + !layer.seriesType.includes('bar') || + !chartHasMoreThanOneBarSeries) + )} + /> + )} + + {filteredLayers.flatMap((layer, layerIndex) => + layer.accessors.map((accessor, accessorIndex) => { + const { + splitAccessor, + seriesType, + accessors, + xAccessor, + layerId, + columnToLabel, + yScaleType, + xScaleType, + isHistogram, + palette, + } = layer; + const columnToLabelMap: Record = columnToLabel + ? JSON.parse(columnToLabel) + : {}; + + const table = data.tables[layerId]; + + const formatterPerColumn = new Map(); + for (const column of table.columns) { + formatterPerColumn.set(column, formatFactory(column.meta.params)); + } - // what if row values are not primitive? That is the case of, for instance, Ranges - // remaps them to their serialized version with the formatHint metadata - // In order to do it we need to make a copy of the table as the raw one is required for more features (filters, etc...) later on - const tableConverted: Datatable = { - ...table, - rows: table.rows.map((row: DatatableRow) => { - const newRow = { ...row }; - for (const column of table.columns) { - const record = newRow[column.id]; - if ( - record != null && - // pre-format values for ordinal x axes because there can only be a single x axis formatter on chart level - (!isPrimitive(record) || (column.id === xAccessor && xScaleType === 'ordinal')) - ) { - newRow[column.id] = formatterPerColumn.get(column)!.convert(record); + // what if row values are not primitive? That is the case of, for instance, Ranges + // remaps them to their serialized version with the formatHint metadata + // In order to do it we need to make a copy of the table as the raw one is required for more features (filters, etc...) later on + const tableConverted: Datatable = { + ...table, + rows: table.rows.map((row: DatatableRow) => { + const newRow = { ...row }; + for (const column of table.columns) { + const record = newRow[column.id]; + if ( + record != null && + // pre-format values for ordinal x axes because there can only be a single x axis formatter on chart level + (!isPrimitive(record) || (column.id === xAccessor && xScaleType === 'ordinal')) + ) { + newRow[column.id] = formatterPerColumn.get(column)!.convert(record); + } } - } - return newRow; - }), - }; - - // save the id of the layer with the custom table - table.columns.reduce>( - (alreadyFormatted: Record, { id }) => { - if (alreadyFormatted[id]) { + return newRow; + }), + }; + + // save the id of the layer with the custom table + table.columns.reduce>( + (alreadyFormatted: Record, { id }) => { + if (alreadyFormatted[id]) { + return alreadyFormatted; + } + alreadyFormatted[id] = table.rows.some( + (row, i) => row[id] !== tableConverted.rows[i][id] + ); return alreadyFormatted; - } - alreadyFormatted[id] = table.rows.some( - (row, i) => row[id] !== tableConverted.rows[i][id] - ); - return alreadyFormatted; - }, - layersAlreadyFormatted - ); - - const isStacked = seriesType.includes('stacked'); - const isPercentage = seriesType.includes('percentage'); - const isBarChart = seriesType.includes('bar'); - const enableHistogramMode = - isHistogram && - (isStacked || !splitAccessor) && - (isStacked || !isBarChart || !chartHasMoreThanOneBarSeries); - - // For date histogram chart type, we're getting the rows that represent intervals without data. - // To not display them in the legend, they need to be filtered out. - const rows = tableConverted.rows.filter( - (row) => - !(xAccessor && typeof row[xAccessor] === 'undefined') && - !( - splitAccessor && - typeof row[splitAccessor] === 'undefined' && - typeof row[accessor] === 'undefined' - ) - ); - - if (!xAccessor) { - rows.forEach((row) => { - row.unifiedX = i18n.translate('xpack.lens.xyChart.emptyXLabel', { - defaultMessage: '(empty)', + }, + layersAlreadyFormatted + ); + + const isStacked = seriesType.includes('stacked'); + const isPercentage = seriesType.includes('percentage'); + const isBarChart = seriesType.includes('bar'); + const enableHistogramMode = + isHistogram && + (isStacked || !splitAccessor) && + (isStacked || !isBarChart || !chartHasMoreThanOneBarSeries); + + // For date histogram chart type, we're getting the rows that represent intervals without data. + // To not display them in the legend, they need to be filtered out. + const rows = tableConverted.rows.filter( + (row) => + !(xAccessor && typeof row[xAccessor] === 'undefined') && + !( + splitAccessor && + typeof row[splitAccessor] === 'undefined' && + typeof row[accessor] === 'undefined' + ) + ); + + if (!xAccessor) { + rows.forEach((row) => { + row.unifiedX = i18n.translate('xpack.lens.xyChart.emptyXLabel', { + defaultMessage: '(empty)', + }); }); - }); - } - - const yAxis = yAxesConfiguration.find((axisConfiguration) => - axisConfiguration.series.find((currentSeries) => currentSeries.accessor === accessor) - ); + } - const formatter = table?.columns.find((column) => column.id === accessor)?.meta?.params; - const splitHint = table.columns.find((col) => col.id === splitAccessor)?.meta?.params; - const splitFormatter = formatFactory(splitHint); - - const seriesProps: SeriesSpec = { - splitSeriesAccessors: splitAccessor ? [splitAccessor] : [], - stackAccessors: isStacked ? [xAccessor as string] : [], - id: `${splitAccessor}-${accessor}`, - xAccessor: xAccessor || 'unifiedX', - yAccessors: [accessor], - data: rows, - xScaleType: xAccessor ? xScaleType : 'ordinal', - yScaleType: - formatter?.id === 'bytes' && yScaleType === ScaleType.Linear - ? ScaleType.LinearBinary - : yScaleType, - color: ({ yAccessor, seriesKeys }) => { - const overwriteColor = getSeriesColor(layer, accessor); - if (overwriteColor !== null) { - return overwriteColor; - } - const colorAssignment = colorAssignments[palette.name]; - const seriesLayers: SeriesLayer[] = [ - { - name: splitAccessor ? String(seriesKeys[0]) : columnToLabelMap[seriesKeys[0]], - totalSeriesAtDepth: colorAssignment.totalSeriesCount, - rankAtDepth: colorAssignment.getRank( - layer, - String(seriesKeys[0]), - String(yAccessor) - ), - }, - ]; - return paletteService.get(palette.name).getCategoricalColor( - seriesLayers, - { - maxDepth: 1, - behindText: false, - totalSeries: colorAssignment.totalSeriesCount, - syncColors, + const yAxis = yAxesConfiguration.find((axisConfiguration) => + axisConfiguration.series.find((currentSeries) => currentSeries.accessor === accessor) + ); + + const formatter = table?.columns.find((column) => column.id === accessor)?.meta?.params; + const splitHint = table.columns.find((col) => col.id === splitAccessor)?.meta?.params; + const splitFormatter = formatFactory(splitHint); + + const seriesProps: SeriesSpec = { + splitSeriesAccessors: splitAccessor ? [splitAccessor] : [], + stackAccessors: isStacked ? [xAccessor as string] : [], + id: `${splitAccessor}-${accessor}`, + xAccessor: xAccessor || 'unifiedX', + yAccessors: [accessor], + data: rows, + xScaleType: xAccessor ? xScaleType : 'ordinal', + yScaleType: + formatter?.id === 'bytes' && yScaleType === ScaleType.Linear + ? ScaleType.LinearBinary + : yScaleType, + color: ({ yAccessor, seriesKeys }) => { + const overwriteColor = getSeriesColor(layer, accessor); + if (overwriteColor !== null) { + return overwriteColor; + } + const colorAssignment = colorAssignments[palette.name]; + const seriesLayers: SeriesLayer[] = [ + { + name: splitAccessor ? String(seriesKeys[0]) : columnToLabelMap[seriesKeys[0]], + totalSeriesAtDepth: colorAssignment.totalSeriesCount, + rankAtDepth: colorAssignment.getRank( + layer, + String(seriesKeys[0]), + String(yAccessor) + ), + }, + ]; + return paletteService.get(palette.name).getCategoricalColor( + seriesLayers, + { + maxDepth: 1, + behindText: false, + totalSeries: colorAssignment.totalSeriesCount, + syncColors, + }, + palette.params + ); + }, + groupId: yAxis?.groupId, + enableHistogramMode, + stackMode: isPercentage ? StackMode.Percentage : undefined, + timeZone, + areaSeriesStyle: { + point: { + visible: !xAccessor, + radius: 5, }, - palette.params - ); - }, - groupId: yAxis?.groupId, - enableHistogramMode, - stackMode: isPercentage ? StackMode.Percentage : undefined, - timeZone, - areaSeriesStyle: { - point: { - visible: !xAccessor, - radius: 5, + ...(args.fillOpacity && { area: { opacity: args.fillOpacity } }), }, - ...(args.fillOpacity && { area: { opacity: args.fillOpacity } }), - }, - lineSeriesStyle: { - point: { - visible: !xAccessor, - radius: 5, + lineSeriesStyle: { + point: { + visible: !xAccessor, + radius: 5, + }, }, - }, - name(d) { - // For multiple y series, the name of the operation is used on each, either: - // * Key - Y name - // * Formatted value - Y name - if (accessors.length > 1) { - const result = d.seriesKeys - .map((key: string | number, i) => { - if ( - i === 0 && - splitHint && - splitAccessor && - !layersAlreadyFormatted[splitAccessor] - ) { - return splitFormatter.convert(key); - } - return splitAccessor && i === 0 ? key : columnToLabelMap[key] ?? ''; - }) - .join(' - '); - return result; - } + name(d) { + // For multiple y series, the name of the operation is used on each, either: + // * Key - Y name + // * Formatted value - Y name + if (accessors.length > 1) { + const result = d.seriesKeys + .map((key: string | number, i) => { + if ( + i === 0 && + splitHint && + splitAccessor && + !layersAlreadyFormatted[splitAccessor] + ) { + return splitFormatter.convert(key); + } + return splitAccessor && i === 0 ? key : columnToLabelMap[key] ?? ''; + }) + .join(' - '); + return result; + } - // For formatted split series, format the key - // This handles splitting by dates, for example - if (splitHint) { - if (splitAccessor && layersAlreadyFormatted[splitAccessor]) { - return d.seriesKeys[0]; + // For formatted split series, format the key + // This handles splitting by dates, for example + if (splitHint) { + if (splitAccessor && layersAlreadyFormatted[splitAccessor]) { + return d.seriesKeys[0]; + } + return splitFormatter.convert(d.seriesKeys[0]); } - return splitFormatter.convert(d.seriesKeys[0]); - } - // This handles both split and single-y cases: - // * If split series without formatting, show the value literally - // * If single Y, the seriesKey will be the accessor, so we show the human-readable name - return splitAccessor ? d.seriesKeys[0] : columnToLabelMap[d.seriesKeys[0]] ?? ''; - }, - }; - - const index = `${layerIndex}-${accessorIndex}`; - - const curveType = args.curveType ? CurveType[args.curveType] : undefined; - - switch (seriesType) { - case 'line': - return ( - - ); - case 'bar': - case 'bar_stacked': - case 'bar_percentage_stacked': - case 'bar_horizontal': - case 'bar_horizontal_stacked': - case 'bar_horizontal_percentage_stacked': - const valueLabelsSettings = { - displayValueSettings: { - // This format double fixes two issues in elastic-chart - // * when rotating the chart, the formatter is not correctly picked - // * in some scenarios value labels are not strings, and this breaks the elastic-chart lib - valueFormatter: (d: unknown) => yAxis?.formatter?.convert(d) || '', - showValueLabel: shouldShowValueLabels && valueLabels !== 'hide', - isValueContainedInElement: false, - isAlternatingValueLabel: false, - overflowConstraints: [ - LabelOverflowConstraint.ChartEdges, - LabelOverflowConstraint.BarGeometry, - ], - }, - }; - return ; - case 'area_stacked': - case 'area_percentage_stacked': - return ( - - ); - case 'area': - return ( - - ); - default: - return assertNever(seriesType); - } - }) - )} - {referenceLineLayers.length ? ( - - ) : null} - + // This handles both split and single-y cases: + // * If split series without formatting, show the value literally + // * If single Y, the seriesKey will be the accessor, so we show the human-readable name + return splitAccessor ? d.seriesKeys[0] : columnToLabelMap[d.seriesKeys[0]] ?? ''; + }, + }; + + const index = `${layerIndex}-${accessorIndex}`; + + const curveType = args.curveType ? CurveType[args.curveType] : undefined; + + switch (seriesType) { + case 'line': + return ( + + ); + case 'bar': + case 'bar_stacked': + case 'bar_percentage_stacked': + case 'bar_horizontal': + case 'bar_horizontal_stacked': + case 'bar_horizontal_percentage_stacked': + const valueLabelsSettings = { + displayValueSettings: { + // This format double fixes two issues in elastic-chart + // * when rotating the chart, the formatter is not correctly picked + // * in some scenarios value labels are not strings, and this breaks the elastic-chart lib + valueFormatter: (d: unknown) => yAxis?.formatter?.convert(d) || '', + showValueLabel: shouldShowValueLabels && valueLabels !== 'hide', + isValueContainedInElement: false, + isAlternatingValueLabel: false, + overflowConstraints: [ + LabelOverflowConstraint.ChartEdges, + LabelOverflowConstraint.BarGeometry, + ], + }, + }; + return ; + case 'area_stacked': + case 'area_percentage_stacked': + return ( + + ); + case 'area': + return ( + + ); + default: + return assertNever(seriesType); + } + }) + )} + {referenceLineLayers.length ? ( + + ) : null} + +
); } diff --git a/x-pack/test/functional/apps/lens/chart_data.ts b/x-pack/test/functional/apps/lens/chart_data.ts index 8a43ff909fea5..a8c75b5582ef5 100644 --- a/x-pack/test/functional/apps/lens/chart_data.ts +++ b/x-pack/test/functional/apps/lens/chart_data.ts @@ -32,8 +32,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { operation: 'average', field: 'bytes', }); - - await PageObjects.lens.waitForVisualization(); }); const expectedData = [ @@ -75,6 +73,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { } it('should render xy chart', async () => { + await PageObjects.lens.waitForVisualization('xyVisChart'); + const data = await PageObjects.lens.getCurrentChartDebugState(); assertMatchesExpectedData(data!); }); From d39681f967fc97741a884be8a746f9aac1a7e910 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Wed, 16 Mar 2022 09:55:00 +0200 Subject: [PATCH 019/133] Fixed broken chart selectors. --- .../chart_expressions/expression_xy/kibana.json | 2 +- .../expression_xy/public/components/xy_chart.tsx | 7 ++----- .../public/expression_renderers/xy_chart_renderer.tsx | 11 +++++------ .../chart_expressions/expression_xy/tsconfig.json | 1 - x-pack/test/functional/apps/lens/chart_data.ts | 10 +++++----- 5 files changed, 13 insertions(+), 18 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/kibana.json b/src/plugins/chart_expressions/expression_xy/kibana.json index 58b1623e3ce31..7f24173b071b1 100755 --- a/src/plugins/chart_expressions/expression_xy/kibana.json +++ b/src/plugins/chart_expressions/expression_xy/kibana.json @@ -9,7 +9,7 @@ "description": "Expression XY plugin adds a `xy` renderer and function to the expression plugin. The renderer will display the `xy` chart.", "server": true, "ui": true, - "requiredPlugins": ["expressions", "charts", "data", "fieldFormats", "uiActions", "presentationUtil"], + "requiredPlugins": ["expressions", "charts", "data", "fieldFormats", "uiActions"], "requiredBundles": ["kibanaReact"], "optionalPlugins": [] } diff --git a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx index e4815736280ce..60365123a3fb4 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import React, { memo, useRef } from 'react'; +import React, { useRef } from 'react'; import { Chart, Settings, @@ -521,7 +521,7 @@ export function XYChart({ }; return ( -
+
import('../components/xy_chart')); -const XYChartComponent = withSuspense(LazyXYChart); - export const getXyChartRenderer = ({ getStartDeps, }: XyChartRendererDeps): ExpressionRenderDefinition => ({ @@ -56,10 +52,13 @@ export const getXyChartRenderer = ({ handlers.event({ name: 'brush', data }); }; const deps = await getStartDeps(); + + const { XYChartReportable } = await import('../components/xy_chart'); + ReactDOM.render( - { await PageObjects.lens.switchToVisualization('pie'); - await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.waitForVisualization('partitionVisChart'); const data = await PageObjects.lens.getCurrentChartDebugState(); assertMatchesExpectedPieData(data!); }); it('should render donut chart', async () => { await PageObjects.lens.switchToVisualization('donut'); - await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.waitForVisualization('partitionVisChart'); const data = await PageObjects.lens.getCurrentChartDebugState(); assertMatchesExpectedPieData(data!); }); it('should render treemap chart', async () => { await PageObjects.lens.switchToVisualization('treemap', 'treemap'); - await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.waitForVisualization('partitionVisChart'); const data = await PageObjects.lens.getCurrentChartDebugState(); assertMatchesExpectedPieData(data!); }); it('should render heatmap chart', async () => { await PageObjects.lens.switchToVisualization('heatmap', 'heat'); - await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.waitForVisualization('heatmapChart'); const debugState = await PageObjects.lens.getCurrentChartDebugState(); if (!debugState) { @@ -150,7 +150,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should render metric', async () => { await PageObjects.lens.switchToVisualization('lnsMetric'); - await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.waitForVisualization('mtrVis'); await PageObjects.lens.assertMetric('Average of bytes', '5,727.322'); }); }); From b901d685cd0e2ca3a38b0c6ec803a08097a37d6a Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Wed, 16 Mar 2022 10:20:06 +0200 Subject: [PATCH 020/133] Fixed dashboard tests. --- .../dashboard/feature_controls/time_to_visualize_security.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/test/functional/apps/dashboard/feature_controls/time_to_visualize_security.ts b/x-pack/test/functional/apps/dashboard/feature_controls/time_to_visualize_security.ts index 1d2d3f6862e43..9eeb49f5eb0d2 100644 --- a/x-pack/test/functional/apps/dashboard/feature_controls/time_to_visualize_security.ts +++ b/x-pack/test/functional/apps/dashboard/feature_controls/time_to_visualize_security.ts @@ -130,7 +130,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await PageObjects.lens.switchToVisualization('lnsMetric'); - await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.waitForVisualization('mtrVis'); await PageObjects.lens.assertMetric('Average of bytes', '5,727.322'); await PageObjects.header.waitUntilLoadingHasFinished(); From d327dfe57fdd11dd6d1028337c406862b5d6edd3 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Wed, 16 Mar 2022 10:31:23 +0200 Subject: [PATCH 021/133] dashboard test fixed. --- test/functional/apps/home/_sample_data.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/functional/apps/home/_sample_data.ts b/test/functional/apps/home/_sample_data.ts index e0a96940337e2..9557021d76d31 100644 --- a/test/functional/apps/home/_sample_data.ts +++ b/test/functional/apps/home/_sample_data.ts @@ -95,7 +95,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.header.waitUntilLoadingHasFinished(); await renderable.waitForRender(); log.debug('Checking charts rendered'); - await elasticChart.waitForRenderComplete('lnsVisualizationContainer'); + await elasticChart.waitForRenderComplete('xyVisChart'); log.debug('Checking saved searches rendered'); await dashboardExpect.savedSearchRowCount(10); log.debug('Checking input controls rendered'); From b500961e773f646638e6a4f0c83050e6e5f5ca56 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Wed, 16 Mar 2022 10:57:16 +0200 Subject: [PATCH 022/133] Fixed heatmap vis. --- x-pack/test/functional/apps/lens/add_to_dashboard.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/x-pack/test/functional/apps/lens/add_to_dashboard.ts b/x-pack/test/functional/apps/lens/add_to_dashboard.ts index 90285cc2333de..5dd6920265f36 100644 --- a/x-pack/test/functional/apps/lens/add_to_dashboard.ts +++ b/x-pack/test/functional/apps/lens/add_to_dashboard.ts @@ -253,11 +253,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { field: 'bytes', }); - await PageObjects.lens.waitForVisualization(); - await PageObjects.lens.switchToVisualization('heatmap', 'heat'); - await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.waitForVisualization('heatmapChart'); await PageObjects.lens.openDimensionEditor('lnsHeatmap_cellPanel > lns-dimensionTrigger'); await PageObjects.lens.openPalettePanel('lnsHeatmap'); await testSubjects.click('lnsPalettePanel_dynamicColoring_rangeType_groups_number'); From 7a25b74d8f1ea3074cf02242ed280eedf43c53d7 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Wed, 16 Mar 2022 11:26:48 +0200 Subject: [PATCH 023/133] Smokescreen test fixed. --- x-pack/test/functional/apps/lens/smokescreen.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/test/functional/apps/lens/smokescreen.ts b/x-pack/test/functional/apps/lens/smokescreen.ts index c638344f68df9..5cb55aa15ef01 100644 --- a/x-pack/test/functional/apps/lens/smokescreen.ts +++ b/x-pack/test/functional/apps/lens/smokescreen.ts @@ -60,7 +60,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await listingTable.searchForItemWithName('Afancilenstest'); await PageObjects.lens.clickVisualizeListItemTitle('Afancilenstest'); await PageObjects.lens.goToTimeRange(); - await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.waitForVisualization('xyVisChart'); expect(await PageObjects.lens.getTitle()).to.eql('Afancilenstest'); From 0004794445643011f1aa087a2816414c76a80805 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Wed, 16 Mar 2022 11:46:25 +0200 Subject: [PATCH 024/133] more fixes. --- x-pack/test/functional/apps/lens/drag_and_drop.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/x-pack/test/functional/apps/lens/drag_and_drop.ts b/x-pack/test/functional/apps/lens/drag_and_drop.ts index 27e336a1cbc12..fec3c9e51a591 100644 --- a/x-pack/test/functional/apps/lens/drag_and_drop.ts +++ b/x-pack/test/functional/apps/lens/drag_and_drop.ts @@ -314,9 +314,9 @@ export default function ({ getPageObjects }: FtrProviderContext) { await PageObjects.lens.goToTimeRange(); await PageObjects.header.waitUntilLoadingHasFinished(); await PageObjects.lens.dragFieldToWorkspace('@timestamp'); - await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.waitForVisualization('xyVisChart'); await PageObjects.lens.dragFieldToWorkspace('clientip'); - await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.waitForVisualization('xyVisChart'); expect( await PageObjects.lens.getDimensionTriggersTexts('lnsXY_splitDimensionPanel') ).to.eql(['Top values of clientip']); @@ -330,10 +330,10 @@ export default function ({ getPageObjects }: FtrProviderContext) { it('overwrite existing time dimension if one exists already', async () => { await PageObjects.lens.searchField('utc'); await PageObjects.lens.dragFieldToWorkspace('utc_time'); - await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.waitForVisualization('xyVisChart'); await PageObjects.lens.searchField('client'); await PageObjects.lens.dragFieldToWorkspace('clientip'); - await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.waitForVisualization('xyVisChart'); expect(await PageObjects.lens.getDimensionTriggersTexts('lnsXY_xDimensionPanel')).to.eql([ 'utc_time', ]); From fc8c0b386d63044f9225a62fcbd441902ca8c0ae Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Wed, 16 Mar 2022 13:27:30 +0200 Subject: [PATCH 025/133] async dashboard tests fixed. --- x-pack/test/functional/apps/dashboard/_async_dashboard.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/x-pack/test/functional/apps/dashboard/_async_dashboard.ts b/x-pack/test/functional/apps/dashboard/_async_dashboard.ts index 92cdc72ffc81a..71ef909ffa24a 100644 --- a/x-pack/test/functional/apps/dashboard/_async_dashboard.ts +++ b/x-pack/test/functional/apps/dashboard/_async_dashboard.ts @@ -137,7 +137,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { // check at least one visualization await renderable.waitForRender(); log.debug('Checking charts rendered'); - await elasticChart.waitForRenderComplete('lnsVisualizationContainer'); + await elasticChart.waitForRenderComplete('xyVisChart'); await appMenu.clickLink('Discover'); await retry.try(async function () { @@ -148,7 +148,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.header.waitUntilLoadingHasFinished(); await renderable.waitForRender(); log.debug('Checking charts rendered'); - await elasticChart.waitForRenderComplete('lnsVisualizationContainer'); + await elasticChart.waitForRenderComplete('xyVisChart'); }); it('toggle from Discover to Dashboard attempt 1', async () => { @@ -161,7 +161,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.header.waitUntilLoadingHasFinished(); await renderable.waitForRender(); log.debug('Checking charts rendered'); - await elasticChart.waitForRenderComplete('lnsVisualizationContainer'); + await elasticChart.waitForRenderComplete('xyVisChart'); }); it('toggle from Discover to Dashboard attempt 2', async () => { @@ -174,7 +174,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.header.waitUntilLoadingHasFinished(); await renderable.waitForRender(); log.debug('Checking charts rendered'); - await elasticChart.waitForRenderComplete('lnsVisualizationContainer'); + await elasticChart.waitForRenderComplete('xyVisChart'); log.debug('Checking saved searches rendered'); await dashboardExpect.savedSearchRowCount(10); From 98454ada96e9876f7f35af656ef3e05514ea86fa Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Wed, 16 Mar 2022 15:14:40 +0200 Subject: [PATCH 026/133] Fixed xy smokescreen tests selectors. --- .../test/functional/apps/lens/smokescreen.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/x-pack/test/functional/apps/lens/smokescreen.ts b/x-pack/test/functional/apps/lens/smokescreen.ts index 5cb55aa15ef01..a8367ee597d84 100644 --- a/x-pack/test/functional/apps/lens/smokescreen.ts +++ b/x-pack/test/functional/apps/lens/smokescreen.ts @@ -82,7 +82,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { keepOpen: true, }); await PageObjects.lens.addFilterToAgg(`geo.src : CN`); - await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.waitForVisualization('xyVisChart'); // Verify that the field was persisted from the transition expect(await PageObjects.lens.getFiltersAggLabels()).to.eql([`ip : *`, `geo.src : CN`]); @@ -199,7 +199,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const longLabel = 'Veryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryvery long label wrapping multiple lines'; await PageObjects.lens.editDimensionLabel(longLabel); - await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.waitForVisualization('xyVisChart'); await PageObjects.lens.closeDimensionEditor(); expect(await PageObjects.lens.getDimensionTriggerText('lnsXY_yDimensionPanel')).to.eql( @@ -239,19 +239,19 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); await PageObjects.lens.changeAxisSide('right'); - await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.waitForVisualization('xyVisChart'); let data = await PageObjects.lens.getCurrentChartDebugState(); expect(data?.axes?.y.length).to.eql(2); expect(data?.axes?.y.some(({ position }) => position === 'right')).to.eql(true); await PageObjects.lens.changeAxisSide('left'); - await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.waitForVisualization('xyVisChart'); data = await PageObjects.lens.getCurrentChartDebugState(); expect(data?.axes?.y.length).to.eql(1); expect(data?.axes?.y.some(({ position }) => position === 'right')).to.eql(false); await PageObjects.lens.changeAxisSide('right'); - await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.waitForVisualization('xyVisChart'); await PageObjects.lens.closeDimensionEditor(); }); @@ -261,7 +261,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.lens.openVisualOptions(); await testSubjects.click('lns_valueLabels_inside'); - await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.waitForVisualization('xyVisChart'); // check for value labels let data = await PageObjects.lens.getCurrentChartDebugState(); @@ -269,7 +269,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { // switch to stacked bar chart await PageObjects.lens.switchToVisualization('bar_stacked'); - await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.waitForVisualization('xyVisChart'); // check for value labels data = await PageObjects.lens.getCurrentChartDebugState(); @@ -282,14 +282,14 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await testSubjects.setValue('lnsyLeftAxisTitle', axisTitle, { clearWithKeyboard: true, }); - await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.waitForVisualization('xyVisChart'); let data = await PageObjects.lens.getCurrentChartDebugState(); expect(data?.axes?.y?.[0].title).to.eql(axisTitle); // hide the gridlines await testSubjects.click('lnsshowyLeftAxisGridlines'); - await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.waitForVisualization('xyVisChart'); data = await PageObjects.lens.getCurrentChartDebugState(); expect(data?.axes?.y?.[0].gridlines.length).to.eql(0); From 05cccac4fbf65a82969de0d4fad4a16e6d6ea699 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Wed, 16 Mar 2022 15:23:05 +0200 Subject: [PATCH 027/133] fixed show_underlying_data tests. --- .../test/functional/apps/lens/show_underlying_data.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/x-pack/test/functional/apps/lens/show_underlying_data.ts b/x-pack/test/functional/apps/lens/show_underlying_data.ts index d6ae299baceaf..cdad7097c259d 100644 --- a/x-pack/test/functional/apps/lens/show_underlying_data.ts +++ b/x-pack/test/functional/apps/lens/show_underlying_data.ts @@ -23,7 +23,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.lens.clickVisualizeListItemTitle('lnsXYvis'); await PageObjects.lens.goToTimeRange(); - await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.waitForVisualization('xyVisChart'); // expect the button is shown and enabled await testSubjects.clickWhenNotDisabled(`lnsApp_openInDiscover`); @@ -50,7 +50,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.lens.closeDimensionEditor(); - await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.waitForVisualization('xyVisChart'); // expect the button is shown and enabled await testSubjects.clickWhenNotDisabled(`lnsApp_openInDiscover`); @@ -87,7 +87,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.lens.closeDimensionEditor(); - await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.waitForVisualization('xyVisChart'); await testSubjects.clickWhenNotDisabled(`lnsApp_openInDiscover`); @@ -123,7 +123,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.lens.closeDimensionEditor(); - await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.waitForVisualization('xyVisChart'); // expect the button is shown and enabled await testSubjects.clickWhenNotDisabled(`lnsApp_openInDiscover`); @@ -158,7 +158,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.lens.setFilterBy('bytes > 4000'); await PageObjects.common.sleep(1000); - await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.waitForVisualization('xyVisChart'); // expect the button is shown and enabled await testSubjects.clickWhenNotDisabled(`lnsApp_openInDiscover`); From bcf5201b634da911dd79480092abe2b7ef4f4325 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Wed, 16 Mar 2022 15:24:40 +0200 Subject: [PATCH 028/133] Updated snapshots. --- .../__snapshots__/xy_chart.test.tsx.snap | 3284 +++++++++-------- 1 file changed, 1677 insertions(+), 1607 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/public/components/__snapshots__/xy_chart.test.tsx.snap b/src/plugins/chart_expressions/expression_xy/public/components/__snapshots__/xy_chart.test.tsx.snap index 210b02f984284..23bc1fddf00a8 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/__snapshots__/xy_chart.test.tsx.snap +++ b/src/plugins/chart_expressions/expression_xy/public/components/__snapshots__/xy_chart.test.tsx.snap @@ -1,1705 +1,1775 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`XYChart component it renders area 1`] = ` - - + - + - + - - + + - + - + groupId="left" + id="d-b" + key="0-1" + lineSeriesStyle={ + Object { + "point": Object { + "radius": 5, + "visible": false, + }, + } + } + name={[Function]} + splitSeriesAccessors={ + Array [ + "d", + ] + } + stackAccessors={Array []} + timeZone="UTC" + xAccessor="c" + xScaleType="linear" + yAccessors={ + Array [ + "b", + ] + } + yScaleType="linear" + /> + +
`; exports[`XYChart component it renders bar 1`] = ` - - - - + + - - + - + + + - + displayValueSettings={ + Object { + "isAlternatingValueLabel": false, + "isValueContainedInElement": false, + "overflowConstraints": Array [ + "chartEdges", + "barGeometry", + ], + "showValueLabel": false, + "valueFormatter": [Function], + } + } + enableHistogramMode={false} + groupId="left" + id="d-b" + key="0-1" + lineSeriesStyle={ + Object { + "point": Object { + "radius": 5, + "visible": false, + }, + } + } + name={[Function]} + splitSeriesAccessors={ + Array [ + "d", + ] + } + stackAccessors={Array []} + timeZone="UTC" + xAccessor="c" + xScaleType="linear" + yAccessors={ + Array [ + "b", + ] + } + yScaleType="linear" + /> + +
`; exports[`XYChart component it renders horizontal bar 1`] = ` - - - - + + - - + - + + + - + displayValueSettings={ + Object { + "isAlternatingValueLabel": false, + "isValueContainedInElement": false, + "overflowConstraints": Array [ + "chartEdges", + "barGeometry", + ], + "showValueLabel": false, + "valueFormatter": [Function], + } + } + enableHistogramMode={false} + groupId="left" + id="d-b" + key="0-1" + lineSeriesStyle={ + Object { + "point": Object { + "radius": 5, + "visible": false, + }, + } + } + name={[Function]} + splitSeriesAccessors={ + Array [ + "d", + ] + } + stackAccessors={Array []} + timeZone="UTC" + xAccessor="c" + xScaleType="linear" + yAccessors={ + Array [ + "b", + ] + } + yScaleType="linear" + /> +
+ `; exports[`XYChart component it renders line 1`] = ` - - + - + - + - - + + - + - + groupId="left" + id="d-b" + key="0-1" + lineSeriesStyle={ + Object { + "point": Object { + "radius": 5, + "visible": false, + }, + } + } + name={[Function]} + splitSeriesAccessors={ + Array [ + "d", + ] + } + stackAccessors={Array []} + timeZone="UTC" + xAccessor="c" + xScaleType="ordinal" + yAccessors={ + Array [ + "b", + ] + } + yScaleType="linear" + /> + + `; exports[`XYChart component it renders stacked area 1`] = ` - - + - + - + - - + + - + - + groupId="left" + id="d-b" + key="0-1" + lineSeriesStyle={ + Object { + "point": Object { + "radius": 5, + "visible": false, + }, + } + } + name={[Function]} + splitSeriesAccessors={ + Array [ + "d", + ] + } + stackAccessors={ + Array [ + "c", + ] + } + timeZone="UTC" + xAccessor="c" + xScaleType="ordinal" + yAccessors={ + Array [ + "b", + ] + } + yScaleType="linear" + /> + + `; exports[`XYChart component it renders stacked bar 1`] = ` - - + - + - - - + - + + + - + displayValueSettings={ + Object { + "isAlternatingValueLabel": false, + "isValueContainedInElement": false, + "overflowConstraints": Array [ + "chartEdges", + "barGeometry", + ], + "showValueLabel": false, + "valueFormatter": [Function], + } + } + enableHistogramMode={false} + groupId="left" + id="d-b" + key="0-1" + lineSeriesStyle={ + Object { + "point": Object { + "radius": 5, + "visible": false, + }, + } + } + name={[Function]} + splitSeriesAccessors={ + Array [ + "d", + ] + } + stackAccessors={ + Array [ + "c", + ] + } + timeZone="UTC" + xAccessor="c" + xScaleType="ordinal" + yAccessors={ + Array [ + "b", + ] + } + yScaleType="linear" + /> + + `; exports[`XYChart component it renders stacked horizontal bar 1`] = ` - - - - + + - - + - + + + - + displayValueSettings={ + Object { + "isAlternatingValueLabel": false, + "isValueContainedInElement": false, + "overflowConstraints": Array [ + "chartEdges", + "barGeometry", + ], + "showValueLabel": false, + "valueFormatter": [Function], + } + } + enableHistogramMode={false} + groupId="left" + id="d-b" + key="0-1" + lineSeriesStyle={ + Object { + "point": Object { + "radius": 5, + "visible": false, + }, + } + } + name={[Function]} + splitSeriesAccessors={ + Array [ + "d", + ] + } + stackAccessors={ + Array [ + "c", + ] + } + timeZone="UTC" + xAccessor="c" + xScaleType="ordinal" + yAccessors={ + Array [ + "b", + ] + } + yScaleType="linear" + /> + + `; From 99dc7731d7915b68d3d45af055b7d3d107e32c23 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Wed, 16 Mar 2022 16:15:43 +0200 Subject: [PATCH 029/133] updated limits. --- packages/kbn-optimizer/limits.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index 862f6c4174f16..d176fdee2b1c6 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -124,4 +124,4 @@ pageLoadAssetSize: sessionView: 77750 cloudSecurityPosture: 19109 visTypeGauge: 24113 - expressionXY: 16241 + expressionXY: 49145 From 94a5850b9c89f6b8c07c9051bc7960e65292ff91 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Wed, 16 Mar 2022 16:36:42 +0200 Subject: [PATCH 030/133] Fixed more selectors --- .../functional/apps/lens/drag_and_drop.ts | 29 ++++++++++--------- .../data_visualizer/index_data_visualizer.ts | 5 +++- .../test/functional/page_objects/lens_page.ts | 4 +-- .../services/ml/data_visualizer_table.ts | 4 +-- 4 files changed, 23 insertions(+), 19 deletions(-) diff --git a/x-pack/test/functional/apps/lens/drag_and_drop.ts b/x-pack/test/functional/apps/lens/drag_and_drop.ts index fec3c9e51a591..3f4997ff9bd7f 100644 --- a/x-pack/test/functional/apps/lens/drag_and_drop.ts +++ b/x-pack/test/functional/apps/lens/drag_and_drop.ts @@ -10,6 +10,7 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['visualize', 'lens', 'common', 'header']); + const xyChartContainer = 'xyVisChart'; describe('lens drag and drop tests', () => { describe('basic drag and drop', () => { @@ -18,7 +19,7 @@ export default function ({ getPageObjects }: FtrProviderContext) { await PageObjects.visualize.clickVisType('lens'); await PageObjects.lens.goToTimeRange(); await PageObjects.header.waitUntilLoadingHasFinished(); - await PageObjects.lens.dragFieldToWorkspace('@timestamp'); + await PageObjects.lens.dragFieldToWorkspace('@timestamp', xyChartContainer); expect(await PageObjects.lens.getDimensionTriggerText('lnsXY_xDimensionPanel')).to.eql( '@timestamp' @@ -136,7 +137,7 @@ export default function ({ getPageObjects }: FtrProviderContext) { it('Should duplicate and swap elements when dragging over secondary drop targets', async () => { await PageObjects.lens.removeLayer(); await PageObjects.lens.switchToVisualization('bar'); - await PageObjects.lens.dragFieldToWorkspace('@timestamp'); + await PageObjects.lens.dragFieldToWorkspace('@timestamp', xyChartContainer); await PageObjects.lens.dragDimensionToExtraDropType( 'lnsXY_xDimensionPanel > lns-dimensionTrigger', @@ -165,8 +166,8 @@ export default function ({ getPageObjects }: FtrProviderContext) { it('should combine breakdown dimension with the horizontal one', async () => { await PageObjects.lens.removeLayer(); - await PageObjects.lens.dragFieldToWorkspace('clientip'); - await PageObjects.lens.dragFieldToWorkspace('@message.raw'); + await PageObjects.lens.dragFieldToWorkspace('clientip', xyChartContainer); + await PageObjects.lens.dragFieldToWorkspace('@message.raw', xyChartContainer); await PageObjects.lens.dragDimensionToExtraDropType( 'lnsXY_splitDimensionPanel > lns-dimensionTrigger', @@ -180,7 +181,7 @@ export default function ({ getPageObjects }: FtrProviderContext) { it('should combine field to existing horizontal dimension', async () => { await PageObjects.lens.removeLayer(); - await PageObjects.lens.dragFieldToWorkspace('clientip'); + await PageObjects.lens.dragFieldToWorkspace('clientip', xyChartContainer); await PageObjects.lens.dragFieldToExtraDropType( '@message.raw', @@ -194,7 +195,7 @@ export default function ({ getPageObjects }: FtrProviderContext) { it('should combine two multi terms dimensions', async () => { await PageObjects.lens.removeLayer(); - await PageObjects.lens.dragFieldToWorkspace('clientip'); + await PageObjects.lens.dragFieldToWorkspace('clientip', xyChartContainer); await PageObjects.lens.dragFieldToExtraDropType( '@message.raw', @@ -313,10 +314,10 @@ export default function ({ getPageObjects }: FtrProviderContext) { await PageObjects.visualize.clickVisType('lens'); await PageObjects.lens.goToTimeRange(); await PageObjects.header.waitUntilLoadingHasFinished(); - await PageObjects.lens.dragFieldToWorkspace('@timestamp'); - await PageObjects.lens.waitForVisualization('xyVisChart'); - await PageObjects.lens.dragFieldToWorkspace('clientip'); - await PageObjects.lens.waitForVisualization('xyVisChart'); + await PageObjects.lens.dragFieldToWorkspace('@timestamp', xyChartContainer); + await PageObjects.lens.waitForVisualization(xyChartContainer); + await PageObjects.lens.dragFieldToWorkspace('clientip', xyChartContainer); + await PageObjects.lens.waitForVisualization(xyChartContainer); expect( await PageObjects.lens.getDimensionTriggersTexts('lnsXY_splitDimensionPanel') ).to.eql(['Top values of clientip']); @@ -329,11 +330,11 @@ export default function ({ getPageObjects }: FtrProviderContext) { it('overwrite existing time dimension if one exists already', async () => { await PageObjects.lens.searchField('utc'); - await PageObjects.lens.dragFieldToWorkspace('utc_time'); - await PageObjects.lens.waitForVisualization('xyVisChart'); + await PageObjects.lens.dragFieldToWorkspace('utc_time', xyChartContainer); + await PageObjects.lens.waitForVisualization(xyChartContainer); await PageObjects.lens.searchField('client'); - await PageObjects.lens.dragFieldToWorkspace('clientip'); - await PageObjects.lens.waitForVisualization('xyVisChart'); + await PageObjects.lens.dragFieldToWorkspace('clientip', xyChartContainer); + await PageObjects.lens.waitForVisualization(xyChartContainer); expect(await PageObjects.lens.getDimensionTriggersTexts('lnsXY_xDimensionPanel')).to.eql([ 'utc_time', ]); diff --git a/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts b/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts index a20962e607af2..e63deb5993fb8 100644 --- a/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts +++ b/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts @@ -262,7 +262,10 @@ export default function ({ getPageObject, getService }: FtrProviderContext) { ); if (lensNonMetricField) { - await ml.dataVisualizerTable.assertLensActionShowChart(lensNonMetricField.fieldName); + await ml.dataVisualizerTable.assertLensActionShowChart( + lensNonMetricField.fieldName, + 'xyVisChart' + ); await ml.navigation.browserBackTo('dataVisualizerTable'); } }); diff --git a/x-pack/test/functional/page_objects/lens_page.ts b/x-pack/test/functional/page_objects/lens_page.ts index ae11939558462..515e1b5855db0 100644 --- a/x-pack/test/functional/page_objects/lens_page.ts +++ b/x-pack/test/functional/page_objects/lens_page.ts @@ -196,7 +196,7 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont * * @param field - the desired field for the dimension * */ - async dragFieldToWorkspace(field: string) { + async dragFieldToWorkspace(field: string, visualizationTestSubj?: string) { const from = `lnsFieldListPanelField-${field}`; await find.existsByCssSelector(from); await browser.html5DragAndDrop( @@ -204,7 +204,7 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont testSubjects.getCssSelector('lnsWorkspace') ); await this.waitForLensDragDropToFinish(); - await this.waitForVisualization(); + await this.waitForVisualization(visualizationTestSubj); }, /** diff --git a/x-pack/test/functional/services/ml/data_visualizer_table.ts b/x-pack/test/functional/services/ml/data_visualizer_table.ts index cf9b1f8fa35a5..e5c0dafbd00f7 100644 --- a/x-pack/test/functional/services/ml/data_visualizer_table.ts +++ b/x-pack/test/functional/services/ml/data_visualizer_table.ts @@ -565,12 +565,12 @@ export function MachineLearningDataVisualizerTableProvider( } } - public async assertLensActionShowChart(fieldName: string) { + public async assertLensActionShowChart(fieldName: string, visualizationContainer?: string) { await retry.tryForTime(30 * 1000, async () => { await testSubjects.clickWhenNotDisabled( this.rowSelector(fieldName, 'dataVisualizerActionViewInLensButton') ); - await testSubjects.existOrFail('lnsVisualizationContainer', { + await testSubjects.existOrFail(visualizationContainer ?? 'lnsVisualizationContainer', { timeout: 15 * 1000, }); }); From bfea6f1ae0ff1b21b7124ae89dd11736e8bc7fcc Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Wed, 16 Mar 2022 16:52:46 +0200 Subject: [PATCH 031/133] Fixed persistent context test. --- x-pack/test/functional/apps/lens/persistent_context.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/test/functional/apps/lens/persistent_context.ts b/x-pack/test/functional/apps/lens/persistent_context.ts index ff677440e8fe0..29c850ea553d3 100644 --- a/x-pack/test/functional/apps/lens/persistent_context.ts +++ b/x-pack/test/functional/apps/lens/persistent_context.ts @@ -83,7 +83,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.navigationalSearch.clickOnOption(0); await PageObjects.lens.waitForEmptyWorkspace(); await PageObjects.lens.switchToVisualization('lnsMetric'); - await PageObjects.lens.dragFieldToWorkspace('@timestamp'); + await PageObjects.lens.dragFieldToWorkspace('@timestamp', 'mtrVis'); }); it('preserves time range', async () => { // fill the navigation search and select empty From 7cb4db40c9ca984183704c8a0bd9476a055ff8bc Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Wed, 16 Mar 2022 17:11:20 +0200 Subject: [PATCH 032/133] Fixed some more test at ml. --- .../apps/ml/data_visualizer/index_data_visualizer.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts b/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts index e63deb5993fb8..a65468e0ca3ec 100644 --- a/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts +++ b/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts @@ -254,7 +254,10 @@ export default function ({ getPageObject, getService }: FtrProviderContext) { const lensMetricField = testData.expected.metricFields![0]; if (lensMetricField) { - await ml.dataVisualizerTable.assertLensActionShowChart(lensMetricField.fieldName); + await ml.dataVisualizerTable.assertLensActionShowChart( + lensMetricField.fieldName, + 'mtrVis' + ); await ml.navigation.browserBackTo('dataVisualizerTable'); } const lensNonMetricField = testData.expected.nonMetricFields?.find( @@ -264,7 +267,7 @@ export default function ({ getPageObject, getService }: FtrProviderContext) { if (lensNonMetricField) { await ml.dataVisualizerTable.assertLensActionShowChart( lensNonMetricField.fieldName, - 'xyVisChart' + 'mtrVis' ); await ml.navigation.browserBackTo('dataVisualizerTable'); } From 5ea9cc5b21a46b3123f0d379d444181f4190047a Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Wed, 16 Mar 2022 18:06:34 +0200 Subject: [PATCH 033/133] Fixed types and imports --- .../common/expression_functions/xy_chart.ts | 5 ++++- .../xy_config_panel/dimension_editor.tsx | 13 +++++-------- .../shared/marker_decoration_settings.tsx | 1 - 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_chart.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_chart.ts index ac8b3110682ee..6e7ed760018cb 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_chart.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_chart.ts @@ -161,7 +161,10 @@ export const xyChartFunction: ExpressionFunctionDefinition< }, fn(data, args, handlers) { if (handlers?.inspectorAdapters?.tables) { - logDataTable(handlers.inspectorAdapters.tables, data.tables); + handlers.inspectorAdapters.tables.logDataTable( + handlers.inspectorAdapters.tables, + data.tables + ); } return { type: 'render', diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/dimension_editor.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/dimension_editor.tsx index 7fbe54387721f..3d68c5b0af530 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/dimension_editor.tsx @@ -50,16 +50,12 @@ export function DimensionEditor( const index = state.layers.findIndex((l) => l.layerId === layerId); const layer = state.layers[index]; - const localLayer: XYDataLayerConfig = layer; - const { inputValue: localState, handleInputChange: setLocalState } = useDebouncedValue({ value: props.state, onChange: props.setState, }); - const localYConfig = localLayer?.yConfig?.find( - (yAxisConfig) => yAxisConfig.forAccessor === accessor - ); + const localYConfig = layer?.yConfig?.find((yAxisConfig) => yAxisConfig.forAccessor === accessor); const axisMode = localYConfig?.axisMode || 'auto'; const setConfig = useCallback( @@ -67,7 +63,7 @@ export function DimensionEditor( if (yConfig == null) { return; } - const newYConfigs = [...(localLayer.yConfig || [])]; + const newYConfigs = [...(layer.yConfig || [])]; const existingIndex = newYConfigs.findIndex( (yAxisConfig) => yAxisConfig.forAccessor === accessor ); @@ -79,15 +75,16 @@ export function DimensionEditor( ...yConfig, }); } - setLocalState(updateLayer(localState, { ...localLayer, yConfig: newYConfigs }, index)); + setLocalState(updateLayer(localState, { ...layer, yConfig: newYConfigs }, index)); }, - [accessor, index, localState, localLayer, setLocalState] + [accessor, index, localState, layer, setLocalState] ); if (isReferenceLayer(layer)) { return ; } + const localLayer: XYDataLayerConfig = layer; if (props.groupId === 'breakdown') { return ( <> diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/shared/marker_decoration_settings.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/shared/marker_decoration_settings.tsx index d8157a8f4e078..cd5709887522e 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/shared/marker_decoration_settings.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/shared/marker_decoration_settings.tsx @@ -10,7 +10,6 @@ import { i18n } from '@kbn/i18n'; import { EuiButtonGroup, EuiFormRow } from '@elastic/eui'; import { IconPosition, - YConfig, YAxisMode, } from '../../../../../../../src/plugins/chart_expressions/expression_xy/common'; From 4f7265867e70ceb5d1604fc4f3a1e80592ee2e3c Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Wed, 16 Mar 2022 20:54:48 +0200 Subject: [PATCH 034/133] Fixed handlers.inspectorAdapters.tables.logDatatable --- .../expression_xy/common/expression_functions/xy_chart.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_chart.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_chart.ts index 6e7ed760018cb..7dcaf6f5edf5f 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_chart.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_chart.ts @@ -161,7 +161,7 @@ export const xyChartFunction: ExpressionFunctionDefinition< }, fn(data, args, handlers) { if (handlers?.inspectorAdapters?.tables) { - handlers.inspectorAdapters.tables.logDataTable( + handlers.inspectorAdapters.tables.logDatatable( handlers.inspectorAdapters.tables, data.tables ); From ee4ec20fb6ccdc750e498f7012cb6d905a26f2c7 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Thu, 17 Mar 2022 08:28:20 +0200 Subject: [PATCH 035/133] Fixed logDatatable --- .../common/expression_functions/xy_chart.ts | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_chart.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_chart.ts index 7dcaf6f5edf5f..a75756a1db531 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_chart.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_chart.ts @@ -7,7 +7,11 @@ */ import { i18n } from '@kbn/i18n'; -import type { ExpressionFunctionDefinition } from '../../../../../../src/plugins/expressions/common'; +import type { + ExpressionFunctionDefinition, + TablesAdapter, + Datatable, +} from '../../../../expressions'; import { LensMultiTable, XYArgs, XYRender } from '../types'; import { XY_CHART, @@ -26,6 +30,13 @@ import { AXIS_TITLES_VISIBILITY_CONFIG, } from '../constants'; +export const logDataTable = ( + tableAdapter: TablesAdapter, + datatables: Record = {} +) => { + Object.entries(datatables).forEach(([key, table]) => tableAdapter.logDatatable(key, table)); +}; + export const xyChartFunction: ExpressionFunctionDefinition< typeof XY_CHART, LensMultiTable, @@ -161,11 +172,9 @@ export const xyChartFunction: ExpressionFunctionDefinition< }, fn(data, args, handlers) { if (handlers?.inspectorAdapters?.tables) { - handlers.inspectorAdapters.tables.logDatatable( - handlers.inspectorAdapters.tables, - data.tables - ); + logDataTable(handlers.inspectorAdapters.tables, data.tables); } + return { type: 'render', as: XY_CHART_RENDERER, From e72005c7858b440b86bce1c43f9ae6dc5707ffde Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Thu, 17 Mar 2022 10:38:26 +0200 Subject: [PATCH 036/133] Translations fixed. --- .../axis_extent_config.ts | 16 +-- .../axis_titles_visibility_config.ts | 12 +- .../expression_functions/data_layer_config.ts | 56 ++++++--- .../expression_functions/grid_lines_config.ts | 10 +- .../labels_orientation_config.ts | 10 +- .../expression_functions/legend_config.ts | 24 ++-- .../reference_line_layer_config.ts | 21 +++- .../tick_labels_config.ts | 10 +- .../common/expression_functions/xy_chart.ts | 42 ++++--- .../expression_functions/y_axis_config.ts | 41 +++++-- .../components/legend_action_popover.tsx | 6 +- .../public/components/xy_chart.tsx | 2 +- .../public/definitions/visualizations.ts | 115 ++---------------- .../xy_chart_renderer.tsx | 2 +- .../translations/translations/fr-FR.json | 85 ++++++------- .../translations/translations/ja-JP.json | 83 +++++++------ .../translations/translations/zh-CN.json | 85 ++++++------- 17 files changed, 303 insertions(+), 317 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/axis_extent_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/axis_extent_config.ts index deba7f9f541e6..c5cf89a4663c9 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/axis_extent_config.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/axis_extent_config.ts @@ -7,7 +7,7 @@ */ import { i18n } from '@kbn/i18n'; -import type { ExpressionFunctionDefinition } from '../../../../../../src/plugins/expressions/common'; +import type { ExpressionFunctionDefinition } from '../../../../expressions/common'; import { AxisExtentConfig, AxisExtentConfigResult } from '../types'; import { AxisExtentModes, AXIS_EXTENT_CONFIG } from '../constants'; @@ -20,26 +20,28 @@ export const axisExtentConfigFunction: ExpressionFunctionDefinition< name: AXIS_EXTENT_CONFIG, aliases: [], type: AXIS_EXTENT_CONFIG, - help: `Configure the xy chart's axis extents`, + help: i18n.translate('expressionXY.axisExtentConfig.help', { + defaultMessage: `Configure the xy chart's axis extents`, + }), inputTypes: ['null'], args: { mode: { types: ['string'], options: [...Object.values(AxisExtentModes)], - help: i18n.translate('xpack.lens.xyChart.extentMode.help', { + help: i18n.translate('expressionXY.axisExtentConfig.extentMode.help', { defaultMessage: 'The extent mode', }), }, lowerBound: { types: ['number'], - help: i18n.translate('xpack.lens.xyChart.extentMode.help', { - defaultMessage: 'The extent mode', + help: i18n.translate('expressionXY.axisExtentConfig.lowerBound.help', { + defaultMessage: 'Lower bound', }), }, upperBound: { types: ['number'], - help: i18n.translate('xpack.lens.xyChart.extentMode.help', { - defaultMessage: 'The extent mode', + help: i18n.translate('expressionXY.axisExtentConfig.upperBound.help', { + defaultMessage: 'Upper bound', }), }, }, diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/axis_titles_visibility_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/axis_titles_visibility_config.ts index 9cdf3128afa3d..50302214fc37c 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/axis_titles_visibility_config.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/axis_titles_visibility_config.ts @@ -7,7 +7,7 @@ */ import { i18n } from '@kbn/i18n'; -import type { ExpressionFunctionDefinition } from '../../../../../../src/plugins/expressions/common'; +import type { ExpressionFunctionDefinition } from '../../../../expressions/common'; import { AXIS_TITLES_VISIBILITY_CONFIG } from '../constants'; import { AxesSettingsConfig, AxisTitlesVisibilityConfigResult } from '../types'; @@ -20,24 +20,26 @@ export const axisTitlesVisibilityConfigFunction: ExpressionFunctionDefinition< name: AXIS_TITLES_VISIBILITY_CONFIG, aliases: [], type: AXIS_TITLES_VISIBILITY_CONFIG, - help: `Configure the xy chart's axis titles appearance`, + help: i18n.translate('expressionXY.axisTitlesVisibilityConfig.help', { + defaultMessage: `Configure the xy chart's axis titles appearance`, + }), inputTypes: ['null'], args: { x: { types: ['boolean'], - help: i18n.translate('xpack.lens.xyChart.xAxisTitle.help', { + help: i18n.translate('expressionXY.axisTitlesVisibilityConfig.x.help', { defaultMessage: 'Specifies whether or not the title of the x-axis are visible.', }), }, yLeft: { types: ['boolean'], - help: i18n.translate('xpack.lens.xyChart.yLeftAxisTitle.help', { + help: i18n.translate('expressionXY.axisTitlesVisibilityConfig.yLeft.help', { defaultMessage: 'Specifies whether or not the title of the left y-axis are visible.', }), }, yRight: { types: ['boolean'], - help: i18n.translate('xpack.lens.xyChart.yRightAxisTitle.help', { + help: i18n.translate('expressionXY.axisTitlesVisibilityConfig.yRight.help', { defaultMessage: 'Specifies whether or not the title of the right y-axis are visible.', }), }, diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer_config.ts index fd5bfb471c0f6..3aac992d674d9 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer_config.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer_config.ts @@ -6,7 +6,8 @@ * Side Public License, v 1. */ -import type { ExpressionFunctionDefinition } from '../../../../../../src/plugins/expressions/common'; +import { i18n } from '@kbn/i18n'; +import type { ExpressionFunctionDefinition } from '../../../../expressions/common'; import { DataLayerArgs, DataLayerConfigResult } from '../types'; import { DATA_LAYER, @@ -26,64 +27,89 @@ export const dataLayerConfigFunction: ExpressionFunctionDefinition< name: DATA_LAYER, aliases: [], type: DATA_LAYER, - help: `Configure a layer in the xy chart`, + help: i18n.translate('expressionXY.dataLayer.help', { + defaultMessage: `Configure a layer in the xy chart`, + }), inputTypes: ['null'], args: { hide: { types: ['boolean'], default: false, - help: 'Show / hide axis', + help: i18n.translate('expressionXY.dataLayer.hide.help', { + defaultMessage: 'Show / hide axis', + }), }, layerId: { types: ['string'], - help: '', + help: i18n.translate('expressionXY.dataLayer.layerId.help', { + defaultMessage: 'Layer ID', + }), }, xAccessor: { types: ['string'], - help: '', + help: i18n.translate('expressionXY.dataLayer.xAccessor.help', { + defaultMessage: 'X-axis', + }), }, seriesType: { types: ['string'], options: [...Object.values(SeriesTypes)], - help: 'The type of chart to display.', + help: i18n.translate('expressionXY.dataLayer.seriesType.help', { + defaultMessage: 'The type of chart to display.', + }), }, xScaleType: { options: [...Object.values(XScaleTypes)], - help: 'The scale type of the x axis', + help: i18n.translate('expressionXY.dataLayer.xScaleType.help', { + defaultMessage: 'The scale type of the x axis', + }), default: XScaleTypes.ORDINAL, }, isHistogram: { types: ['boolean'], default: false, - help: 'Whether to layout the chart as a histogram', + help: i18n.translate('expressionXY.dataLayer.isHistogram.help', { + defaultMessage: 'Whether to layout the chart as a histogram', + }), }, yScaleType: { options: [...Object.values(YScaleTypes)], - help: 'The scale type of the y axes', + help: i18n.translate('expressionXY.dataLayer.yScaleType.help', { + defaultMessage: 'The scale type of the y axes', + }), default: YScaleTypes.LINEAR, }, splitAccessor: { types: ['string'], - help: 'The column to split by', - multi: false, + help: i18n.translate('expressionXY.dataLayer.splitAccessor.help', { + defaultMessage: 'The column to split by', + }), }, accessors: { types: ['string'], - help: 'The columns to display on the y axis.', + help: i18n.translate('expressionXY.dataLayer.accessors.help', { + defaultMessage: 'The columns to display on the y axis.', + }), multi: true, }, yConfig: { types: [Y_CONFIG], - help: 'Additional configuration for y axes', + help: i18n.translate('expressionXY.dataLayer.yConfig.help', { + defaultMessage: 'Additional configuration for y axes', + }), multi: true, }, columnToLabel: { types: ['string'], - help: 'JSON key-value pairs of column ID to label', + help: i18n.translate('expressionXY.dataLayer.columnToLabel.help', { + defaultMessage: 'JSON key-value pairs of column ID to label', + }), }, palette: { default: `{theme "palette" default={system_palette name="default"} }`, - help: '', + help: i18n.translate('expressionXY.dataLayer.palette.help', { + defaultMessage: 'Palette', + }), types: ['palette'], }, }, diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/grid_lines_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/grid_lines_config.ts index 2a3a749489a7f..b94b8b5709c03 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/grid_lines_config.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/grid_lines_config.ts @@ -20,24 +20,26 @@ export const gridlinesConfigFunction: ExpressionFunctionDefinition< name: GRID_LINES_CONFIG, aliases: [], type: GRID_LINES_CONFIG, - help: `Configure the xy chart's gridlines appearance`, + help: i18n.translate('expressionXY.gridlinesConfig.help', { + defaultMessage: `Configure the xy chart's gridlines appearance`, + }), inputTypes: ['null'], args: { x: { types: ['boolean'], - help: i18n.translate('xpack.lens.xyChart.xAxisGridlines.help', { + help: i18n.translate('expressionXY.gridlinesConfig.x.help', { defaultMessage: 'Specifies whether or not the gridlines of the x-axis are visible.', }), }, yLeft: { types: ['boolean'], - help: i18n.translate('xpack.lens.xyChart.yLeftAxisgridlines.help', { + help: i18n.translate('expressionXY.gridlinesConfig.yLeft.help', { defaultMessage: 'Specifies whether or not the gridlines of the left y-axis are visible.', }), }, yRight: { types: ['boolean'], - help: i18n.translate('xpack.lens.xyChart.yRightAxisgridlines.help', { + help: i18n.translate('expressionXY.gridlinesConfig.yRight.help', { defaultMessage: 'Specifies whether or not the gridlines of the right y-axis are visible.', }), }, diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/labels_orientation_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/labels_orientation_config.ts index 383c7a564e7c9..94d726c56f3b2 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/labels_orientation_config.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/labels_orientation_config.ts @@ -20,27 +20,29 @@ export const labelsOrientationConfigFunction: ExpressionFunctionDefinition< name: LABELS_ORIENTATION_CONFIG, aliases: [], type: LABELS_ORIENTATION_CONFIG, - help: `Configure the xy chart's tick labels orientation`, + help: i18n.translate('expressionXY.labelsOrientationConfig.help', { + defaultMessage: `Configure the xy chart's tick labels orientation`, + }), inputTypes: ['null'], args: { x: { types: ['number'], options: [0, -90, -45], - help: i18n.translate('xpack.lens.xyChart.xAxisLabelsOrientation.help', { + help: i18n.translate('expressionXY.labelsOrientationConfig.x.help', { defaultMessage: 'Specifies the labels orientation of the x-axis.', }), }, yLeft: { types: ['number'], options: [0, -90, -45], - help: i18n.translate('xpack.lens.xyChart.yLeftAxisLabelsOrientation.help', { + help: i18n.translate('expressionXY.labelsOrientationConfig.yLeft.help', { defaultMessage: 'Specifies the labels orientation of the left y-axis.', }), }, yRight: { types: ['number'], options: [0, -90, -45], - help: i18n.translate('xpack.lens.xyChart.yRightAxisLabelsOrientation.help', { + help: i18n.translate('expressionXY.labelsOrientationConfig.yRight.help', { defaultMessage: 'Specifies the labels orientation of the right y-axis.', }), }, diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/legend_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/legend_config.ts index 041f160033506..384f23aee811a 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/legend_config.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/legend_config.ts @@ -21,38 +21,40 @@ export const legendConfigFunction: ExpressionFunctionDefinition< name: LEGEND_CONFIG, aliases: [], type: LEGEND_CONFIG, - help: `Configure the xy chart's legend`, + help: i18n.translate('expressionXY.legendConfig.help', { + defaultMessage: `Configure the xy chart's legend`, + }), inputTypes: ['null'], args: { isVisible: { types: ['boolean'], - help: i18n.translate('xpack.lens.xyChart.isVisible.help', { + help: i18n.translate('expressionXY.legendConfig.isVisible.help', { defaultMessage: 'Specifies whether or not the legend is visible.', }), }, position: { types: ['string'], options: [Position.Top, Position.Right, Position.Bottom, Position.Left], - help: i18n.translate('xpack.lens.xyChart.position.help', { + help: i18n.translate('expressionXY.legendConfig.position.help', { defaultMessage: 'Specifies the legend position.', }), }, showSingleSeries: { types: ['boolean'], - help: i18n.translate('xpack.lens.xyChart.showSingleSeries.help', { + help: i18n.translate('expressionXY.legendConfig.showSingleSeries.help', { defaultMessage: 'Specifies whether a legend with just a single entry should be shown', }), }, isInside: { types: ['boolean'], - help: i18n.translate('xpack.lens.xyChart.isInside.help', { + help: i18n.translate('expressionXY.legendConfig.isInside.help', { defaultMessage: 'Specifies whether a legend is inside the chart', }), }, horizontalAlignment: { types: ['string'], options: [HorizontalAlignment.Right, HorizontalAlignment.Left], - help: i18n.translate('xpack.lens.xyChart.horizontalAlignment.help', { + help: i18n.translate('expressionXY.legendConfig.horizontalAlignment.help', { defaultMessage: 'Specifies the horizontal alignment of the legend when it is displayed inside chart.', }), @@ -60,33 +62,33 @@ export const legendConfigFunction: ExpressionFunctionDefinition< verticalAlignment: { types: ['string'], options: [VerticalAlignment.Top, VerticalAlignment.Bottom], - help: i18n.translate('xpack.lens.xyChart.verticalAlignment.help', { + help: i18n.translate('expressionXY.legendConfig.verticalAlignment.help', { defaultMessage: 'Specifies the vertical alignment of the legend when it is displayed inside chart.', }), }, floatingColumns: { types: ['number'], - help: i18n.translate('xpack.lens.xyChart.floatingColumns.help', { + help: i18n.translate('expressionXY.legendConfig.floatingColumns.help', { defaultMessage: 'Specifies the number of columns when legend is displayed inside chart.', }), }, maxLines: { types: ['number'], - help: i18n.translate('xpack.lens.xyChart.maxLines.help', { + help: i18n.translate('expressionXY.legendConfig.maxLines.help', { defaultMessage: 'Specifies the number of lines per legend item.', }), }, shouldTruncate: { types: ['boolean'], default: true, - help: i18n.translate('xpack.lens.xyChart.shouldTruncate.help', { + help: i18n.translate('expressionXY.legendConfig.shouldTruncate.help', { defaultMessage: 'Specifies whether the legend items will be truncated or not', }), }, legendSize: { types: ['number'], - help: i18n.translate('xpack.lens.xyChart.legendSize.help', { + help: i18n.translate('expressionXY.legendConfig.legendSize.help', { defaultMessage: 'Specifies the legend size in pixels.', }), }, diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line_layer_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line_layer_config.ts index 6f06e7e0f0839..c5d0f17ff138d 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line_layer_config.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line_layer_config.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import { i18n } from '@kbn/i18n'; import type { ExpressionFunctionDefinition } from '../../../../expressions/common'; import { LayerTypes, REFERENCE_LINE_LAYER, Y_CONFIG } from '../constants'; import { ReferenceLineLayerArgs, ReferenceLineLayerConfigResult } from '../types'; @@ -19,26 +20,36 @@ export const referenceLineLayerConfigFunction: ExpressionFunctionDefinition< name: REFERENCE_LINE_LAYER, aliases: [], type: REFERENCE_LINE_LAYER, - help: `Configure a layer in the xy chart`, + help: i18n.translate('expressionXY.referenceLineLayer.help', { + defaultMessage: `Configure a reference line in the xy chart`, + }), inputTypes: ['null'], args: { layerId: { types: ['string'], - help: '', + help: i18n.translate('expressionXY.referenceLineLayer.layerId.help', { + defaultMessage: `Layer ID`, + }), }, accessors: { types: ['string'], - help: 'The columns to display on the y axis.', + help: i18n.translate('expressionXY.referenceLineLayer.accessors.help', { + defaultMessage: 'The columns to display on the y axis.', + }), multi: true, }, yConfig: { types: [Y_CONFIG], - help: 'Additional configuration for y axes', + help: i18n.translate('expressionXY.referenceLineLayer.yConfig.help', { + defaultMessage: 'Additional configuration for y axes', + }), multi: true, }, columnToLabel: { types: ['string'], - help: 'JSON key-value pairs of column ID to label', + help: i18n.translate('expressionXY.referenceLineLayer.columnToLabel.help', { + defaultMessage: 'JSON key-value pairs of column ID to label', + }), }, }, fn(input, args) { diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/tick_labels_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/tick_labels_config.ts index 3b39c1992d273..6a882094a56d2 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/tick_labels_config.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/tick_labels_config.ts @@ -20,24 +20,26 @@ export const tickLabelsConfigFunction: ExpressionFunctionDefinition< name: TICK_LABELS_CONFIG, aliases: [], type: TICK_LABELS_CONFIG, - help: `Configure the xy chart's tick labels appearance`, + help: i18n.translate('expressionXY.tickLabelsConfig.help', { + defaultMessage: `Configure the xy chart's tick labels appearance`, + }), inputTypes: ['null'], args: { x: { types: ['boolean'], - help: i18n.translate('xpack.lens.xyChart.xAxisTickLabels.help', { + help: i18n.translate('expressionXY.tickLabelsConfig.x.help', { defaultMessage: 'Specifies whether or not the tick labels of the x-axis are visible.', }), }, yLeft: { types: ['boolean'], - help: i18n.translate('xpack.lens.xyChart.yLeftAxisTickLabels.help', { + help: i18n.translate('expressionXY.tickLabelsConfig.yLeft.help', { defaultMessage: 'Specifies whether or not the tick labels of the left y-axis are visible.', }), }, yRight: { types: ['boolean'], - help: i18n.translate('xpack.lens.xyChart.yRightAxisTickLabels.help', { + help: i18n.translate('expressionXY.tickLabelsConfig.yRight.help', { defaultMessage: 'Specifies whether or not the tick labels of the right y-axis are visible.', }), }, diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_chart.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_chart.ts index a75756a1db531..f6fddf2bf39bf 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_chart.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_chart.ts @@ -46,7 +46,7 @@ export const xyChartFunction: ExpressionFunctionDefinition< name: XY_CHART, type: 'render', inputTypes: [MULTITABLE], - help: i18n.translate('xpack.lens.xyChart.help', { + help: i18n.translate('expressionXY.xyVis.help', { defaultMessage: 'An X/Y chart', }), args: { @@ -60,111 +60,115 @@ export const xyChartFunction: ExpressionFunctionDefinition< }, xTitle: { types: ['string'], - help: i18n.translate('xpack.lens.xyChart.xTitle.help', { + help: i18n.translate('expressionXY.xyVis.xTitle.help', { defaultMessage: 'X axis title', }), }, yTitle: { types: ['string'], - help: i18n.translate('xpack.lens.xyChart.yLeftTitle.help', { + help: i18n.translate('expressionXY.xyVis.yLeftTitle.help', { defaultMessage: 'Y left axis title', }), }, yRightTitle: { types: ['string'], - help: i18n.translate('xpack.lens.xyChart.yRightTitle.help', { + help: i18n.translate('expressionXY.xyVis.yRightTitle.help', { defaultMessage: 'Y right axis title', }), }, yLeftExtent: { types: [AXIS_EXTENT_CONFIG], - help: i18n.translate('xpack.lens.xyChart.yLeftExtent.help', { + help: i18n.translate('expressionXY.xyVis.yLeftExtent.help', { defaultMessage: 'Y left axis extents', }), }, yRightExtent: { types: [AXIS_EXTENT_CONFIG], - help: i18n.translate('xpack.lens.xyChart.yRightExtent.help', { + help: i18n.translate('expressionXY.xyVis.yRightExtent.help', { defaultMessage: 'Y right axis extents', }), }, legend: { types: [LEGEND_CONFIG], - help: i18n.translate('xpack.lens.xyChart.legend.help', { + help: i18n.translate('expressionXY.xyVis.legend.help', { defaultMessage: 'Configure the chart legend.', }), }, fittingFunction: { types: ['string'], options: [...Object.values(FittingFunctions)], - help: i18n.translate('xpack.lens.xyChart.fittingFunction.help', { + help: i18n.translate('expressionXY.xyVis.fittingFunction.help', { defaultMessage: 'Define how missing values are treated', }), }, valueLabels: { types: ['string'], options: [...Object.values(ValueLabelModes)], - help: '', + help: i18n.translate('expressionXY.xyVis.valueLabels.help', { + defaultMessage: 'Value labels mode', + }), }, tickLabelsVisibilitySettings: { types: [TICK_LABELS_CONFIG], - help: i18n.translate('xpack.lens.xyChart.tickLabelsSettings.help', { + help: i18n.translate('expressionXY.xyVis.tickLabelsVisibilitySettings.help', { defaultMessage: 'Show x and y axes tick labels', }), }, labelsOrientation: { types: [LABELS_ORIENTATION_CONFIG], - help: i18n.translate('xpack.lens.xyChart.labelsOrientation.help', { + help: i18n.translate('expressionXY.xyVis.labelsOrientation.help', { defaultMessage: 'Defines the rotation of the axis labels', }), }, gridlinesVisibilitySettings: { types: [GRID_LINES_CONFIG], - help: i18n.translate('xpack.lens.xyChart.gridlinesSettings.help', { + help: i18n.translate('expressionXY.xyVis.gridlinesVisibilitySettings.help', { defaultMessage: 'Show x and y axes gridlines', }), }, axisTitlesVisibilitySettings: { types: [AXIS_TITLES_VISIBILITY_CONFIG], - help: i18n.translate('xpack.lens.xyChart.axisTitlesSettings.help', { + help: i18n.translate('expressionXY.xyVis.axisTitlesVisibilitySettings.help', { defaultMessage: 'Show x and y axes titles', }), }, layers: { types: [DATA_LAYER, REFERENCE_LINE_LAYER], - help: 'Layers of visual series', + help: i18n.translate('expressionXY.xyVis.layers.help', { + defaultMessage: 'Layers of visual series', + }), multi: true, }, curveType: { types: ['string'], options: [...Object.values(XYCurveTypes)], - help: i18n.translate('xpack.lens.xyChart.curveType.help', { + help: i18n.translate('expressionXY.xyVis.curveType.help', { defaultMessage: 'Define how curve type is rendered for a line chart', }), }, fillOpacity: { types: ['number'], - help: i18n.translate('xpack.lens.xyChart.fillOpacity.help', { + help: i18n.translate('expressionXY.xyVis.fillOpacity.help', { defaultMessage: 'Define the area chart fill opacity', }), }, hideEndzones: { types: ['boolean'], default: false, - help: i18n.translate('xpack.lens.xyChart.hideEndzones.help', { + help: i18n.translate('expressionXY.xyVis.hideEndzones.help', { defaultMessage: 'Hide endzone markers for partial data', }), }, valuesInLegend: { types: ['boolean'], default: false, - help: i18n.translate('xpack.lens.xyChart.valuesInLegend.help', { + help: i18n.translate('expressionXY.xyVis.valuesInLegend.help', { defaultMessage: 'Show values in legend', }), }, ariaLabel: { types: ['string'], - help: i18n.translate('xpack.lens.xyChart.ariaLabel.help', { + help: i18n.translate('expressionXY.xyVis.ariaLabel.help', { defaultMessage: 'Specifies the aria label of the xy chart', }), required: false, diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/y_axis_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/y_axis_config.ts index f6b30ac7120d6..e665fc2b8cea0 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/y_axis_config.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/y_axis_config.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import { i18n } from '@kbn/i18n'; import type { ExpressionFunctionDefinition } from '../../../../expressions/common'; import { FillStyles, IconPositions, LineStyles, YAxisModes, Y_CONFIG } from '../constants'; import { YConfig, YConfigResult } from '../types'; @@ -19,48 +20,68 @@ export const yAxisConfigFunction: ExpressionFunctionDefinition< name: Y_CONFIG, aliases: [], type: Y_CONFIG, - help: `Configure the behavior of a xy chart's y axis metric`, + help: i18n.translate('expressionXY.yConfig.help', { + defaultMessage: `Configure the behavior of a xy chart's y axis metric`, + }), inputTypes: ['null'], args: { forAccessor: { types: ['string'], - help: 'The accessor this configuration is for', + help: i18n.translate('expressionXY.yConfig.forAccessor.help', { + defaultMessage: 'The accessor this configuration is for', + }), }, axisMode: { types: ['string'], options: [...Object.values(YAxisModes)], - help: 'The axis mode of the metric', + help: i18n.translate('expressionXY.yConfig.axisMode.help', { + defaultMessage: 'The axis mode of the metric', + }), }, color: { types: ['string'], - help: 'The color of the series', + help: i18n.translate('expressionXY.yConfig.color.help', { + defaultMessage: 'The color of the series', + }), }, lineStyle: { types: ['string'], options: [...Object.values(LineStyles)], - help: 'The style of the reference line', + help: i18n.translate('expressionXY.yConfig.lineStyle.help', { + defaultMessage: 'The style of the reference line', + }), }, lineWidth: { types: ['number'], - help: 'The width of the reference line', + help: i18n.translate('expressionXY.yConfig.lineWidth.help', { + defaultMessage: 'The width of the reference line', + }), }, icon: { types: ['string'], - help: 'An optional icon used for reference lines', + help: i18n.translate('expressionXY.yConfig.icon.help', { + defaultMessage: 'An optional icon used for reference lines', + }), }, iconPosition: { types: ['string'], options: [...Object.values(IconPositions)], - help: 'The placement of the icon for the reference line', + help: i18n.translate('expressionXY.yConfig.iconPosition.help', { + defaultMessage: 'The placement of the icon for the reference line', + }), }, textVisibility: { types: ['boolean'], - help: 'Visibility of the label on the reference line', + help: i18n.translate('expressionXY.yConfig.textVisibility.help', { + defaultMessage: 'Visibility of the label on the reference line', + }), }, fill: { types: ['string'], options: [...Object.values(FillStyles)], - help: '', + help: i18n.translate('expressionXY.yConfig.fill.help', { + defaultMessage: 'Fill', + }), }, }, fn(input, args) { diff --git a/src/plugins/chart_expressions/expression_xy/public/components/legend_action_popover.tsx b/src/plugins/chart_expressions/expression_xy/public/components/legend_action_popover.tsx index 8adc2895fe734..a1ab31a993edf 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/legend_action_popover.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/legend_action_popover.tsx @@ -40,7 +40,7 @@ export const LegendActionPopover: React.FunctionComponent { - row.unifiedX = i18n.translate('xpack.lens.xyChart.emptyXLabel', { + row.unifiedX = i18n.translate('expressionXY.xyChart.emptyXLabel', { defaultMessage: '(empty)', }); }); diff --git a/src/plugins/chart_expressions/expression_xy/public/definitions/visualizations.ts b/src/plugins/chart_expressions/expression_xy/public/definitions/visualizations.ts index f958469b3b1d7..c7f083ee2895d 100644 --- a/src/plugins/chart_expressions/expression_xy/public/definitions/visualizations.ts +++ b/src/plugins/chart_expressions/expression_xy/public/definitions/visualizations.ts @@ -20,109 +20,16 @@ import { BarHorizontalStackedIcon, BarHorizontalPercentageIcon, } from '../icons'; -import { VisualizationType } from '../types'; -const groupLabelForBar = i18n.translate('xpack.lens.xyVisualization.barGroupLabel', { - defaultMessage: 'Bar', -}); - -const groupLabelForLineAndArea = i18n.translate('xpack.lens.xyVisualization.lineGroupLabel', { - defaultMessage: 'Line and area', -}); - -export const visualizationDefinitions: VisualizationType[] = [ - { - id: SeriesTypes.BAR, - icon: BarIcon, - label: i18n.translate('xpack.lens.xyVisualization.barLabel', { - defaultMessage: 'Bar vertical', - }), - groupLabel: groupLabelForBar, - sortPriority: 4, - }, - { - id: SeriesTypes.BAR_HORIZONTAL, - icon: BarHorizontalIcon, - label: i18n.translate('xpack.lens.xyVisualization.barHorizontalLabel', { - defaultMessage: 'H. Bar', - }), - fullLabel: i18n.translate('xpack.lens.xyVisualization.barHorizontalFullLabel', { - defaultMessage: 'Bar horizontal', - }), - groupLabel: groupLabelForBar, - }, - { - id: SeriesTypes.BAR_STACKED, - icon: BarStackedIcon, - label: i18n.translate('xpack.lens.xyVisualization.stackedBarLabel', { - defaultMessage: 'Bar vertical stacked', - }), - groupLabel: groupLabelForBar, - }, - { - id: SeriesTypes.BAR_PERCENTAGE_STACKED, - icon: BarPercentageIcon, - label: i18n.translate('xpack.lens.xyVisualization.stackedPercentageBarLabel', { - defaultMessage: 'Bar vertical percentage', - }), - groupLabel: groupLabelForBar, - }, - { - id: SeriesTypes.BAR_HORIZONTAL_STACKED, - icon: BarHorizontalStackedIcon, - label: i18n.translate('xpack.lens.xyVisualization.stackedBarHorizontalLabel', { - defaultMessage: 'H. Stacked bar', - }), - fullLabel: i18n.translate('xpack.lens.xyVisualization.stackedBarHorizontalFullLabel', { - defaultMessage: 'Bar horizontal stacked', - }), - groupLabel: groupLabelForBar, - }, - { - id: SeriesTypes.BAR_HORIZONTAL_PERCENTAGE_STACKED, - icon: BarHorizontalPercentageIcon, - label: i18n.translate('xpack.lens.xyVisualization.stackedPercentageBarHorizontalLabel', { - defaultMessage: 'H. Percentage bar', - }), - fullLabel: i18n.translate( - 'xpack.lens.xyVisualization.stackedPercentageBarHorizontalFullLabel', - { - defaultMessage: 'Bar horizontal percentage', - } - ), - groupLabel: groupLabelForBar, - }, - { - id: SeriesTypes.AREA, - icon: AreaIcon, - label: i18n.translate('xpack.lens.xyVisualization.areaLabel', { - defaultMessage: 'Area', - }), - groupLabel: groupLabelForLineAndArea, - }, - { - id: SeriesTypes.AREA_STACKED, - icon: AreaStackedIcon, - label: i18n.translate('xpack.lens.xyVisualization.stackedAreaLabel', { - defaultMessage: 'Area stacked', - }), - groupLabel: groupLabelForLineAndArea, - }, - { - id: SeriesTypes.AREA_PERCENTAGE_STACKED, - icon: AreaPercentageIcon, - label: i18n.translate('xpack.lens.xyVisualization.stackedPercentageAreaLabel', { - defaultMessage: 'Area percentage', - }), - groupLabel: groupLabelForLineAndArea, - }, - { - id: SeriesTypes.LINE, - icon: LineIcon, - label: i18n.translate('xpack.lens.xyVisualization.lineLabel', { - defaultMessage: 'Line', - }), - groupLabel: groupLabelForLineAndArea, - sortPriority: 2, - }, +export const visualizationDefinitions = [ + { id: SeriesTypes.BAR, icon: BarIcon }, + { id: SeriesTypes.BAR_HORIZONTAL, icon: BarHorizontalIcon }, + { id: SeriesTypes.BAR_STACKED, icon: BarStackedIcon }, + { id: SeriesTypes.BAR_PERCENTAGE_STACKED, icon: BarPercentageIcon }, + { id: SeriesTypes.BAR_HORIZONTAL_STACKED, icon: BarHorizontalStackedIcon }, + { id: SeriesTypes.BAR_HORIZONTAL_PERCENTAGE_STACKED, icon: BarHorizontalPercentageIcon }, + { id: SeriesTypes.AREA, icon: AreaIcon }, + { id: SeriesTypes.AREA_STACKED, icon: AreaStackedIcon }, + { id: SeriesTypes.AREA_PERCENTAGE_STACKED, icon: AreaPercentageIcon }, + { id: SeriesTypes.LINE, icon: LineIcon }, ]; diff --git a/src/plugins/chart_expressions/expression_xy/public/expression_renderers/xy_chart_renderer.tsx b/src/plugins/chart_expressions/expression_xy/public/expression_renderers/xy_chart_renderer.tsx index ae1b4acc2e464..b6b4229175c46 100644 --- a/src/plugins/chart_expressions/expression_xy/public/expression_renderers/xy_chart_renderer.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/expression_renderers/xy_chart_renderer.tsx @@ -38,7 +38,7 @@ export const getXyChartRenderer = ({ }: XyChartRendererDeps): ExpressionRenderDefinition => ({ name: 'xyVis', displayName: 'XY chart', - help: i18n.translate('xpack.lens.xyChart.renderer.help', { + help: i18n.translate('expressionXY.xyVis.renderer.help', { defaultMessage: 'X/Y chart renderer', }), validate: () => undefined, diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index ea47bedb67152..e3cf31ce9e6a2 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -644,9 +644,10 @@ "xpack.lens.section.workspaceLabel": "Espace de travail de visualisation", "xpack.lens.shared.chartValueLabelVisibilityLabel": "Étiquettes", "xpack.lens.shared.curveLabel": "Options visuelles", - "xpack.lens.shared.legend.filterForValueButtonAriaLabel": "Filtre pour la valeur", + "expressionXY.legend.filterForValueButtonAriaLabel": "Filtre pour la valeur", "xpack.lens.shared.legend.filterOptionsLegend": "{legendDataLabel}, options de filtre", - "xpack.lens.shared.legend.filterOutValueButtonAriaLabel": "Filtrer la valeur", + "expressionXY.legend.filterOptionsLegend": "{legendDataLabel}, options de filtre", + "expressionXY.legend.filterOutValueButtonAriaLabel": "Filtrer la valeur", "xpack.lens.shared.legendAlignmentLabel": "Alignement", "xpack.lens.shared.legendInsideAlignmentLabel": "Alignement", "xpack.lens.shared.legendInsideColumnsLabel": "Nombre de colonnes", @@ -732,80 +733,80 @@ "xpack.lens.xyChart.axisSide.left": "Gauche", "xpack.lens.xyChart.axisSide.right": "Droite", "xpack.lens.xyChart.axisSide.top": "Haut", - "xpack.lens.xyChart.axisTitlesSettings.help": "Afficher les titres des axes X et Y", + "expressionXY.xyVis.axisTitlesVisibilitySettings.help": "Afficher les titres des axes X et Y", "xpack.lens.xyChart.bottomAxisDisabledHelpText": "Ce paramètre s'applique uniquement lorsque l'axe du bas est activé.", "xpack.lens.xyChart.bottomAxisLabel": "Axe du bas", "xpack.lens.xyChart.boundaryError": "La limite inférieure doit être plus grande que la limite supérieure", "xpack.lens.xyChart.curveStyleLabel": "Courbes", - "xpack.lens.xyChart.curveType.help": "Définir de quelle façon le type de courbe est rendu pour un graphique linéaire", - "xpack.lens.xyChart.emptyXLabel": "(vide)", - "xpack.lens.xyChart.extentMode.help": "Mode d'extension", - "xpack.lens.xyChart.fillOpacity.help": "Définir l'opacité du remplissage du graphique en aires", + "expressionXY.xyVis.curveType.help": "Définir de quelle façon le type de courbe est rendu pour un graphique linéaire", + "expressionXY.xyChart.emptyXLabel": "(vide)", + "expressionXY.axisExtentConfig.extentMode.help": "Mode d'extension", + "expressionXY.xyVis.fillOpacity.help": "Définir l'opacité du remplissage du graphique en aires", "xpack.lens.xyChart.fillOpacityLabel": "Opacité de remplissage", - "xpack.lens.xyChart.fittingFunction.help": "Définir le mode de traitement des valeurs manquantes", - "xpack.lens.xyChart.floatingColumns.help": "Spécifie le nombre de colonnes lorsque la légende est affichée à l'intérieur du graphique.", + "expressionXY.xyVis.fittingFunction.help": "Définir le mode de traitement des valeurs manquantes", + "expressionXY.legendConfig.floatingColumns.help": "Spécifie le nombre de colonnes lorsque la légende est affichée à l'intérieur du graphique.", "xpack.lens.xyChart.Gridlines": "Quadrillage", - "xpack.lens.xyChart.gridlinesSettings.help": "Afficher le quadrillage des axes X et Y", - "xpack.lens.xyChart.help": "Graphique X/Y", - "xpack.lens.xyChart.hideEndzones.help": "Masquer les marqueurs de zone de fin pour les données partielles", - "xpack.lens.xyChart.horizontalAlignment.help": "Spécifie l'alignement horizontal de la légende lorsqu'elle est affichée à l'intérieur du graphique.", + "expressionXY.xyVis.gridlinesVisibilitySettings.help": "Afficher le quadrillage des axes X et Y", + "expressionXY.xyVis.help": "Graphique X/Y", + "expressionXY.xyVis.hideEndzones.help": "Masquer les marqueurs de zone de fin pour les données partielles", + "expressionXY.legendConfig.horizontalAlignment.help": "Spécifie l'alignement horizontal de la légende lorsqu'elle est affichée à l'intérieur du graphique.", "xpack.lens.xyChart.horizontalAxisLabel": "Axe horizontal", "xpack.lens.xyChart.inclusiveZero": "Les limites doivent inclure zéro.", - "xpack.lens.xyChart.isInside.help": "Spécifie si une légende se trouve à l'intérieur d'un graphique", - "xpack.lens.xyChart.isVisible.help": "Spécifie si la légende est visible ou non.", - "xpack.lens.xyChart.labelsOrientation.help": "Définit la rotation des étiquettes des axes", + "expressionXY.legendConfig.isInside.help": "Spécifie si une légende se trouve à l'intérieur d'un graphique", + "expressionXY.legendConfig.isVisible.help": "Spécifie si la légende est visible ou non.", + "expressionXY.xyVis.labelsOrientation.help": "Définit la rotation des étiquettes des axes", "xpack.lens.xyChart.leftAxisDisabledHelpText": "Ce paramètre s'applique uniquement lorsque l'axe de gauche est activé.", "xpack.lens.xyChart.leftAxisLabel": "Axe de gauche", - "xpack.lens.xyChart.legend.help": "Configurez la légende du graphique.", + "expressionXY.xyVis.legend.help": "Configurez la légende du graphique.", "xpack.lens.xyChart.legendLocation.inside": "Intérieur", "xpack.lens.xyChart.legendLocation.outside": "Extérieur", "xpack.lens.xyChart.legendVisibility.auto": "Auto", "xpack.lens.xyChart.legendVisibility.hide": "Masquer", "xpack.lens.xyChart.legendVisibility.show": "Afficher", "xpack.lens.xyChart.lowerBoundLabel": "Limite inférieure", - "xpack.lens.xyChart.maxLines.help": "Spécifie le nombre de lignes par élément de légende.", + "expressionXY.legendConfig.maxLines.help": "Spécifie le nombre de lignes par élément de légende.", "xpack.lens.xyChart.missingValuesLabel": "Valeurs manquantes", "xpack.lens.xyChart.missingValuesLabelHelpText": "Par défaut, Lens masque les blancs dans les données. Pour remplir le blanc, effectuez une sélection.", "xpack.lens.xyChart.nestUnderRoot": "Ensemble de données entier", - "xpack.lens.xyChart.position.help": "Spécifie la position de la légende.", - "xpack.lens.xyChart.renderer.help": "Outil de rendu de graphique X/Y", + "expressionXY.legendConfig.position.help": "Spécifie la position de la légende.", + "expressionXY.xyVis.renderer.help": "Outil de rendu de graphique X/Y", "xpack.lens.xyChart.rightAxisDisabledHelpText": "Ce paramètre s'applique uniquement lorsque l'axe de droite est activé.", "xpack.lens.xyChart.rightAxisLabel": "Axe de droite", "xpack.lens.xyChart.seriesColor.auto": "Auto", "xpack.lens.xyChart.seriesColor.label": "Couleur de la série", - "xpack.lens.xyChart.shouldTruncate.help": "Spécifie si les éléments de légende seront tronqués ou non", + "expressionXY.legendConfig.shouldTruncate.help": "Spécifie si les éléments de légende seront tronqués ou non", "xpack.lens.xyChart.showEnzones": "Afficher les marqueurs de données partielles", - "xpack.lens.xyChart.showSingleSeries.help": "Spécifie si une légende comportant une seule entrée doit être affichée", + "expressionXY.legendConfig.showSingleSeries.help": "Spécifie si une légende comportant une seule entrée doit être affichée", "xpack.lens.xyChart.splitSeries": "Répartir par", "xpack.lens.xyChart.tickLabels": "Étiquettes de graduation", - "xpack.lens.xyChart.tickLabelsSettings.help": "Afficher les étiquettes de graduation des axes X et Y", + "expressionXY.xyVis.tickLabelsVisibilitySettings.help": "Afficher les étiquettes de graduation des axes X et Y", "xpack.lens.xyChart.title.help": "Titre de l'axe", "xpack.lens.xyChart.topAxisDisabledHelpText": "Ce paramètre s'applique uniquement lorsque l'axe du haut est activé.", "xpack.lens.xyChart.topAxisLabel": "Axe du haut", "xpack.lens.xyChart.upperBoundLabel": "Limite supérieure", "xpack.lens.xyChart.valuesHistogramDisabledHelpText": "Ce paramètre ne peut pas être modifié dans les histogrammes.", - "xpack.lens.xyChart.valuesInLegend.help": "Afficher les valeurs dans la légende", + "expressionXY.xyVis.valuesInLegend.help": "Afficher les valeurs dans la légende", "xpack.lens.xyChart.valuesPercentageDisabledHelpText": "Ce paramètre ne peut pas être modifié dans les graphiques en aires à pourcentages.", "xpack.lens.xyChart.valuesStackedDisabledHelpText": "Ce paramètre ne peut pas être modifié dans les graphiques empilés ou les graphiques à barres à pourcentages", - "xpack.lens.xyChart.verticalAlignment.help": "Spécifie l'alignement vertical de la légende lorsqu'elle est affichée à l'intérieur du graphique.", + "expressionXY.legendConfig.verticalAlignment.help": "Spécifie l'alignement vertical de la légende lorsqu'elle est affichée à l'intérieur du graphique.", "xpack.lens.xyChart.verticalAxisLabel": "Axe vertical", - "xpack.lens.xyChart.xAxisGridlines.help": "Spécifie si le quadrillage de l'axe X est visible ou non.", - "xpack.lens.xyChart.xAxisLabelsOrientation.help": "Spécifie l'orientation des étiquettes de l'axe X.", - "xpack.lens.xyChart.xAxisTickLabels.help": "Spécifie si les étiquettes de graduation de l'axe X sont visibles ou non.", - "xpack.lens.xyChart.xAxisTitle.help": "Spécifie si le titre de l'axe X est visible ou non.", - "xpack.lens.xyChart.xTitle.help": "Titre de l'axe X", - "xpack.lens.xyChart.yLeftAxisgridlines.help": "Spécifie si le quadrillage de l'axe Y de gauche est visible ou non.", - "xpack.lens.xyChart.yLeftAxisLabelsOrientation.help": "Spécifie l'orientation des étiquettes de l'axe Y de gauche.", - "xpack.lens.xyChart.yLeftAxisTickLabels.help": "Spécifie si les étiquettes de graduation de l'axe Y de gauche sont visibles ou non.", - "xpack.lens.xyChart.yLeftAxisTitle.help": "Spécifie si le titre de l'axe Y de gauche est visible ou non.", - "xpack.lens.xyChart.yLeftExtent.help": "Portée de l'axe Y de gauche", - "xpack.lens.xyChart.yLeftTitle.help": "Titre de l'axe Y de gauche", - "xpack.lens.xyChart.yRightAxisgridlines.help": "Spécifie si le quadrillage de l'axe Y de droite est visible ou non.", - "xpack.lens.xyChart.yRightAxisLabelsOrientation.help": "Spécifie l'orientation des étiquettes de l'axe Y de droite.", - "xpack.lens.xyChart.yRightAxisTickLabels.help": "Spécifie si les étiquettes de graduation de l'axe Y de droite sont visibles ou non.", - "xpack.lens.xyChart.yRightAxisTitle.help": "Spécifie si le titre de l'axe Y de droite est visible ou non.", - "xpack.lens.xyChart.yRightExtent.help": "Portée de l'axe Y de droite", - "xpack.lens.xyChart.yRightTitle.help": "Titre de l'axe Y de droite", + "expressionXY.gridlinesConfig.x.help": "Spécifie si le quadrillage de l'axe X est visible ou non.", + "expressionXY.labelsOrientationConfig.x.help": "Spécifie l'orientation des étiquettes de l'axe X.", + "expressionXY.tickLabelsConfig.x.help": "Spécifie si les étiquettes de graduation de l'axe X sont visibles ou non.", + "expressionXY.axisTitlesVisibilityConfig.x.help": "Spécifie si le titre de l'axe X est visible ou non.", + "expressionXY.xyVis.xTitle.help": "Titre de l'axe X", + "expressionXY.gridlinesConfig.yLeft.help": "Spécifie si le quadrillage de l'axe Y de gauche est visible ou non.", + "expressionXY.labelsOrientationConfig.yLeft.help": "Spécifie l'orientation des étiquettes de l'axe Y de gauche.", + "expressionXY.tickLabelsConfig.yLeft.help": "Spécifie si les étiquettes de graduation de l'axe Y de gauche sont visibles ou non.", + "expressionXY.axisTitlesVisibilityConfig.yLeft.help": "Spécifie si le titre de l'axe Y de gauche est visible ou non.", + "expressionXY.xyVis.yLeftExtent.help": "Portée de l'axe Y de gauche", + "expressionXY.xyVis.yLeftTitle.help": "Titre de l'axe Y de gauche", + "expressionXY.gridlinesConfig.yRight.help": "Spécifie si le quadrillage de l'axe Y de droite est visible ou non.", + "expressionXY.labelsOrientationConfig.yRight.help": "Spécifie l'orientation des étiquettes de l'axe Y de droite.", + "expressionXY.tickLabelsConfig.yRight.help": "Spécifie si les étiquettes de graduation de l'axe Y de droite sont visibles ou non.", + "expressionXY.axisTitlesVisibilityConfig.yRight.help": "Spécifie si le titre de l'axe Y de droite est visible ou non.", + "expressionXY.xyVis.yRightExtent.help": "Portée de l'axe Y de droite", + "expressionXY.xyVis.yRightTitle.help": "Titre de l'axe Y de droite", "xpack.lens.xySuggestions.asPercentageTitle": "Pourcentage", "xpack.lens.xySuggestions.barChartTitle": "Graphique à barres", "xpack.lens.xySuggestions.dateSuggestion": "{yTitle} sur {xTitle}", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 8f96343daced1..7ae738987b26c 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -774,8 +774,11 @@ "xpack.lens.shared.chartValueLabelVisibilityLabel": "ラベル", "xpack.lens.shared.curveLabel": "視覚オプション", "xpack.lens.shared.legend.filterForValueButtonAriaLabel": "値でフィルター", + "expressionXY.legend.filterForValueButtonAriaLabel": "値でフィルター", + "expressionXY.legend.filterOptionsLegend": "{legendDataLabel}、フィルターオプション", "xpack.lens.shared.legend.filterOptionsLegend": "{legendDataLabel}、フィルターオプション", "xpack.lens.shared.legend.filterOutValueButtonAriaLabel": "値を除外", + "expressionXY.legend.filterOutValueButtonAriaLabel": "値を除外", "xpack.lens.shared.legendAlignmentLabel": "アラインメント", "xpack.lens.shared.legendInsideAlignmentLabel": "アラインメント", "xpack.lens.shared.legendInsideColumnsLabel": "列の数", @@ -874,34 +877,34 @@ "xpack.lens.xyChart.axisSide.left": "左", "xpack.lens.xyChart.axisSide.right": "右", "xpack.lens.xyChart.axisSide.top": "トップ", - "xpack.lens.xyChart.axisTitlesSettings.help": "xおよびy軸のタイトルを表示", + "expressionXY.xyVis.axisTitlesVisibilitySettings.help": "xおよびy軸のタイトルを表示", "xpack.lens.xyChart.bottomAxisDisabledHelpText": "この設定は、下の軸が有効であるときにのみ適用されます。", "xpack.lens.xyChart.bottomAxisLabel": "下の軸", "xpack.lens.xyChart.boundaryError": "下界は上界よりも大きくなければなりません", "xpack.lens.xyChart.curveStyleLabel": "曲線", - "xpack.lens.xyChart.curveType.help": "折れ線グラフで曲線タイプをレンダリングする方法を定義します", - "xpack.lens.xyChart.emptyXLabel": "(空)", - "xpack.lens.xyChart.extentMode.help": "範囲モード", - "xpack.lens.xyChart.fillOpacity.help": "エリアグラフの塗りつぶしの透明度を定義", + "expressionXY.xyVis.curveType.help": "折れ線グラフで曲線タイプをレンダリングする方法を定義します", + "expressionXY.xyChart.emptyXLabel": "(空)", + "expressionXY.axisExtentConfig.extentMode.help": "範囲モード", + "expressionXY.xyVis.fillOpacity.help": "エリアグラフの塗りつぶしの透明度を定義", "xpack.lens.xyChart.fillOpacityLabel": "塗りつぶしの透明度", - "xpack.lens.xyChart.fittingFunction.help": "欠測値の処理方法を定義", - "xpack.lens.xyChart.floatingColumns.help": "凡例がグラフ内に表示されるときに列数を指定します。", + "expressionXY.xyVis.fittingFunction.help": "欠測値の処理方法を定義", + "expressionXY.legendConfig.floatingColumns.help": "凡例がグラフ内に表示されるときに列数を指定します。", "xpack.lens.xyChart.Gridlines": "グリッド線", - "xpack.lens.xyChart.gridlinesSettings.help": "xおよびy軸のグリッド線を表示", - "xpack.lens.xyChart.help": "X/Y チャート", - "xpack.lens.xyChart.hideEndzones.help": "部分データの終了ゾーンマーカーを非表示", - "xpack.lens.xyChart.horizontalAlignment.help": "凡例がグラフ内に表示されるときに凡例の横の配置を指定します。", + "expressionXY.xyVis.gridlinesVisibilitySettings.help": "xおよびy軸のグリッド線を表示", + "expressionXY.xyVis.help": "X/Y チャート", + "expressionXY.xyVis.hideEndzones.help": "部分データの終了ゾーンマーカーを非表示", + "expressionXY.legendConfig.horizontalAlignment.help": "凡例がグラフ内に表示されるときに凡例の横の配置を指定します。", "xpack.lens.xyChart.horizontalAxisLabel": "横軸", "xpack.lens.xyChart.horizontalLeftAxisLabel": "横上軸", "xpack.lens.xyChart.horizontalRightAxisLabel": "横下軸", "xpack.lens.xyChart.inclusiveZero": "境界にはゼロを含める必要があります。", - "xpack.lens.xyChart.isInside.help": "凡例がグラフ内に表示されるかどうかを指定します", - "xpack.lens.xyChart.isVisible.help": "判例の表示・非表示を指定します。", - "xpack.lens.xyChart.labelsOrientation.help": "軸ラベルの回転を定義します", + "expressionXY.legendConfig.isInside.help": "凡例がグラフ内に表示されるかどうかを指定します", + "expressionXY.legendConfig.isVisible.help": "判例の表示・非表示を指定します。", + "expressionXY.xyVis.labelsOrientation.help": "軸ラベルの回転を定義します", "xpack.lens.xyChart.layerReferenceLineLabel": "基準線", "xpack.lens.xyChart.leftAxisDisabledHelpText": "この設定は、左の軸が有効であるときにのみ適用されます。", "xpack.lens.xyChart.leftAxisLabel": "左の軸", - "xpack.lens.xyChart.legend.help": "チャートの凡例を構成します。", + "expressionXY.xyVis.legend.help": "チャートの凡例を構成します。", "xpack.lens.xyChart.legendLocation.inside": "内部", "xpack.lens.xyChart.legendLocation.outside": "外側", "xpack.lens.xyChart.legendVisibility.auto": "自動", @@ -912,51 +915,51 @@ "xpack.lens.xyChart.markerPosition.below": "一番下", "xpack.lens.xyChart.markerPosition.left": "左", "xpack.lens.xyChart.markerPosition.right": "右", - "xpack.lens.xyChart.maxLines.help": "凡例項目ごとの行数を指定します。", + "expressionXY.legendConfig.maxLines.help": "凡例項目ごとの行数を指定します。", "xpack.lens.xyChart.missingValuesLabel": "欠測値", "xpack.lens.xyChart.missingValuesLabelHelpText": "デフォルトでは、Lensではデータのギャップが表示されません。ギャップを埋めるには、選択します。", "xpack.lens.xyChart.nestUnderRoot": "データセット全体", - "xpack.lens.xyChart.position.help": "凡例の配置を指定します。", - "xpack.lens.xyChart.renderer.help": "X/Y チャートを再レンダリング", + "expressionXY.legendConfig.position.help": "凡例の配置を指定します。", + "expressionXY.xyVis.renderer.help": "X/Y チャートを再レンダリング", "xpack.lens.xyChart.rightAxisDisabledHelpText": "この設定は、右の軸が有効であるときにのみ適用されます。", "xpack.lens.xyChart.rightAxisLabel": "右の軸", "xpack.lens.xyChart.seriesColor.auto": "自動", "xpack.lens.xyChart.seriesColor.label": "系列色", - "xpack.lens.xyChart.shouldTruncate.help": "凡例項目が切り捨てられるかどうかを指定します", + "expressionXY.legendConfig.shouldTruncate.help": "凡例項目が切り捨てられるかどうかを指定します", "xpack.lens.xyChart.showEnzones": "部分データマーカーを表示", - "xpack.lens.xyChart.showSingleSeries.help": "エントリが1件の凡例を表示するかどうかを指定します", + "expressionXY.legendConfig.showSingleSeries.help": "エントリが1件の凡例を表示するかどうかを指定します", "xpack.lens.xyChart.splitSeries": "内訳の基準", "xpack.lens.xyChart.tickLabels": "目盛ラベル", - "xpack.lens.xyChart.tickLabelsSettings.help": "xおよびy軸の目盛ラベルを表示", + "expressionXY.xyVis.tickLabelsVisibilitySettings.help": "xおよびy軸の目盛ラベルを表示", "xpack.lens.xyChart.title.help": "軸のタイトル", "xpack.lens.xyChart.topAxisDisabledHelpText": "この設定は、上の軸が有効であるときにのみ適用されます。", "xpack.lens.xyChart.topAxisLabel": "上の軸", "xpack.lens.xyChart.upperBoundLabel": "上界", "xpack.lens.xyChart.valuesHistogramDisabledHelpText": "この設定はヒストグラムで変更できません。", - "xpack.lens.xyChart.valuesInLegend.help": "凡例に値を表示", + "expressionXY.xyVis.valuesInLegend.help": "凡例に値を表示", "xpack.lens.xyChart.valuesPercentageDisabledHelpText": "この設定は割合エリアグラフで変更できません。", "xpack.lens.xyChart.valuesStackedDisabledHelpText": "この設定は積み上げ棒グラフまたは割合棒グラフで変更できません", - "xpack.lens.xyChart.verticalAlignment.help": "凡例がグラフ内に表示されるときに凡例の縦の配置を指定します。", + "expressionXY.legendConfig.verticalAlignment.help": "凡例がグラフ内に表示されるときに凡例の縦の配置を指定します。", "xpack.lens.xyChart.verticalAxisLabel": "縦軸", "xpack.lens.xyChart.verticalLeftAxisLabel": "縦左軸", "xpack.lens.xyChart.verticalRightAxisLabel": "縦右軸", - "xpack.lens.xyChart.xAxisGridlines.help": "x 軸のグリッド線を表示するかどうかを指定します。", - "xpack.lens.xyChart.xAxisLabelsOrientation.help": "x軸のラベルの向きを指定します。", - "xpack.lens.xyChart.xAxisTickLabels.help": "x軸の目盛ラベルを表示するかどうかを指定します。", - "xpack.lens.xyChart.xAxisTitle.help": "x軸のタイトルを表示するかどうかを指定します。", - "xpack.lens.xyChart.xTitle.help": "x軸のタイトル", - "xpack.lens.xyChart.yLeftAxisgridlines.help": "左y軸のグリッド線を表示するかどうかを指定します。", - "xpack.lens.xyChart.yLeftAxisLabelsOrientation.help": "左y軸のラベルの向きを指定します。", - "xpack.lens.xyChart.yLeftAxisTickLabels.help": "左y軸の目盛ラベルを表示するかどうかを指定します。", - "xpack.lens.xyChart.yLeftAxisTitle.help": "左y軸のタイトルを表示するかどうかを指定します。", - "xpack.lens.xyChart.yLeftExtent.help": "Y左軸範囲", - "xpack.lens.xyChart.yLeftTitle.help": "左y軸のタイトル", - "xpack.lens.xyChart.yRightAxisgridlines.help": "右y軸のグリッド線を表示するかどうかを指定します。", - "xpack.lens.xyChart.yRightAxisLabelsOrientation.help": "右y軸のラベルの向きを指定します。", - "xpack.lens.xyChart.yRightAxisTickLabels.help": "右y軸の目盛ラベルを表示するかどうかを指定します。", - "xpack.lens.xyChart.yRightAxisTitle.help": "右y軸のタイトルを表示するかどうかを指定します。", - "xpack.lens.xyChart.yRightExtent.help": "Y右軸範囲", - "xpack.lens.xyChart.yRightTitle.help": "右 y 軸のタイトル", + "expressionXY.gridlinesConfig.x.help": "x 軸のグリッド線を表示するかどうかを指定します。", + "expressionXY.labelsOrientationConfig.x.help": "x軸のラベルの向きを指定します。", + "expressionXY.tickLabelsConfig.x.help": "x軸の目盛ラベルを表示するかどうかを指定します。", + "expressionXY.axisTitlesVisibilityConfig.x.help": "x軸のタイトルを表示するかどうかを指定します。", + "expressionXY.xyVis.xTitle.help": "x軸のタイトル", + "expressionXY.gridlinesConfig.yLeft.help": "左y軸のグリッド線を表示するかどうかを指定します。", + "expressionXY.labelsOrientationConfig.yLeft.help": "左y軸のラベルの向きを指定します。", + "expressionXY.tickLabelsConfig.yLeft.help": "左y軸の目盛ラベルを表示するかどうかを指定します。", + "expressionXY.axisTitlesVisibilityConfig.yLeft.help": "左y軸のタイトルを表示するかどうかを指定します。", + "expressionXY.xyVis.yLeftExtent.help": "Y左軸範囲", + "expressionXY.xyVis.yLeftTitle.help": "左y軸のタイトル", + "expressionXY.gridlinesConfig.yRight.help": "右y軸のグリッド線を表示するかどうかを指定します。", + "expressionXY.labelsOrientationConfig.yRight.help": "右y軸のラベルの向きを指定します。", + "expressionXY.tickLabelsConfig.yRight.help": "右y軸の目盛ラベルを表示するかどうかを指定します。", + "expressionXY.axisTitlesVisibilityConfig.yRight.help": "右y軸のタイトルを表示するかどうかを指定します。", + "expressionXY.xyVis.yRightExtent.help": "Y右軸範囲", + "expressionXY.xyVis.yRightTitle.help": "右 y 軸のタイトル", "xpack.lens.xySuggestions.asPercentageTitle": "割合(%)", "xpack.lens.xySuggestions.barChartTitle": "棒グラフ", "xpack.lens.xySuggestions.dateSuggestion": "{xTitle}の上の {yTitle}", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index f49f943fbd2e3..ee08500204f49 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -779,9 +779,10 @@ "xpack.lens.shared.axisNameLabel": "轴标题", "xpack.lens.shared.chartValueLabelVisibilityLabel": "标签", "xpack.lens.shared.curveLabel": "视觉选项", - "xpack.lens.shared.legend.filterForValueButtonAriaLabel": "筛留值", + "expressionXY.legend.filterForValueButtonAriaLabel": "筛留值", + "expressionXY.legend.filterOptionsLegend": "{legendDataLabel}, 筛选选项", "xpack.lens.shared.legend.filterOptionsLegend": "{legendDataLabel}, 筛选选项", - "xpack.lens.shared.legend.filterOutValueButtonAriaLabel": "筛除值", + "expressionXY.legend.filterOutValueButtonAriaLabel": "筛除值", "xpack.lens.shared.legendAlignmentLabel": "对齐方式", "xpack.lens.shared.legendInsideAlignmentLabel": "对齐方式", "xpack.lens.shared.legendInsideColumnsLabel": "列数目", @@ -880,34 +881,34 @@ "xpack.lens.xyChart.axisSide.left": "左", "xpack.lens.xyChart.axisSide.right": "右", "xpack.lens.xyChart.axisSide.top": "顶部", - "xpack.lens.xyChart.axisTitlesSettings.help": "显示 x 和 y 轴标题", + "expressionXY.xyVis.axisTitlesVisibilitySettings.help": "显示 x 和 y 轴标题", "xpack.lens.xyChart.bottomAxisDisabledHelpText": "此设置仅在启用底轴时应用。", "xpack.lens.xyChart.bottomAxisLabel": "底轴", "xpack.lens.xyChart.boundaryError": "下边界必须大于上边界", "xpack.lens.xyChart.curveStyleLabel": "曲线", - "xpack.lens.xyChart.curveType.help": "定义为折线图渲染曲线类型的方式", - "xpack.lens.xyChart.emptyXLabel": "(空)", - "xpack.lens.xyChart.extentMode.help": "范围模式", - "xpack.lens.xyChart.fillOpacity.help": "定义面积图填充透明度", + "expressionXY.xyVis.curveType.help": "定义为折线图渲染曲线类型的方式", + "expressionXY.xyChart.emptyXLabel": "(空)", + "expressionXY.axisExtentConfig.extentMode.help": "范围模式", + "expressionXY.xyVis.fillOpacity.help": "定义面积图填充透明度", "xpack.lens.xyChart.fillOpacityLabel": "填充透明度", - "xpack.lens.xyChart.fittingFunction.help": "定义处理缺失值的方式", - "xpack.lens.xyChart.floatingColumns.help": "指定图例显示在图表内时的列数。", + "expressionXY.xyVis.fittingFunction.help": "定义处理缺失值的方式", + "expressionXY.legendConfig.floatingColumns.help": "指定图例显示在图表内时的列数。", "xpack.lens.xyChart.Gridlines": "网格线", - "xpack.lens.xyChart.gridlinesSettings.help": "显示 x 和 y 轴网格线", - "xpack.lens.xyChart.help": "X/Y 图表", - "xpack.lens.xyChart.hideEndzones.help": "隐藏部分数据的末日区域标记", - "xpack.lens.xyChart.horizontalAlignment.help": "指定图例显示在图表内时水平对齐。", + "expressionXY.xyVis.gridlinesVisibilitySettings.help": "显示 x 和 y 轴网格线", + "expressionXY.xyVis.help": "X/Y 图表", + "expressionXY.xyVis.hideEndzones.help": "隐藏部分数据的末日区域标记", + "expressionXY.legendConfig.horizontalAlignment.help": "指定图例显示在图表内时水平对齐。", "xpack.lens.xyChart.horizontalAxisLabel": "水平轴", "xpack.lens.xyChart.horizontalLeftAxisLabel": "水平顶轴", "xpack.lens.xyChart.horizontalRightAxisLabel": "水平底轴", "xpack.lens.xyChart.inclusiveZero": "边界必须包括零。", - "xpack.lens.xyChart.isInside.help": "指定图例是否在图表内", - "xpack.lens.xyChart.isVisible.help": "指定图例是否可见。", - "xpack.lens.xyChart.labelsOrientation.help": "定义轴标签的旋转", + "expressionXY.legendConfig.isInside.help": "指定图例是否在图表内", + "expressionXY.legendConfig.isVisible.help": "指定图例是否可见。", + "expressionXY.xyVis.labelsOrientation.help": "定义轴标签的旋转", "xpack.lens.xyChart.layerReferenceLineLabel": "参考线", "xpack.lens.xyChart.leftAxisDisabledHelpText": "此设置仅在启用左轴时应用。", "xpack.lens.xyChart.leftAxisLabel": "左轴", - "xpack.lens.xyChart.legend.help": "配置图表图例。", + "expressionXY.xyVis.legend.help": "配置图表图例。", "xpack.lens.xyChart.legendLocation.inside": "内部", "xpack.lens.xyChart.legendLocation.outside": "外部", "xpack.lens.xyChart.legendVisibility.auto": "自动", @@ -918,51 +919,51 @@ "xpack.lens.xyChart.markerPosition.below": "底部", "xpack.lens.xyChart.markerPosition.left": "左", "xpack.lens.xyChart.markerPosition.right": "右", - "xpack.lens.xyChart.maxLines.help": "指定每个图例项的行数。", + "expressionXY.legendConfig.maxLines.help": "指定每个图例项的行数。", "xpack.lens.xyChart.missingValuesLabel": "缺少的值", "xpack.lens.xyChart.missingValuesLabelHelpText": "默认情况下,Lens 隐藏数据中的缺口。要填充缺口,请进行选择。", "xpack.lens.xyChart.nestUnderRoot": "整个数据集", - "xpack.lens.xyChart.position.help": "指定图例位置。", - "xpack.lens.xyChart.renderer.help": "X/Y 图表呈现器", + "expressionXY.legendConfig.position.help": "指定图例位置。", + "expressionXY.xyVis.renderer.help": "X/Y 图表呈现器", "xpack.lens.xyChart.rightAxisDisabledHelpText": "此设置仅在启用右轴时应用。", "xpack.lens.xyChart.rightAxisLabel": "右轴", "xpack.lens.xyChart.seriesColor.auto": "自动", "xpack.lens.xyChart.seriesColor.label": "系列颜色", - "xpack.lens.xyChart.shouldTruncate.help": "指定是否将截断图例项", + "expressionXY.legendConfig.shouldTruncate.help": "指定是否将截断图例项", "xpack.lens.xyChart.showEnzones": "显示部分数据标记", - "xpack.lens.xyChart.showSingleSeries.help": "指定是否应显示只包含一个条目的图例", + "expressionXY.legendConfig.showSingleSeries.help": "指定是否应显示只包含一个条目的图例", "xpack.lens.xyChart.splitSeries": "细分方式", "xpack.lens.xyChart.tickLabels": "刻度标签", - "xpack.lens.xyChart.tickLabelsSettings.help": "显示 x 和 y 轴刻度标签", + "expressionXY.xyVis.tickLabelsVisibilitySettings.help": "显示 x 和 y 轴刻度标签", "xpack.lens.xyChart.title.help": "轴标题", "xpack.lens.xyChart.topAxisDisabledHelpText": "此设置仅在启用顶轴时应用。", "xpack.lens.xyChart.topAxisLabel": "顶轴", "xpack.lens.xyChart.upperBoundLabel": "上边界", "xpack.lens.xyChart.valuesHistogramDisabledHelpText": "不能在直方图上更改此设置。", - "xpack.lens.xyChart.valuesInLegend.help": "在图例中显示值", + "expressionXY.xyVis.valuesInLegend.help": "在图例中显示值", "xpack.lens.xyChart.valuesPercentageDisabledHelpText": "不能在百分比面积图上更改此设置。", "xpack.lens.xyChart.valuesStackedDisabledHelpText": "不能在堆积图或百分比条形图上更改此设置", - "xpack.lens.xyChart.verticalAlignment.help": "指定图例显示在图表内时垂直对齐。", + "expressionXY.legendConfig.verticalAlignment.help": "指定图例显示在图表内时垂直对齐。", "xpack.lens.xyChart.verticalAxisLabel": "垂直轴", "xpack.lens.xyChart.verticalLeftAxisLabel": "垂直左轴", "xpack.lens.xyChart.verticalRightAxisLabel": "垂直右轴", - "xpack.lens.xyChart.xAxisGridlines.help": "指定 x 轴的网格线是否可见。", - "xpack.lens.xyChart.xAxisLabelsOrientation.help": "指定 x 轴的标签方向。", - "xpack.lens.xyChart.xAxisTickLabels.help": "指定 x 轴的刻度标签是否可见。", - "xpack.lens.xyChart.xAxisTitle.help": "指定 x 轴的标题是否可见。", - "xpack.lens.xyChart.xTitle.help": "X 轴标题", - "xpack.lens.xyChart.yLeftAxisgridlines.help": "指定左侧 y 轴的网格线是否可见。", - "xpack.lens.xyChart.yLeftAxisLabelsOrientation.help": "指定左 y 轴的标签方向。", - "xpack.lens.xyChart.yLeftAxisTickLabels.help": "指定左侧 y 轴的刻度标签是否可见。", - "xpack.lens.xyChart.yLeftAxisTitle.help": "指定左侧 y 轴的标题是否可见。", - "xpack.lens.xyChart.yLeftExtent.help": "左侧 Y 轴范围", - "xpack.lens.xyChart.yLeftTitle.help": "左侧 Y 轴标题", - "xpack.lens.xyChart.yRightAxisgridlines.help": "指定右侧 y 轴的网格线是否可见。", - "xpack.lens.xyChart.yRightAxisLabelsOrientation.help": "指定右 y 轴的标签方向。", - "xpack.lens.xyChart.yRightAxisTickLabels.help": "指定右侧 y 轴的刻度标签是否可见。", - "xpack.lens.xyChart.yRightAxisTitle.help": "指定右侧 y 轴的标题是否可见。", - "xpack.lens.xyChart.yRightExtent.help": "右侧 Y 轴范围", - "xpack.lens.xyChart.yRightTitle.help": "右侧 Y 轴标题", + "expressionXY.gridlinesConfig.x.help": "指定 x 轴的网格线是否可见。", + "expressionXY.labelsOrientationConfig.x.help": "指定 x 轴的标签方向。", + "expressionXY.tickLabelsConfig.x.help": "指定 x 轴的刻度标签是否可见。", + "expressionXY.axisTitlesVisibilityConfig.x.help": "指定 x 轴的标题是否可见。", + "expressionXY.xyVis.xTitle.help": "X 轴标题", + "expressionXY.gridlinesConfig.yLeft.help": "指定左侧 y 轴的网格线是否可见。", + "expressionXY.labelsOrientationConfig.yLeft.help": "指定左 y 轴的标签方向。", + "expressionXY.tickLabelsConfig.yLeft.help": "指定左侧 y 轴的刻度标签是否可见。", + "expressionXY.axisTitlesVisibilityConfig.yLeft.help": "指定左侧 y 轴的标题是否可见。", + "expressionXY.xyVis.yLeftExtent.help": "左侧 Y 轴范围", + "expressionXY.xyVis.yLeftTitle.help": "左侧 Y 轴标题", + "expressionXY.gridlinesConfig.yRight.help": "指定右侧 y 轴的网格线是否可见。", + "expressionXY.labelsOrientationConfig.yRight.help": "指定右 y 轴的标签方向。", + "expressionXY.tickLabelsConfig.yRight.help": "指定右侧 y 轴的刻度标签是否可见。", + "expressionXY.axisTitlesVisibilityConfig.yRight.help": "指定右侧 y 轴的标题是否可见。", + "expressionXY.xyVis.yRightExtent.help": "右侧 Y 轴范围", + "expressionXY.xyVis.yRightTitle.help": "右侧 Y 轴标题", "xpack.lens.xySuggestions.asPercentageTitle": "百分比", "xpack.lens.xySuggestions.barChartTitle": "条形图", "xpack.lens.xySuggestions.dateSuggestion": "{yTitle} / {xTitle}", From 902972de1307bc216cd3172e542a16c704c7aef2 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Thu, 17 Mar 2022 10:51:25 +0200 Subject: [PATCH 037/133] Fixed "Visualize App ... cleans filters and query" test. cleans filters and query --- x-pack/test/functional/apps/lens/persistent_context.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/test/functional/apps/lens/persistent_context.ts b/x-pack/test/functional/apps/lens/persistent_context.ts index 29c850ea553d3..445caa1abbec2 100644 --- a/x-pack/test/functional/apps/lens/persistent_context.ts +++ b/x-pack/test/functional/apps/lens/persistent_context.ts @@ -120,7 +120,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.visualize.clickVisType('lens'); await PageObjects.lens.waitForEmptyWorkspace(); await PageObjects.lens.switchToVisualization('lnsMetric'); - await PageObjects.lens.dragFieldToWorkspace('@timestamp'); + await PageObjects.lens.dragFieldToWorkspace('@timestamp', 'mtrVis'); const timePickerValues = await PageObjects.timePicker.getTimeConfigAsAbsoluteTimes(); expect(timePickerValues.start).to.eql(PageObjects.timePicker.defaultStartTime); From 05b0f8d0ea6f026405808fbacb1c4c445297c4f4 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Thu, 17 Mar 2022 11:09:47 +0200 Subject: [PATCH 038/133] Fixed "lens disable auto-apply tests" test. --- x-pack/test/functional/apps/lens/disable_auto_apply.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/test/functional/apps/lens/disable_auto_apply.ts b/x-pack/test/functional/apps/lens/disable_auto_apply.ts index 3660de10ecd47..e280bbd148493 100644 --- a/x-pack/test/functional/apps/lens/disable_auto_apply.ts +++ b/x-pack/test/functional/apps/lens/disable_auto_apply.ts @@ -83,7 +83,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.lens.applyChanges(); - await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.waitForVisualization('xyVisChart'); }); it('should hide suggestions when a change is made', async () => { From cae1e75a976a52b2706931d55ae1e13de4cce395 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Thu, 17 Mar 2022 11:29:44 +0200 Subject: [PATCH 039/133] Updated dashboard tests. --- .../__snapshots__/xy_chart.test.tsx.snap | 3284 ++++++++--------- .../public/components/xy_chart.tsx | 736 ++-- .../xy_chart_renderer.tsx | 32 +- .../apps/dashboard/dashboard_lens_by_value.ts | 2 +- 4 files changed, 1992 insertions(+), 2062 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/public/components/__snapshots__/xy_chart.test.tsx.snap b/src/plugins/chart_expressions/expression_xy/public/components/__snapshots__/xy_chart.test.tsx.snap index 23bc1fddf00a8..210b02f984284 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/__snapshots__/xy_chart.test.tsx.snap +++ b/src/plugins/chart_expressions/expression_xy/public/components/__snapshots__/xy_chart.test.tsx.snap @@ -1,1775 +1,1705 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`XYChart component it renders area 1`] = ` -
- - - + - + - - + + - + - -
+ } + color={[Function]} + data={ + Array [ + Object { + "a": 1, + "b": 2, + "c": "I", + "d": "Foo", + }, + Object { + "a": 1, + "b": 5, + "c": "J", + "d": "Bar", + }, + ] + } + enableHistogramMode={false} + fit={ + Object { + "type": "none", + } + } + groupId="left" + id="d-b" + key="0-1" + lineSeriesStyle={ + Object { + "point": Object { + "radius": 5, + "visible": false, + }, + } + } + name={[Function]} + splitSeriesAccessors={ + Array [ + "d", + ] + } + stackAccessors={Array []} + timeZone="UTC" + xAccessor="c" + xScaleType="linear" + yAccessors={ + Array [ + "b", + ] + } + yScaleType="linear" + /> + `; exports[`XYChart component it renders bar 1`] = ` -
- - - + - + - - + + - - -
+ } + enableHistogramMode={false} + groupId="left" + id="d-a" + key="0-0" + lineSeriesStyle={ + Object { + "point": Object { + "radius": 5, + "visible": false, + }, + } + } + name={[Function]} + splitSeriesAccessors={ + Array [ + "d", + ] + } + stackAccessors={Array []} + timeZone="UTC" + xAccessor="c" + xScaleType="linear" + yAccessors={ + Array [ + "a", + ] + } + yScaleType="linear" + /> + + `; exports[`XYChart component it renders horizontal bar 1`] = ` -
- - - + - + - - + + - - -
+ } + enableHistogramMode={false} + groupId="left" + id="d-a" + key="0-0" + lineSeriesStyle={ + Object { + "point": Object { + "radius": 5, + "visible": false, + }, + } + } + name={[Function]} + splitSeriesAccessors={ + Array [ + "d", + ] + } + stackAccessors={Array []} + timeZone="UTC" + xAccessor="c" + xScaleType="linear" + yAccessors={ + Array [ + "a", + ] + } + yScaleType="linear" + /> + + `; exports[`XYChart component it renders line 1`] = ` -
- - - + - + - - + + - + - -
+ } + color={[Function]} + data={ + Array [ + Object { + "a": 1, + "b": 2, + "c": "I", + "d": "Foo", + }, + Object { + "a": 1, + "b": 5, + "c": "J", + "d": "Bar", + }, + ] + } + enableHistogramMode={false} + fit={ + Object { + "type": "none", + } + } + groupId="left" + id="d-b" + key="0-1" + lineSeriesStyle={ + Object { + "point": Object { + "radius": 5, + "visible": false, + }, + } + } + name={[Function]} + splitSeriesAccessors={ + Array [ + "d", + ] + } + stackAccessors={Array []} + timeZone="UTC" + xAccessor="c" + xScaleType="ordinal" + yAccessors={ + Array [ + "b", + ] + } + yScaleType="linear" + /> + `; exports[`XYChart component it renders stacked area 1`] = ` -
- - - + - + - - + + - + - -
+ } + color={[Function]} + data={ + Array [ + Object { + "a": 1, + "b": 2, + "c": "I", + "d": "Foo", + }, + Object { + "a": 1, + "b": 5, + "c": "J", + "d": "Bar", + }, + ] + } + enableHistogramMode={false} + fit={ + Object { + "type": "none", + } + } + groupId="left" + id="d-b" + key="0-1" + lineSeriesStyle={ + Object { + "point": Object { + "radius": 5, + "visible": false, + }, + } + } + name={[Function]} + splitSeriesAccessors={ + Array [ + "d", + ] + } + stackAccessors={ + Array [ + "c", + ] + } + timeZone="UTC" + xAccessor="c" + xScaleType="ordinal" + yAccessors={ + Array [ + "b", + ] + } + yScaleType="linear" + /> + `; exports[`XYChart component it renders stacked bar 1`] = ` -
- - - + - + - - + + - - -
+ } + enableHistogramMode={false} + groupId="left" + id="d-a" + key="0-0" + lineSeriesStyle={ + Object { + "point": Object { + "radius": 5, + "visible": false, + }, + } + } + name={[Function]} + splitSeriesAccessors={ + Array [ + "d", + ] + } + stackAccessors={ + Array [ + "c", + ] + } + timeZone="UTC" + xAccessor="c" + xScaleType="ordinal" + yAccessors={ + Array [ + "a", + ] + } + yScaleType="linear" + /> + + `; exports[`XYChart component it renders stacked horizontal bar 1`] = ` -
- - - + - + - - + + - - -
+ } + enableHistogramMode={false} + groupId="left" + id="d-a" + key="0-0" + lineSeriesStyle={ + Object { + "point": Object { + "radius": 5, + "visible": false, + }, + } + } + name={[Function]} + splitSeriesAccessors={ + Array [ + "d", + ] + } + stackAccessors={ + Array [ + "c", + ] + } + timeZone="UTC" + xAccessor="c" + xScaleType="ordinal" + yAccessors={ + Array [ + "a", + ] + } + yScaleType="linear" + /> + + `; diff --git a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx index 6ffb5c08ce2b7..a1350eb6a226e 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx @@ -521,388 +521,386 @@ export function XYChart({ }; return ( -
- - safeXAccessorLabelRenderer(d.value), - }} - allowBrushingLastHistogramBin={isTimeViz} - rotation={shouldRotate ? 90 : 0} - xDomain={xDomain} - onBrushEnd={interactive ? (brushHandler as BrushEndListener) : undefined} - onElementClick={interactive ? clickHandler : undefined} - legendAction={ - interactive - ? getLegendAction( - filteredLayers, - data.tables, - onClickValue, - formatFactory, - layersAlreadyFormatted - ) - : undefined - } - showLegendExtra={isHistogramViz && valuesInLegend} - ariaLabel={args.ariaLabel} - ariaUseDefaultSummary={!args.ariaLabel} + + safeXAccessorLabelRenderer(d.value), + }} + allowBrushingLastHistogramBin={isTimeViz} + rotation={shouldRotate ? 90 : 0} + xDomain={xDomain} + onBrushEnd={interactive ? (brushHandler as BrushEndListener) : undefined} + onElementClick={interactive ? clickHandler : undefined} + legendAction={ + interactive + ? getLegendAction( + filteredLayers, + data.tables, + onClickValue, + formatFactory, + layersAlreadyFormatted + ) + : undefined + } + showLegendExtra={isHistogramViz && valuesInLegend} + ariaLabel={args.ariaLabel} + ariaUseDefaultSummary={!args.ariaLabel} + /> + + safeXAccessorLabelRenderer(d)} + style={xAxisStyle} + timeAxisLayerCount={shouldUseNewTimeAxis ? 3 : 0} + /> + + {yAxesConfiguration.map((axis) => { + return ( + axis.formatter?.convert(d) || ''} + style={getYAxesStyle(axis.groupId as 'left' | 'right')} + domain={getYAxisDomain(axis)} + ticks={5} + /> + ); + })} + + {!hideEndzones && ( + + layer.isHistogram && + (layer.seriesType.includes('stacked') || !layer.splitAccessor) && + (layer.seriesType.includes('stacked') || + !layer.seriesType.includes('bar') || + !chartHasMoreThanOneBarSeries) + )} /> + )} + + {filteredLayers.flatMap((layer, layerIndex) => + layer.accessors.map((accessor, accessorIndex) => { + const { + splitAccessor, + seriesType, + accessors, + xAccessor, + layerId, + columnToLabel, + yScaleType, + xScaleType, + isHistogram, + palette, + } = layer; + const columnToLabelMap: Record = columnToLabel + ? JSON.parse(columnToLabel) + : {}; - safeXAccessorLabelRenderer(d)} - style={xAxisStyle} - timeAxisLayerCount={shouldUseNewTimeAxis ? 3 : 0} - /> + const table = data.tables[layerId]; - {yAxesConfiguration.map((axis) => { - return ( - axis.formatter?.convert(d) || ''} - style={getYAxesStyle(axis.groupId as 'left' | 'right')} - domain={getYAxisDomain(axis)} - ticks={5} - /> - ); - })} - - {!hideEndzones && ( - - layer.isHistogram && - (layer.seriesType.includes('stacked') || !layer.splitAccessor) && - (layer.seriesType.includes('stacked') || - !layer.seriesType.includes('bar') || - !chartHasMoreThanOneBarSeries) - )} - /> - )} - - {filteredLayers.flatMap((layer, layerIndex) => - layer.accessors.map((accessor, accessorIndex) => { - const { - splitAccessor, - seriesType, - accessors, - xAccessor, - layerId, - columnToLabel, - yScaleType, - xScaleType, - isHistogram, - palette, - } = layer; - const columnToLabelMap: Record = columnToLabel - ? JSON.parse(columnToLabel) - : {}; - - const table = data.tables[layerId]; - - const formatterPerColumn = new Map(); - for (const column of table.columns) { - formatterPerColumn.set(column, formatFactory(column.meta.params)); - } + const formatterPerColumn = new Map(); + for (const column of table.columns) { + formatterPerColumn.set(column, formatFactory(column.meta.params)); + } - // what if row values are not primitive? That is the case of, for instance, Ranges - // remaps them to their serialized version with the formatHint metadata - // In order to do it we need to make a copy of the table as the raw one is required for more features (filters, etc...) later on - const tableConverted: Datatable = { - ...table, - rows: table.rows.map((row: DatatableRow) => { - const newRow = { ...row }; - for (const column of table.columns) { - const record = newRow[column.id]; - if ( - record != null && - // pre-format values for ordinal x axes because there can only be a single x axis formatter on chart level - (!isPrimitive(record) || (column.id === xAccessor && xScaleType === 'ordinal')) - ) { - newRow[column.id] = formatterPerColumn.get(column)!.convert(record); - } - } - return newRow; - }), - }; - - // save the id of the layer with the custom table - table.columns.reduce>( - (alreadyFormatted: Record, { id }) => { - if (alreadyFormatted[id]) { - return alreadyFormatted; + // what if row values are not primitive? That is the case of, for instance, Ranges + // remaps them to their serialized version with the formatHint metadata + // In order to do it we need to make a copy of the table as the raw one is required for more features (filters, etc...) later on + const tableConverted: Datatable = { + ...table, + rows: table.rows.map((row: DatatableRow) => { + const newRow = { ...row }; + for (const column of table.columns) { + const record = newRow[column.id]; + if ( + record != null && + // pre-format values for ordinal x axes because there can only be a single x axis formatter on chart level + (!isPrimitive(record) || (column.id === xAccessor && xScaleType === 'ordinal')) + ) { + newRow[column.id] = formatterPerColumn.get(column)!.convert(record); } - alreadyFormatted[id] = table.rows.some( - (row, i) => row[id] !== tableConverted.rows[i][id] - ); + } + return newRow; + }), + }; + + // save the id of the layer with the custom table + table.columns.reduce>( + (alreadyFormatted: Record, { id }) => { + if (alreadyFormatted[id]) { return alreadyFormatted; - }, - layersAlreadyFormatted - ); - - const isStacked = seriesType.includes('stacked'); - const isPercentage = seriesType.includes('percentage'); - const isBarChart = seriesType.includes('bar'); - const enableHistogramMode = - isHistogram && - (isStacked || !splitAccessor) && - (isStacked || !isBarChart || !chartHasMoreThanOneBarSeries); - - // For date histogram chart type, we're getting the rows that represent intervals without data. - // To not display them in the legend, they need to be filtered out. - const rows = tableConverted.rows.filter( - (row) => - !(xAccessor && typeof row[xAccessor] === 'undefined') && - !( - splitAccessor && - typeof row[splitAccessor] === 'undefined' && - typeof row[accessor] === 'undefined' - ) - ); - - if (!xAccessor) { - rows.forEach((row) => { - row.unifiedX = i18n.translate('expressionXY.xyChart.emptyXLabel', { - defaultMessage: '(empty)', - }); + } + alreadyFormatted[id] = table.rows.some( + (row, i) => row[id] !== tableConverted.rows[i][id] + ); + return alreadyFormatted; + }, + layersAlreadyFormatted + ); + + const isStacked = seriesType.includes('stacked'); + const isPercentage = seriesType.includes('percentage'); + const isBarChart = seriesType.includes('bar'); + const enableHistogramMode = + isHistogram && + (isStacked || !splitAccessor) && + (isStacked || !isBarChart || !chartHasMoreThanOneBarSeries); + + // For date histogram chart type, we're getting the rows that represent intervals without data. + // To not display them in the legend, they need to be filtered out. + const rows = tableConverted.rows.filter( + (row) => + !(xAccessor && typeof row[xAccessor] === 'undefined') && + !( + splitAccessor && + typeof row[splitAccessor] === 'undefined' && + typeof row[accessor] === 'undefined' + ) + ); + + if (!xAccessor) { + rows.forEach((row) => { + row.unifiedX = i18n.translate('expressionXY.xyChart.emptyXLabel', { + defaultMessage: '(empty)', }); - } + }); + } - const yAxis = yAxesConfiguration.find((axisConfiguration) => - axisConfiguration.series.find((currentSeries) => currentSeries.accessor === accessor) - ); - - const formatter = table?.columns.find((column) => column.id === accessor)?.meta?.params; - const splitHint = table.columns.find((col) => col.id === splitAccessor)?.meta?.params; - const splitFormatter = formatFactory(splitHint); - - const seriesProps: SeriesSpec = { - splitSeriesAccessors: splitAccessor ? [splitAccessor] : [], - stackAccessors: isStacked ? [xAccessor as string] : [], - id: `${splitAccessor}-${accessor}`, - xAccessor: xAccessor || 'unifiedX', - yAccessors: [accessor], - data: rows, - xScaleType: xAccessor ? xScaleType : 'ordinal', - yScaleType: - formatter?.id === 'bytes' && yScaleType === ScaleType.Linear - ? ScaleType.LinearBinary - : yScaleType, - color: ({ yAccessor, seriesKeys }) => { - const overwriteColor = getSeriesColor(layer, accessor); - if (overwriteColor !== null) { - return overwriteColor; - } - const colorAssignment = colorAssignments[palette.name]; - const seriesLayers: SeriesLayer[] = [ - { - name: splitAccessor ? String(seriesKeys[0]) : columnToLabelMap[seriesKeys[0]], - totalSeriesAtDepth: colorAssignment.totalSeriesCount, - rankAtDepth: colorAssignment.getRank( - layer, - String(seriesKeys[0]), - String(yAccessor) - ), - }, - ]; - return paletteService.get(palette.name).getCategoricalColor( - seriesLayers, - { - maxDepth: 1, - behindText: false, - totalSeries: colorAssignment.totalSeriesCount, - syncColors, - }, - palette.params - ); - }, - groupId: yAxis?.groupId, - enableHistogramMode, - stackMode: isPercentage ? StackMode.Percentage : undefined, - timeZone, - areaSeriesStyle: { - point: { - visible: !xAccessor, - radius: 5, + const yAxis = yAxesConfiguration.find((axisConfiguration) => + axisConfiguration.series.find((currentSeries) => currentSeries.accessor === accessor) + ); + + const formatter = table?.columns.find((column) => column.id === accessor)?.meta?.params; + const splitHint = table.columns.find((col) => col.id === splitAccessor)?.meta?.params; + const splitFormatter = formatFactory(splitHint); + + const seriesProps: SeriesSpec = { + splitSeriesAccessors: splitAccessor ? [splitAccessor] : [], + stackAccessors: isStacked ? [xAccessor as string] : [], + id: `${splitAccessor}-${accessor}`, + xAccessor: xAccessor || 'unifiedX', + yAccessors: [accessor], + data: rows, + xScaleType: xAccessor ? xScaleType : 'ordinal', + yScaleType: + formatter?.id === 'bytes' && yScaleType === ScaleType.Linear + ? ScaleType.LinearBinary + : yScaleType, + color: ({ yAccessor, seriesKeys }) => { + const overwriteColor = getSeriesColor(layer, accessor); + if (overwriteColor !== null) { + return overwriteColor; + } + const colorAssignment = colorAssignments[palette.name]; + const seriesLayers: SeriesLayer[] = [ + { + name: splitAccessor ? String(seriesKeys[0]) : columnToLabelMap[seriesKeys[0]], + totalSeriesAtDepth: colorAssignment.totalSeriesCount, + rankAtDepth: colorAssignment.getRank( + layer, + String(seriesKeys[0]), + String(yAccessor) + ), }, - ...(args.fillOpacity && { area: { opacity: args.fillOpacity } }), - }, - lineSeriesStyle: { - point: { - visible: !xAccessor, - radius: 5, + ]; + return paletteService.get(palette.name).getCategoricalColor( + seriesLayers, + { + maxDepth: 1, + behindText: false, + totalSeries: colorAssignment.totalSeriesCount, + syncColors, }, + palette.params + ); + }, + groupId: yAxis?.groupId, + enableHistogramMode, + stackMode: isPercentage ? StackMode.Percentage : undefined, + timeZone, + areaSeriesStyle: { + point: { + visible: !xAccessor, + radius: 5, }, - name(d) { - // For multiple y series, the name of the operation is used on each, either: - // * Key - Y name - // * Formatted value - Y name - if (accessors.length > 1) { - const result = d.seriesKeys - .map((key: string | number, i) => { - if ( - i === 0 && - splitHint && - splitAccessor && - !layersAlreadyFormatted[splitAccessor] - ) { - return splitFormatter.convert(key); - } - return splitAccessor && i === 0 ? key : columnToLabelMap[key] ?? ''; - }) - .join(' - '); - return result; - } + ...(args.fillOpacity && { area: { opacity: args.fillOpacity } }), + }, + lineSeriesStyle: { + point: { + visible: !xAccessor, + radius: 5, + }, + }, + name(d) { + // For multiple y series, the name of the operation is used on each, either: + // * Key - Y name + // * Formatted value - Y name + if (accessors.length > 1) { + const result = d.seriesKeys + .map((key: string | number, i) => { + if ( + i === 0 && + splitHint && + splitAccessor && + !layersAlreadyFormatted[splitAccessor] + ) { + return splitFormatter.convert(key); + } + return splitAccessor && i === 0 ? key : columnToLabelMap[key] ?? ''; + }) + .join(' - '); + return result; + } - // For formatted split series, format the key - // This handles splitting by dates, for example - if (splitHint) { - if (splitAccessor && layersAlreadyFormatted[splitAccessor]) { - return d.seriesKeys[0]; - } - return splitFormatter.convert(d.seriesKeys[0]); + // For formatted split series, format the key + // This handles splitting by dates, for example + if (splitHint) { + if (splitAccessor && layersAlreadyFormatted[splitAccessor]) { + return d.seriesKeys[0]; } - // This handles both split and single-y cases: - // * If split series without formatting, show the value literally - // * If single Y, the seriesKey will be the accessor, so we show the human-readable name - return splitAccessor ? d.seriesKeys[0] : columnToLabelMap[d.seriesKeys[0]] ?? ''; - }, - }; - - const index = `${layerIndex}-${accessorIndex}`; - - const curveType = args.curveType ? CurveType[args.curveType] : undefined; - - switch (seriesType) { - case 'line': - return ( - - ); - case 'bar': - case 'bar_stacked': - case 'bar_percentage_stacked': - case 'bar_horizontal': - case 'bar_horizontal_stacked': - case 'bar_horizontal_percentage_stacked': - const valueLabelsSettings = { - displayValueSettings: { - // This format double fixes two issues in elastic-chart - // * when rotating the chart, the formatter is not correctly picked - // * in some scenarios value labels are not strings, and this breaks the elastic-chart lib - valueFormatter: (d: unknown) => yAxis?.formatter?.convert(d) || '', - showValueLabel: shouldShowValueLabels && valueLabels !== 'hide', - isValueContainedInElement: false, - isAlternatingValueLabel: false, - overflowConstraints: [ - LabelOverflowConstraint.ChartEdges, - LabelOverflowConstraint.BarGeometry, - ], - }, - }; - return ; - case 'area_stacked': - case 'area_percentage_stacked': - return ( - - ); - case 'area': - return ( - - ); - default: - return assertNever(seriesType); - } - }) - )} - {referenceLineLayers.length ? ( - - ) : null} - -
+ return splitFormatter.convert(d.seriesKeys[0]); + } + // This handles both split and single-y cases: + // * If split series without formatting, show the value literally + // * If single Y, the seriesKey will be the accessor, so we show the human-readable name + return splitAccessor ? d.seriesKeys[0] : columnToLabelMap[d.seriesKeys[0]] ?? ''; + }, + }; + + const index = `${layerIndex}-${accessorIndex}`; + + const curveType = args.curveType ? CurveType[args.curveType] : undefined; + + switch (seriesType) { + case 'line': + return ( + + ); + case 'bar': + case 'bar_stacked': + case 'bar_percentage_stacked': + case 'bar_horizontal': + case 'bar_horizontal_stacked': + case 'bar_horizontal_percentage_stacked': + const valueLabelsSettings = { + displayValueSettings: { + // This format double fixes two issues in elastic-chart + // * when rotating the chart, the formatter is not correctly picked + // * in some scenarios value labels are not strings, and this breaks the elastic-chart lib + valueFormatter: (d: unknown) => yAxis?.formatter?.convert(d) || '', + showValueLabel: shouldShowValueLabels && valueLabels !== 'hide', + isValueContainedInElement: false, + isAlternatingValueLabel: false, + overflowConstraints: [ + LabelOverflowConstraint.ChartEdges, + LabelOverflowConstraint.BarGeometry, + ], + }, + }; + return ; + case 'area_stacked': + case 'area_percentage_stacked': + return ( + + ); + case 'area': + return ( + + ); + default: + return assertNever(seriesType); + } + }) + )} + {referenceLineLayers.length ? ( + + ) : null} + ); } diff --git a/src/plugins/chart_expressions/expression_xy/public/expression_renderers/xy_chart_renderer.tsx b/src/plugins/chart_expressions/expression_xy/public/expression_renderers/xy_chart_renderer.tsx index b6b4229175c46..e653f093daba1 100644 --- a/src/plugins/chart_expressions/expression_xy/public/expression_renderers/xy_chart_renderer.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/expression_renderers/xy_chart_renderer.tsx @@ -58,21 +58,23 @@ export const getXyChartRenderer = ({ ReactDOM.render( - +
+ +
{' '}
, domNode, diff --git a/x-pack/test/functional/apps/dashboard/dashboard_lens_by_value.ts b/x-pack/test/functional/apps/dashboard/dashboard_lens_by_value.ts index 382449e5e2586..9e4c2554100b9 100644 --- a/x-pack/test/functional/apps/dashboard/dashboard_lens_by_value.ts +++ b/x-pack/test/functional/apps/dashboard/dashboard_lens_by_value.ts @@ -92,7 +92,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { operation: 'average', field: 'bytes', }); - await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.waitForVisualization('xyVisChart'); await PageObjects.lens.notLinkedToOriginatingApp(); // return to origin should not be present in save modal From 00c7e284a679a740cc96f50f0f1621a2e0fbdb28 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Thu, 17 Mar 2022 11:33:19 +0200 Subject: [PATCH 040/133] Fixed 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 e3cf31ce9e6a2..66c928190d55f 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -780,7 +780,6 @@ "xpack.lens.xyChart.splitSeries": "Répartir par", "xpack.lens.xyChart.tickLabels": "Étiquettes de graduation", "expressionXY.xyVis.tickLabelsVisibilitySettings.help": "Afficher les étiquettes de graduation des axes X et Y", - "xpack.lens.xyChart.title.help": "Titre de l'axe", "xpack.lens.xyChart.topAxisDisabledHelpText": "Ce paramètre s'applique uniquement lorsque l'axe du haut est activé.", "xpack.lens.xyChart.topAxisLabel": "Axe du haut", "xpack.lens.xyChart.upperBoundLabel": "Limite supérieure", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 7ae738987b26c..448c0ee5c049e 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -931,7 +931,6 @@ "xpack.lens.xyChart.splitSeries": "内訳の基準", "xpack.lens.xyChart.tickLabels": "目盛ラベル", "expressionXY.xyVis.tickLabelsVisibilitySettings.help": "xおよびy軸の目盛ラベルを表示", - "xpack.lens.xyChart.title.help": "軸のタイトル", "xpack.lens.xyChart.topAxisDisabledHelpText": "この設定は、上の軸が有効であるときにのみ適用されます。", "xpack.lens.xyChart.topAxisLabel": "上の軸", "xpack.lens.xyChart.upperBoundLabel": "上界", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index ee08500204f49..fd82b3e18daad 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -935,7 +935,6 @@ "xpack.lens.xyChart.splitSeries": "细分方式", "xpack.lens.xyChart.tickLabels": "刻度标签", "expressionXY.xyVis.tickLabelsVisibilitySettings.help": "显示 x 和 y 轴刻度标签", - "xpack.lens.xyChart.title.help": "轴标题", "xpack.lens.xyChart.topAxisDisabledHelpText": "此设置仅在启用顶轴时应用。", "xpack.lens.xyChart.topAxisLabel": "顶轴", "xpack.lens.xyChart.upperBoundLabel": "上边界", From 0ad37937cc558527c0a366c126f2d30af619aafe Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Thu, 17 Mar 2022 11:58:07 +0200 Subject: [PATCH 041/133] Expression tests fixed. --- .../expression_xy/common/constants.ts | 4 +- .../data_layer_config.test.ts | 33 +++++ .../expression_functions/expression.test.tsx | 119 ------------------ .../grid_lines_config.test.ts | 20 +++ .../common/expression_functions/index.ts | 2 +- .../labels_orientation_config.test.ts | 20 +++ .../legend_config.test.ts | 21 ++++ .../tick_labels_config.test.ts | 20 +++ .../expression_functions/xy_vis.test.ts | 21 ++++ .../{xy_chart.ts => xy_vis.ts} | 12 +- .../expression_xy/common/index.ts | 2 +- .../common/types/expression_renderers.ts | 4 +- .../expression_xy/public/plugin.ts | 4 +- .../expression_xy/server/plugin.ts | 4 +- 14 files changed, 151 insertions(+), 135 deletions(-) create mode 100644 src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer_config.test.ts delete mode 100644 src/plugins/chart_expressions/expression_xy/common/expression_functions/expression.test.tsx create mode 100644 src/plugins/chart_expressions/expression_xy/common/expression_functions/grid_lines_config.test.ts create mode 100644 src/plugins/chart_expressions/expression_xy/common/expression_functions/labels_orientation_config.test.ts create mode 100644 src/plugins/chart_expressions/expression_xy/common/expression_functions/legend_config.test.ts create mode 100644 src/plugins/chart_expressions/expression_xy/common/expression_functions/tick_labels_config.test.ts create mode 100644 src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.test.ts rename src/plugins/chart_expressions/expression_xy/common/expression_functions/{xy_chart.ts => xy_vis.ts} (97%) diff --git a/src/plugins/chart_expressions/expression_xy/common/constants.ts b/src/plugins/chart_expressions/expression_xy/common/constants.ts index f77f8950f78bf..9b273710399c3 100644 --- a/src/plugins/chart_expressions/expression_xy/common/constants.ts +++ b/src/plugins/chart_expressions/expression_xy/common/constants.ts @@ -6,12 +6,12 @@ * Side Public License, v 1. */ -export const XY_CHART = 'xyVis'; +export const XY_VIS = 'xyVis'; export const Y_CONFIG = 'yConfig'; export const MULTITABLE = 'lens_multitable'; export const DATA_LAYER = 'dataLayer'; export const LEGEND_CONFIG = 'legendConfig'; -export const XY_CHART_RENDERER = 'xyVis'; +export const XY_VIS_RENDERER = 'xyVis'; export const GRID_LINES_CONFIG = 'gridlinesConfig'; export const TICK_LABELS_CONFIG = 'tickLabelsConfig'; export const AXIS_EXTENT_CONFIG = 'axisExtentConfig'; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer_config.test.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer_config.test.ts new file mode 100644 index 0000000000000..ba7fafd3b3685 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer_config.test.ts @@ -0,0 +1,33 @@ +/* + * 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 { DataLayerArgs } from '../types'; +import { dataLayerConfigFunction } from '../expression_functions'; +import { createMockExecutionContext } from '../../../../expressions/common/mocks'; +import { mockPaletteOutput } from '../__mocks__'; +import { LayerTypes } from '../constants'; + +describe('dataLayerConfig', () => { + test('produces the correct arguments', () => { + const args: DataLayerArgs = { + layerId: 'first', + seriesType: 'line', + xAccessor: 'c', + accessors: ['a', 'b'], + splitAccessor: 'd', + xScaleType: 'linear', + yScaleType: 'linear', + isHistogram: false, + palette: mockPaletteOutput, + }; + + const result = dataLayerConfigFunction.fn(null, args, createMockExecutionContext()); + + expect(result).toEqual({ type: 'dataLayer', layerType: LayerTypes.DATA, ...args }); + }); +}); diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/expression.test.tsx b/src/plugins/chart_expressions/expression_xy/common/expression_functions/expression.test.tsx deleted file mode 100644 index c9b92583ae9f5..0000000000000 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/expression.test.tsx +++ /dev/null @@ -1,119 +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 { Position } from '@elastic/charts'; -import { LegendConfig, AxesSettingsConfig, LabelsOrientationConfig, DataLayerArgs } from '../types'; -import { - xyChartFunction, - dataLayerConfigFunction, - legendConfigFunction, - tickLabelsConfigFunction, - gridlinesConfigFunction, - labelsOrientationConfigFunction, -} from '../expression_functions'; -import { createMockExecutionContext } from '../../../../../plugins/expressions/common/mocks'; -import { mockPaletteOutput, sampleArgs } from '../__mocks__'; -import { LayerTypes } from '../constants'; - -describe('xy_expression', () => { - describe('configs', () => { - test('legendConfigFunction produces the correct arguments', () => { - const args: LegendConfig = { - isVisible: true, - position: Position.Left, - }; - - const result = legendConfigFunction.fn(null, args, createMockExecutionContext()); - - expect(result).toEqual({ - type: 'legendConfig', - ...args, - }); - }); - - test('dataLayerConfig produces the correct arguments', () => { - const args: DataLayerArgs = { - layerId: 'first', - seriesType: 'line', - xAccessor: 'c', - accessors: ['a', 'b'], - splitAccessor: 'd', - xScaleType: 'linear', - yScaleType: 'linear', - isHistogram: false, - palette: mockPaletteOutput, - }; - - const result = dataLayerConfigFunction.fn(null, args, createMockExecutionContext()); - - expect(result).toEqual({ - type: 'dataLayer', - layerType: LayerTypes.DATA, - ...args, - }); - }); - }); - - test('tickLabelsConfigFunction produces the correct arguments', () => { - const args: AxesSettingsConfig = { - x: true, - yLeft: false, - yRight: false, - }; - - const result = tickLabelsConfigFunction.fn(null, args, createMockExecutionContext()); - - expect(result).toEqual({ - type: 'tickLabelsConfig', - ...args, - }); - }); - - test('gridlinesConfigFunction produces the correct arguments', () => { - const args: AxesSettingsConfig = { - x: true, - yLeft: false, - yRight: false, - }; - - const result = gridlinesConfigFunction.fn(null, args, createMockExecutionContext()); - - expect(result).toEqual({ - type: 'gridlinesConfig', - ...args, - }); - }); - - test('labelsOrientationConfigFunction produces the correct arguments', () => { - const args: LabelsOrientationConfig = { - x: 0, - yLeft: -90, - yRight: -45, - }; - - const result = labelsOrientationConfigFunction.fn(null, args, createMockExecutionContext()); - - expect(result).toEqual({ - type: 'labelsOrientationConfig', - ...args, - }); - }); - - describe('xyChartFunction', () => { - test('it renders with the specified data and args', () => { - const { data, args } = sampleArgs(); - const result = xyChartFunction.fn(data, args, createMockExecutionContext()); - - expect(result).toEqual({ - type: 'render', - as: 'xyVis', - value: { data, args }, - }); - }); - }); -}); diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/grid_lines_config.test.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/grid_lines_config.test.ts new file mode 100644 index 0000000000000..91bfbc8fbe6f0 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/grid_lines_config.test.ts @@ -0,0 +1,20 @@ +/* + * 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 { AxesSettingsConfig } from '../types'; +import { gridlinesConfigFunction } from '../expression_functions'; +import { createMockExecutionContext } from '../../../../../plugins/expressions/common/mocks'; + +describe('gridlinesConfig', () => { + test('produces the correct arguments', () => { + const args: AxesSettingsConfig = { x: true, yLeft: false, yRight: false }; + const result = gridlinesConfigFunction.fn(null, args, createMockExecutionContext()); + + expect(result).toEqual({ type: 'gridlinesConfig', ...args }); + }); +}); diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/index.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/index.ts index 736a49487c06d..a7144eef13143 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/index.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/index.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -export * from './xy_chart'; +export * from './xy_vis'; export * from './legend_config'; export * from './y_axis_config'; export * from './data_layer_config'; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/labels_orientation_config.test.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/labels_orientation_config.test.ts new file mode 100644 index 0000000000000..2d54a729d3e5a --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/labels_orientation_config.test.ts @@ -0,0 +1,20 @@ +/* + * 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 { LabelsOrientationConfig } from '../types'; +import { labelsOrientationConfigFunction } from '../expression_functions'; +import { createMockExecutionContext } from '../../../../../plugins/expressions/common/mocks'; + +describe('labelsOrientationConfig', () => { + test('produces the correct arguments', () => { + const args: LabelsOrientationConfig = { x: 0, yLeft: -90, yRight: -45 }; + const result = labelsOrientationConfigFunction.fn(null, args, createMockExecutionContext()); + + expect(result).toEqual({ type: 'labelsOrientationConfig', ...args }); + }); +}); diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/legend_config.test.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/legend_config.test.ts new file mode 100644 index 0000000000000..2c673ab990ded --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/legend_config.test.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { Position } from '@elastic/charts'; +import { createMockExecutionContext } from '../../../../expressions/common/mocks'; +import { LegendConfig } from '../types'; +import { legendConfigFunction } from './legend_config'; + +describe('legendConfigFunction', () => { + test('produces the correct arguments', () => { + const args: LegendConfig = { isVisible: true, position: Position.Left }; + const result = legendConfigFunction.fn(null, args, createMockExecutionContext()); + + expect(result).toEqual({ type: 'legendConfig', ...args }); + }); +}); diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/tick_labels_config.test.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/tick_labels_config.test.ts new file mode 100644 index 0000000000000..8b31258377dd9 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/tick_labels_config.test.ts @@ -0,0 +1,20 @@ +/* + * 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 { AxesSettingsConfig } from '../types'; +import { tickLabelsConfigFunction } from '../expression_functions'; +import { createMockExecutionContext } from '../../../../../plugins/expressions/common/mocks'; + +describe('tickLabelsConfig', () => { + test('produces the correct arguments', () => { + const args: AxesSettingsConfig = { x: true, yLeft: false, yRight: false }; + const result = tickLabelsConfigFunction.fn(null, args, createMockExecutionContext()); + + expect(result).toEqual({ type: 'tickLabelsConfig', ...args }); + }); +}); diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.test.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.test.ts new file mode 100644 index 0000000000000..69d0bf51b3d64 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.test.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { xyVisFunction } from '../expression_functions'; +import { createMockExecutionContext } from '../../../../../plugins/expressions/common/mocks'; +import { sampleArgs } from '../__mocks__'; +import { XY_VIS } from '../constants'; + +describe('xyVis', () => { + test('it renders with the specified data and args', () => { + const { data, args } = sampleArgs(); + const result = xyVisFunction.fn(data, args, createMockExecutionContext()); + + expect(result).toEqual({ type: 'render', as: XY_VIS, value: { data, args } }); + }); +}); diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_chart.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts similarity index 97% rename from src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_chart.ts rename to src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts index f6fddf2bf39bf..8049d117b3389 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_chart.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts @@ -14,7 +14,7 @@ import type { } from '../../../../expressions'; import { LensMultiTable, XYArgs, XYRender } from '../types'; import { - XY_CHART, + XY_VIS, DATA_LAYER, MULTITABLE, XYCurveTypes, @@ -22,7 +22,7 @@ import { ValueLabelModes, FittingFunctions, GRID_LINES_CONFIG, - XY_CHART_RENDERER, + XY_VIS_RENDERER, AXIS_EXTENT_CONFIG, TICK_LABELS_CONFIG, REFERENCE_LINE_LAYER, @@ -37,13 +37,13 @@ export const logDataTable = ( Object.entries(datatables).forEach(([key, table]) => tableAdapter.logDatatable(key, table)); }; -export const xyChartFunction: ExpressionFunctionDefinition< - typeof XY_CHART, +export const xyVisFunction: ExpressionFunctionDefinition< + typeof XY_VIS, LensMultiTable, XYArgs, XYRender > = { - name: XY_CHART, + name: XY_VIS, type: 'render', inputTypes: [MULTITABLE], help: i18n.translate('expressionXY.xyVis.help', { @@ -181,7 +181,7 @@ export const xyChartFunction: ExpressionFunctionDefinition< return { type: 'render', - as: XY_CHART_RENDERER, + as: XY_VIS_RENDERER, value: { data, args: { diff --git a/src/plugins/chart_expressions/expression_xy/common/index.ts b/src/plugins/chart_expressions/expression_xy/common/index.ts index 141aac83b2822..8ecc0777c05a9 100755 --- a/src/plugins/chart_expressions/expression_xy/common/index.ts +++ b/src/plugins/chart_expressions/expression_xy/common/index.ts @@ -10,7 +10,7 @@ export const PLUGIN_ID = 'expressionXy'; export const PLUGIN_NAME = 'expressionXy'; export { - xyChartFunction, + xyVisFunction, yAxisConfigFunction, legendConfigFunction, gridlinesConfigFunction, diff --git a/src/plugins/chart_expressions/expression_xy/common/types/expression_renderers.ts b/src/plugins/chart_expressions/expression_xy/common/types/expression_renderers.ts index 74edf916c7584..1acb98903d06b 100644 --- a/src/plugins/chart_expressions/expression_xy/common/types/expression_renderers.ts +++ b/src/plugins/chart_expressions/expression_xy/common/types/expression_renderers.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { XY_CHART_RENDERER } from '../constants'; +import { XY_VIS_RENDERER } from '../constants'; import { LensMultiTable, XYArgs } from './expression_functions'; export interface XYChartProps { @@ -16,6 +16,6 @@ export interface XYChartProps { export interface XYRender { type: 'render'; - as: typeof XY_CHART_RENDERER; + as: typeof XY_VIS_RENDERER; value: XYChartProps; } diff --git a/src/plugins/chart_expressions/expression_xy/public/plugin.ts b/src/plugins/chart_expressions/expression_xy/public/plugin.ts index a5af4ce62f1ac..9f175b03d6743 100755 --- a/src/plugins/chart_expressions/expression_xy/public/plugin.ts +++ b/src/plugins/chart_expressions/expression_xy/public/plugin.ts @@ -14,7 +14,7 @@ import { ChartsPluginStart } from '../../../charts/public'; import { CoreSetup, CoreStart, IUiSettingsClient } from '../../../../core/public'; import { ExpressionXyPluginSetup, ExpressionXyPluginStart, SetupDeps } from './types'; import { - xyChartFunction, + xyVisFunction, yAxisConfigFunction, legendConfigFunction, gridlinesConfigFunction, @@ -56,7 +56,7 @@ export class ExpressionXyPlugin { expressions.registerFunction(labelsOrientationConfigFunction); expressions.registerFunction(referenceLineLayerConfigFunction); expressions.registerFunction(axisTitlesVisibilityConfigFunction); - expressions.registerFunction(xyChartFunction); + expressions.registerFunction(xyVisFunction); const getStartDeps: GetStartDepsFn = async () => { const [coreStart, deps] = await core.getStartServices(); diff --git a/src/plugins/chart_expressions/expression_xy/server/plugin.ts b/src/plugins/chart_expressions/expression_xy/server/plugin.ts index 53653a0d93c33..38f5526504ae0 100755 --- a/src/plugins/chart_expressions/expression_xy/server/plugin.ts +++ b/src/plugins/chart_expressions/expression_xy/server/plugin.ts @@ -10,7 +10,7 @@ import { CoreSetup, CoreStart, Plugin } from '../../../../core/server'; import { ExpressionXyPluginSetup, ExpressionXyPluginStart } from './types'; import { - xyChartFunction, + xyVisFunction, yAxisConfigFunction, legendConfigFunction, gridlinesConfigFunction, @@ -36,7 +36,7 @@ export class ExpressionXyPlugin expressions.registerFunction(labelsOrientationConfigFunction); expressions.registerFunction(referenceLineLayerConfigFunction); expressions.registerFunction(axisTitlesVisibilityConfigFunction); - expressions.registerFunction(xyChartFunction); + expressions.registerFunction(xyVisFunction); } public start(core: CoreStart) {} From 2fbe81f167b65cdd3055be89871e804de210bc0d Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Thu, 17 Mar 2022 12:10:49 +0200 Subject: [PATCH 042/133] Cleaned up expression_xy. --- .../public/components/reference_lines.tsx | 5 -- .../public/components/xy_chart.tsx | 2 - .../public/definitions/visualizations.ts | 5 +- .../public/helpers/color_assignment.ts | 72 +------------------ .../expression_xy/public/helpers/state.ts | 59 +-------------- 5 files changed, 5 insertions(+), 138 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.tsx b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.tsx index 786e986fed1bd..7666415465381 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.tsx @@ -14,7 +14,6 @@ import { EuiIcon } from '@elastic/eui'; import { RectAnnotation, AnnotationDomainType, LineAnnotation, Position } from '@elastic/charts'; import { euiLightVars } from '@kbn/ui-theme'; import type { FieldFormat } from '../../../../field_formats/common'; -import type { PaletteRegistry } from '../../../../charts/public'; import type { ReferenceLineLayerConfigResult, IconPosition, YAxisMode } from '../../common'; import type { LensMultiTable } from '../../common/types'; import { hasIcon } from '../helpers'; @@ -192,8 +191,6 @@ export interface ReferenceLineAnnotationsProps { layers: ReferenceLineLayerConfigResult[]; data: LensMultiTable; formatters: Record<'left' | 'right' | 'bottom', FieldFormat | undefined>; - paletteService: PaletteRegistry; - syncColors: boolean; axesMap: Record<'left' | 'right', boolean>; isHorizontal: boolean; paddingMap: Partial>; @@ -203,8 +200,6 @@ export const ReferenceLineAnnotations = ({ layers, data, formatters, - paletteService, - syncColors, axesMap, isHorizontal, paddingMap, diff --git a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx index a1350eb6a226e..89c1b62e3ecce 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx @@ -885,8 +885,6 @@ export function XYChart({ value != null && typeof value !== 'object'; @@ -98,64 +91,3 @@ export function getColorAssignments( }; }); } - -const getReferenceLineAccessorColorConfig = (layer: ReferenceLineLayerConfigResult) => { - return layer.accessors.map((accessor) => { - const currentYConfig = layer.yConfig?.find((yConfig) => yConfig.forAccessor === accessor); - return { - columnId: accessor, - triggerIcon: 'color' as const, - color: currentYConfig?.color || defaultReferenceLineColor, - }; - }); -}; - -export function getAccessorColorConfig( - colorAssignments: ColorAssignments, - frame: Pick, - layer: XYLayerConfigResult, - paletteService: PaletteRegistry -): AccessorConfig[] { - if (isReferenceLayer(layer)) { - return getReferenceLineAccessorColorConfig(layer); - } - - const layerContainsSplits = Boolean(layer.splitAccessor); - const currentPalette: PaletteOutput = layer.palette || { type: 'palette', name: 'default' }; - const totalSeriesCount = colorAssignments[currentPalette.name]?.totalSeriesCount; - return layer.accessors.map((accessor) => { - const currentYConfig = layer.yConfig?.find((yConfig) => yConfig.forAccessor === accessor); - if (layerContainsSplits) { - return { - columnId: accessor as string, - triggerIcon: 'disabled', - }; - } - const columnToLabel = getColumnToLabelMap(layer, frame.datasourceLayers[layer.layerId]); - const rank = colorAssignments[currentPalette.name].getRank( - layer, - columnToLabel[accessor] || accessor, - accessor - ); - const customColor = - currentYConfig?.color || - (totalSeriesCount != null - ? paletteService.get(currentPalette.name).getCategoricalColor( - [ - { - name: columnToLabel[accessor] || accessor, - rankAtDepth: rank, - totalSeriesAtDepth: totalSeriesCount, - }, - ], - { maxDepth: 1, totalSeries: totalSeriesCount }, - currentPalette.params - ) - : undefined); - return { - columnId: accessor as string, - triggerIcon: customColor ? 'color' : 'disabled', - color: customColor ?? undefined, - }; - }); -} diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/state.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/state.ts index 973d2d1ca4bba..eda33dde1ba47 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/state.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/state.ts @@ -6,10 +6,7 @@ * Side Public License, v 1. */ -import { EuiIconType } from '@elastic/eui/src/components/icon/icon'; -import type { FramePublicAPI, DatasourcePublicAPI } from '../types'; -import type { SeriesType, XYLayerConfigResult, YConfig, ValidLayer } from '../../common'; -import { visualizationDefinitions } from '../definitions'; +import type { SeriesType, XYLayerConfigResult, YConfig } from '../../common'; import { getDataLayers, isDataLayer } from './visualization'; export function isHorizontalSeries(seriesType: SeriesType) { @@ -20,14 +17,6 @@ export function isHorizontalSeries(seriesType: SeriesType) { ); } -export function isPercentageSeries(seriesType: SeriesType) { - return ( - seriesType === 'bar_percentage_stacked' || - seriesType === 'bar_horizontal_percentage_stacked' || - seriesType === 'area_percentage_stacked' - ); -} - export function isStackedChart(seriesType: SeriesType) { return seriesType.includes('stacked'); } @@ -36,16 +25,6 @@ export function isHorizontalChart(layers: XYLayerConfigResult[]) { return getDataLayers(layers).every((l) => isHorizontalSeries(l.seriesType)); } -export function getIconForSeries(type: SeriesType): EuiIconType { - const definition = visualizationDefinitions.find((t) => t.id === type); - - if (!definition) { - throw new Error(`Unknown series type ${type}`); - } - - return (definition.icon as EuiIconType) || 'empty'; -} - export const getSeriesColor = (layer: XYLayerConfigResult, accessor: string) => { if (isDataLayer(layer) && layer.splitAccessor) { return null; @@ -54,39 +33,3 @@ export const getSeriesColor = (layer: XYLayerConfigResult, accessor: string) => layer?.yConfig?.find((yConfig: YConfig) => yConfig.forAccessor === accessor)?.color || null ); }; - -export const getColumnToLabelMap = ( - layer: XYLayerConfigResult, - datasource: DatasourcePublicAPI -) => { - const columnToLabel: Record = {}; - layer.accessors - .concat(isDataLayer(layer) && layer.splitAccessor ? [layer.splitAccessor] : []) - .forEach((accessor) => { - const operation = datasource.getOperationForColumnId(accessor); - if (operation?.label) { - columnToLabel[accessor] = operation.label; - } - }); - return columnToLabel; -}; - -export function hasHistogramSeries( - layers: ValidLayer[] = [], - datasourceLayers?: FramePublicAPI['datasourceLayers'] -) { - if (!datasourceLayers) { - return false; - } - const validLayers = layers.filter(({ accessors }) => accessors.length); - - return validLayers.some(({ layerId, xAccessor }: ValidLayer) => { - const xAxisOperation = datasourceLayers[layerId].getOperationForColumnId(xAccessor); - return ( - xAxisOperation && - xAxisOperation.isBucketed && - xAxisOperation.scale && - xAxisOperation.scale !== 'ordinal' - ); - }); -} From e9c497f9fc8c67e9ef4380a424a88a7c9b62a407 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Thu, 17 Mar 2022 12:24:55 +0200 Subject: [PATCH 043/133] cleaned up lens xy_visualization. --- .../lens/public/xy_visualization/visualization_helpers.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization_helpers.tsx b/x-pack/plugins/lens/public/xy_visualization/visualization_helpers.tsx index d25a79dcd44c1..1d867497d4c1f 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization_helpers.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/visualization_helpers.tsx @@ -142,9 +142,6 @@ export const isReferenceLayer = ( layer: Pick ): layer is XYReferenceLineLayerConfig => layer.layerType === layerTypes.REFERENCELINE; -export const getReferenceLayers = (layers: XYLayerConfig[]) => - (layers || []).filter((layer): layer is XYReferenceLineLayerConfig => isReferenceLayer(layer)); - export function getVisualizationType(state: State): VisualizationType | 'mixed' { if (!state.layers.length) { return ( From ea299ec0e1904591f284c46021abfc53afa35499 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Thu, 17 Mar 2022 14:24:03 +0200 Subject: [PATCH 044/133] fixed more tests. --- x-pack/test/functional/apps/lens/epoch_millis.ts | 4 ++-- x-pack/test/functional/apps/lens/formula.ts | 4 ++-- x-pack/test/functional/apps/lens/gauge.ts | 2 +- x-pack/test/functional/apps/lens/heatmap.ts | 14 +++++++------- x-pack/test/functional/apps/lens/inspector.ts | 2 +- x-pack/test/functional/apps/lens/rollup.ts | 4 ++-- .../save_search_session_relative_time.ts | 8 ++++---- 7 files changed, 19 insertions(+), 19 deletions(-) diff --git a/x-pack/test/functional/apps/lens/epoch_millis.ts b/x-pack/test/functional/apps/lens/epoch_millis.ts index deaa3e720101e..d882d69ddd1fd 100644 --- a/x-pack/test/functional/apps/lens/epoch_millis.ts +++ b/x-pack/test/functional/apps/lens/epoch_millis.ts @@ -43,7 +43,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { operation: 'count', field: 'Records', }); - await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.waitForVisualization('mtrVis'); expect(await PageObjects.lens.getDatatableCellText(0, 0)).to.eql('1'); }); @@ -52,7 +52,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.lens.enableTimeShift(); await PageObjects.lens.setTimeShift('3d'); - await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.waitForVisualization('mtrVis'); expect(await PageObjects.lens.getDatatableCellText(0, 0)).to.eql('2'); }); }); diff --git a/x-pack/test/functional/apps/lens/formula.ts b/x-pack/test/functional/apps/lens/formula.ts index fcfec350112c4..b6b5664ab54bb 100644 --- a/x-pack/test/functional/apps/lens/formula.ts +++ b/x-pack/test/functional/apps/lens/formula.ts @@ -32,7 +32,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); await PageObjects.lens.switchToFormula(); - await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.waitForVisualization('xyVisChart'); // .echLegendItem__title is the only viable way of getting the xy chart's // legend item(s), so we're using a class selector here. // 4th item is the other bucket @@ -174,7 +174,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { operation: 'formula', }); - await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.waitForVisualization('mtrVis'); expect(await PageObjects.lens.getErrorCount()).to.eql(0); }); diff --git a/x-pack/test/functional/apps/lens/gauge.ts b/x-pack/test/functional/apps/lens/gauge.ts index cce05d7b9baba..c21ddf5b70791 100644 --- a/x-pack/test/functional/apps/lens/gauge.ts +++ b/x-pack/test/functional/apps/lens/gauge.ts @@ -32,7 +32,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { operation: 'average', field: 'bytes', }); - await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.waitForVisualization('xyVisChart'); }); it('should switch to gauge and render a gauge with default values', async () => { diff --git a/x-pack/test/functional/apps/lens/heatmap.ts b/x-pack/test/functional/apps/lens/heatmap.ts index 946de0c9c8e93..1386e1beea899 100644 --- a/x-pack/test/functional/apps/lens/heatmap.ts +++ b/x-pack/test/functional/apps/lens/heatmap.ts @@ -33,12 +33,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { field: 'bytes', }); - await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.waitForVisualization('xyVisChart'); }); it('should render heatmap chart with the temperature palette', async () => { await PageObjects.lens.switchToVisualization('heatmap', 'heat'); - await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.waitForVisualization('heatmapChart'); const debugState = await PageObjects.lens.getCurrentChartDebugState(); if (!debugState) { @@ -78,7 +78,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { typeCharByChar: true, }); }); - await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.waitForVisualization('heatmapChart'); const debugState = await PageObjects.lens.getCurrentChartDebugState(); @@ -98,7 +98,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should not change when passing from percentage to number', async () => { await testSubjects.click('lnsPalettePanel_dynamicColoring_rangeType_groups_number'); - await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.waitForVisualization('heatmapChart'); const debugState = await PageObjects.lens.getCurrentChartDebugState(); @@ -124,7 +124,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await testSubjects.setValue('lnsPalettePanel_dynamicColoring_range_value_0', '0', { clearWithKeyboard: true, }); - await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.waitForVisualization('heatmapChart'); const debugState = await PageObjects.lens.getCurrentChartDebugState(); @@ -144,7 +144,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should reset stop numbers when changing palette', async () => { await PageObjects.lens.changePaletteTo('status'); - await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.waitForVisualization('heatmapChart'); const debugState = await PageObjects.lens.getCurrentChartDebugState(); @@ -164,7 +164,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should not change when passing from number to percent', async () => { await testSubjects.click('lnsPalettePanel_dynamicColoring_rangeType_groups_percent'); - await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.waitForVisualization('heatmapChart'); const debugState = await PageObjects.lens.getCurrentChartDebugState(); diff --git a/x-pack/test/functional/apps/lens/inspector.ts b/x-pack/test/functional/apps/lens/inspector.ts index 9db804d324936..d94d3413c07b0 100644 --- a/x-pack/test/functional/apps/lens/inspector.ts +++ b/x-pack/test/functional/apps/lens/inspector.ts @@ -32,7 +32,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { field: 'bytes', }); - await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.waitForVisualization('xyVisChart'); await inspector.open('lnsApp_inspectButton'); }); diff --git a/x-pack/test/functional/apps/lens/rollup.ts b/x-pack/test/functional/apps/lens/rollup.ts index 7de0d7e76c95c..25ab766d04bbd 100644 --- a/x-pack/test/functional/apps/lens/rollup.ts +++ b/x-pack/test/functional/apps/lens/rollup.ts @@ -86,12 +86,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { operation: 'sum', field: 'bytes', }); - await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.waitForVisualization('mtrVis'); await PageObjects.lens.assertMetric('Sum of bytes', '16,788'); await PageObjects.lens.switchFirstLayerIndexPattern('lens_rolled_up_data'); - await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.waitForVisualization('mtrVis'); await PageObjects.lens.assertMetric('Sum of bytes', '16,788'); }); diff --git a/x-pack/test/search_sessions_integration/tests/apps/dashboard/async_search/save_search_session_relative_time.ts b/x-pack/test/search_sessions_integration/tests/apps/dashboard/async_search/save_search_session_relative_time.ts index 71bf03365e66d..d257a2fb560dd 100644 --- a/x-pack/test/search_sessions_integration/tests/apps/dashboard/async_search/save_search_session_relative_time.ts +++ b/x-pack/test/search_sessions_integration/tests/apps/dashboard/async_search/save_search_session_relative_time.ts @@ -63,7 +63,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await searchSessions.save(); await searchSessions.expectState('backgroundCompleted'); - await checkSampleDashboardLoaded(); + await checkSampleDashboardLoaded('xyVisChart'); // load URL to restore a saved session await PageObjects.searchSessionsManagement.goTo(); @@ -74,7 +74,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.header.waitUntilLoadingHasFinished(); await PageObjects.dashboard.waitForRenderComplete(); - await checkSampleDashboardLoaded(); + await checkSampleDashboardLoaded('xyVisChart'); // Check that session is restored await searchSessions.expectState('restored'); @@ -83,11 +83,11 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { // HELPERS - async function checkSampleDashboardLoaded() { + async function checkSampleDashboardLoaded(visualizationContainer?: string) { log.debug('Checking no error labels'); await testSubjects.missingOrFail('embeddableErrorLabel'); log.debug('Checking charts rendered'); - await elasticChart.waitForRenderComplete('lnsVisualizationContainer'); + await elasticChart.waitForRenderComplete(visualizationContainer ?? 'lnsVisualizationContainer'); log.debug('Checking saved searches rendered'); await dashboardExpect.savedSearchRowCount(11); log.debug('Checking input controls rendered'); From 9ea2cbb76ebf3a184fc8e4b303fa2c0de90576fa Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Thu, 17 Mar 2022 16:24:18 +0200 Subject: [PATCH 045/133] Fix of tsvb. --- x-pack/test/functional/apps/lens/tsvb_open_in_lens.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/x-pack/test/functional/apps/lens/tsvb_open_in_lens.ts b/x-pack/test/functional/apps/lens/tsvb_open_in_lens.ts index 0856fbb4ff1ec..0315d20e5fc91 100644 --- a/x-pack/test/functional/apps/lens/tsvb_open_in_lens.ts +++ b/x-pack/test/functional/apps/lens/tsvb_open_in_lens.ts @@ -48,7 +48,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('visualizes field to Lens and loads fields to the dimesion editor', async () => { const button = await testSubjects.find('visualizeEditInLensButton'); await button.click(); - await lens.waitForVisualization(); + await lens.waitForVisualization('xyVisChart'); await retry.try(async () => { const dimensions = await testSubjects.findAll('lns-dimensionTrigger'); expect(dimensions).to.have.length(2); @@ -72,7 +72,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await header.waitUntilLoadingHasFinished(); const button = await testSubjects.find('visualizeEditInLensButton'); await button.click(); - await lens.waitForVisualization(); + await lens.waitForVisualization('xyVisChart'); expect(await filterBar.hasFilter('extension', 'css')).to.be(true); }); @@ -86,7 +86,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await header.waitUntilLoadingHasFinished(); const button = await testSubjects.find('visualizeEditInLensButton'); await button.click(); - await lens.waitForVisualization(); + await lens.waitForVisualization('xyVisChart'); expect(await queryBar.getQueryString()).to.equal('machine.os : ios'); }); @@ -128,7 +128,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { const button = await testSubjects.find('visualizeEditInLensButton'); await button.click(); - await lens.waitForVisualization(); + await lens.waitForVisualization('xyVisChart'); await retry.try(async () => { const dimensions = await testSubjects.findAll('lns-dimensionTrigger'); expect(await dimensions[1].getVisibleText()).to.be('Count of records'); @@ -157,7 +157,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { const button = await testSubjects.find('visualizeEditInLensButton'); await button.click(); - await lens.waitForVisualization(); + await lens.waitForVisualization('mtrVis'); await retry.try(async () => { const dimensions = await testSubjects.findAll('lns-dimensionTrigger'); expect(await dimensions[1].getVisibleText()).to.be('Count of records'); From 46edc6b56beb26943fb0bc19311b813369ec8348 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Thu, 17 Mar 2022 18:28:18 +0200 Subject: [PATCH 046/133] Fixed more tests. --- x-pack/test/examples/embedded_lens/embedded_example.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/test/examples/embedded_lens/embedded_example.ts b/x-pack/test/examples/embedded_lens/embedded_example.ts index d11495f0450b4..bdd881b3ea318 100644 --- a/x-pack/test/examples/embedded_lens/embedded_example.ts +++ b/x-pack/test/examples/embedded_lens/embedded_example.ts @@ -27,12 +27,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.common.navigateToApp('embedded_lens_example'); await elasticChart.setNewChartUiDebugFlag(true); await testSubjects.click('lns-example-change-time-range'); - await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.waitForVisualization('xyVisChart'); }); it('should show chart', async () => { await testSubjects.click('lns-example-change-color'); - await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.waitForVisualization('xyVisChart'); await checkData(); }); @@ -60,7 +60,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should load Lens editor', async () => { await testSubjects.click('lns-example-open-editor'); - await PageObjects.lens.waitForVisualization(); + await PageObjects.lens.waitForVisualization('xyVisChart'); await checkData(); }); }); From 498b34e11b6d4e188279212a2204f966b51a3f5c Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Fri, 18 Mar 2022 10:06:46 +0200 Subject: [PATCH 047/133] Fixed xy chart limits. --- packages/kbn-optimizer/limits.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index d176fdee2b1c6..a10c64c570f7a 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -124,4 +124,4 @@ pageLoadAssetSize: sessionView: 77750 cloudSecurityPosture: 19109 visTypeGauge: 24113 - expressionXY: 49145 + expressionXY: 41392 From 4996ea6f37fd67d4de1ea392368b535a556d05bc Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Fri, 18 Mar 2022 11:00:51 +0200 Subject: [PATCH 048/133] Fixed new tests. --- x-pack/test/functional/apps/maps/lens/choropleth_chart.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/test/functional/apps/maps/lens/choropleth_chart.ts b/x-pack/test/functional/apps/maps/lens/choropleth_chart.ts index daa490f8ef051..420f895fe6aa6 100644 --- a/x-pack/test/functional/apps/maps/lens/choropleth_chart.ts +++ b/x-pack/test/functional/apps/maps/lens/choropleth_chart.ts @@ -47,7 +47,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.visualize.clickVisType('lens'); await PageObjects.lens.goToTimeRange(); - await PageObjects.lens.dragFieldToWorkspace('geo.dest'); + await PageObjects.lens.dragFieldToWorkspace('geo.dest', 'xyVisChart'); // add filter to force data fetch to set activeData await filterBar.addFilter('bytes', 'is between', '200', '10000'); From 97bb678a81831630b53456831a67e6b1fea4e954 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Fri, 18 Mar 2022 11:34:18 +0200 Subject: [PATCH 049/133] Fixed types. --- x-pack/plugins/lens/public/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/lens/public/index.ts b/x-pack/plugins/lens/public/index.ts index bc7bbf44472da..7c26bc1d0a544 100644 --- a/x-pack/plugins/lens/public/index.ts +++ b/x-pack/plugins/lens/public/index.ts @@ -11,7 +11,7 @@ export type { EmbeddableComponentProps, TypedLensByValueInput, } from './embeddable/embeddable_component'; -export type { XYState } from './xy_visualization/types'; +export type { XYState, XYLayerConfig } from './xy_visualization/types'; export type { DatasourcePublicAPI, DataType, From df91d70df769db6a5cb4471a9961ee13cae7d27c Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Fri, 18 Mar 2022 18:50:58 +0200 Subject: [PATCH 050/133] Added extended layers expressions. --- .../expression_xy/common/constants.ts | 3 + ...ayer_config.test.ts => data_layer.test.ts} | 4 +- .../{data_layer_config.ts => data_layer.ts} | 2 +- .../extended_data_layer.ts | 129 +++++++++++ .../extended_reference_line_layer.ts | 68 ++++++ .../common/expression_functions/index.ts | 7 +- .../expression_functions/layered_xy_vis.ts | 202 ++++++++++++++++++ ...ayer_config.ts => reference_line_layer.ts} | 2 +- .../common/expression_functions/xy_vis.ts | 38 ++-- .../expression_xy/common/index.ts | 7 +- .../common/types/expression_functions.ts | 98 ++++++++- .../common/types/expression_renderers.ts | 7 +- .../expression_xy/public/plugin.ts | 14 +- 13 files changed, 551 insertions(+), 30 deletions(-) rename src/plugins/chart_expressions/expression_xy/common/expression_functions/{data_layer_config.test.ts => data_layer.test.ts} (86%) rename src/plugins/chart_expressions/expression_xy/common/expression_functions/{data_layer_config.ts => data_layer.ts} (98%) create mode 100644 src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_data_layer.ts create mode 100644 src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_reference_line_layer.ts create mode 100644 src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts rename src/plugins/chart_expressions/expression_xy/common/expression_functions/{reference_line_layer_config.ts => reference_line_layer.ts} (96%) diff --git a/src/plugins/chart_expressions/expression_xy/common/constants.ts b/src/plugins/chart_expressions/expression_xy/common/constants.ts index 9b273710399c3..963deb418e339 100644 --- a/src/plugins/chart_expressions/expression_xy/common/constants.ts +++ b/src/plugins/chart_expressions/expression_xy/common/constants.ts @@ -7,15 +7,18 @@ */ export const XY_VIS = 'xyVis'; +export const LAYERED_XY_VIS = 'layeredXyVis'; export const Y_CONFIG = 'yConfig'; export const MULTITABLE = 'lens_multitable'; export const DATA_LAYER = 'dataLayer'; +export const EXTENDED_DATA_LAYER = 'extendedDataLayer'; export const LEGEND_CONFIG = 'legendConfig'; export const XY_VIS_RENDERER = 'xyVis'; export const GRID_LINES_CONFIG = 'gridlinesConfig'; export const TICK_LABELS_CONFIG = 'tickLabelsConfig'; export const AXIS_EXTENT_CONFIG = 'axisExtentConfig'; export const REFERENCE_LINE_LAYER = 'referenceLineLayer'; +export const EXTENDED_REFERENCE_LINE_LAYER = 'extendedReferenceLineLayer'; export const LABELS_ORIENTATION_CONFIG = 'labelsOrientationConfig'; export const AXIS_TITLES_VISIBILITY_CONFIG = 'axisTitlesVisibilityConfig'; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer_config.test.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer.test.ts similarity index 86% rename from src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer_config.test.ts rename to src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer.test.ts index ba7fafd3b3685..05be77a966278 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer_config.test.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer.test.ts @@ -7,7 +7,7 @@ */ import { DataLayerArgs } from '../types'; -import { dataLayerConfigFunction } from '../expression_functions'; +import { dataLayerFunction } from '.'; import { createMockExecutionContext } from '../../../../expressions/common/mocks'; import { mockPaletteOutput } from '../__mocks__'; import { LayerTypes } from '../constants'; @@ -26,7 +26,7 @@ describe('dataLayerConfig', () => { palette: mockPaletteOutput, }; - const result = dataLayerConfigFunction.fn(null, args, createMockExecutionContext()); + const result = dataLayerFunction.fn(null, args, createMockExecutionContext()); expect(result).toEqual({ type: 'dataLayer', layerType: LayerTypes.DATA, ...args }); }); diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer.ts similarity index 98% rename from src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer_config.ts rename to src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer.ts index 3aac992d674d9..d9e7f0eaa7bac 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer_config.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer.ts @@ -18,7 +18,7 @@ import { Y_CONFIG, } from '../constants'; -export const dataLayerConfigFunction: ExpressionFunctionDefinition< +export const dataLayerFunction: ExpressionFunctionDefinition< typeof DATA_LAYER, null, DataLayerArgs, diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_data_layer.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_data_layer.ts new file mode 100644 index 0000000000000..cb20603f94a95 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_data_layer.ts @@ -0,0 +1,129 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import type { Datatable, ExpressionFunctionDefinition } from '../../../../expressions/common'; +import { ExtendedDataLayerArgs, ExtendedDataLayerConfigResult } from '../types'; +import { + EXTENDED_DATA_LAYER, + LayerTypes, + SeriesTypes, + XScaleTypes, + YScaleTypes, + Y_CONFIG, +} from '../constants'; + +export const extendedDataLayerFunction: ExpressionFunctionDefinition< + typeof EXTENDED_DATA_LAYER, + Datatable | null, + ExtendedDataLayerArgs, + ExtendedDataLayerConfigResult +> = { + name: EXTENDED_DATA_LAYER, + aliases: [], + type: EXTENDED_DATA_LAYER, + help: i18n.translate('expressionXY.dataLayer.help', { + defaultMessage: `Configure a layer in the xy chart`, + }), + inputTypes: ['null', 'datatable'], + args: { + hide: { + types: ['boolean'], + default: false, + help: i18n.translate('expressionXY.dataLayer.hide.help', { + defaultMessage: 'Show / hide axis', + }), + }, + layerId: { + types: ['string'], + help: i18n.translate('expressionXY.dataLayer.layerId.help', { + defaultMessage: 'Layer ID', + }), + }, + xAccessor: { + types: ['string'], + help: i18n.translate('expressionXY.dataLayer.xAccessor.help', { + defaultMessage: 'X-axis', + }), + }, + seriesType: { + types: ['string'], + options: [...Object.values(SeriesTypes)], + help: i18n.translate('expressionXY.dataLayer.seriesType.help', { + defaultMessage: 'The type of chart to display.', + }), + }, + xScaleType: { + options: [...Object.values(XScaleTypes)], + help: i18n.translate('expressionXY.dataLayer.xScaleType.help', { + defaultMessage: 'The scale type of the x axis', + }), + default: XScaleTypes.ORDINAL, + }, + isHistogram: { + types: ['boolean'], + default: false, + help: i18n.translate('expressionXY.dataLayer.isHistogram.help', { + defaultMessage: 'Whether to layout the chart as a histogram', + }), + }, + yScaleType: { + options: [...Object.values(YScaleTypes)], + help: i18n.translate('expressionXY.dataLayer.yScaleType.help', { + defaultMessage: 'The scale type of the y axes', + }), + default: YScaleTypes.LINEAR, + }, + splitAccessor: { + types: ['string'], + help: i18n.translate('expressionXY.dataLayer.splitAccessor.help', { + defaultMessage: 'The column to split by', + }), + }, + accessors: { + types: ['string'], + help: i18n.translate('expressionXY.dataLayer.accessors.help', { + defaultMessage: 'The columns to display on the y axis.', + }), + multi: true, + }, + yConfig: { + types: [Y_CONFIG], + help: i18n.translate('expressionXY.dataLayer.yConfig.help', { + defaultMessage: 'Additional configuration for y axes', + }), + multi: true, + }, + columnToLabel: { + types: ['string'], + help: i18n.translate('expressionXY.dataLayer.columnToLabel.help', { + defaultMessage: 'JSON key-value pairs of column ID to label', + }), + }, + palette: { + default: `{theme "palette" default={system_palette name="default"} }`, + help: i18n.translate('expressionXY.dataLayer.palette.help', { + defaultMessage: 'Palette', + }), + types: ['palette'], + }, + table: { + types: ['datatable'], + help: i18n.translate('expressionXY.dataLayer.table.help', { + defaultMessage: 'Table', + }), + }, + }, + fn(input, args) { + return { + type: EXTENDED_DATA_LAYER, + ...args, + layerType: LayerTypes.DATA, + }; + }, +}; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_reference_line_layer.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_reference_line_layer.ts new file mode 100644 index 0000000000000..c59d763607648 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_reference_line_layer.ts @@ -0,0 +1,68 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import type { ExpressionFunctionDefinition } from '../../../../expressions/common'; +import { LayerTypes, EXTENDED_REFERENCE_LINE_LAYER, Y_CONFIG } from '../constants'; +import { ExtendedReferenceLineLayerArgs, ExtendedReferenceLineLayerConfigResult } from '../types'; + +export const extendedReferenceLineLayerFunction: ExpressionFunctionDefinition< + typeof EXTENDED_REFERENCE_LINE_LAYER, + null, + ExtendedReferenceLineLayerArgs, + ExtendedReferenceLineLayerConfigResult +> = { + name: EXTENDED_REFERENCE_LINE_LAYER, + aliases: [], + type: EXTENDED_REFERENCE_LINE_LAYER, + help: i18n.translate('expressionXY.referenceLineLayer.help', { + defaultMessage: `Configure a reference line in the xy chart`, + }), + inputTypes: ['null'], + args: { + layerId: { + types: ['string'], + help: i18n.translate('expressionXY.referenceLineLayer.layerId.help', { + defaultMessage: `Layer ID`, + }), + }, + accessors: { + types: ['string'], + help: i18n.translate('expressionXY.referenceLineLayer.accessors.help', { + defaultMessage: 'The columns to display on the y axis.', + }), + multi: true, + }, + yConfig: { + types: [Y_CONFIG], + help: i18n.translate('expressionXY.referenceLineLayer.yConfig.help', { + defaultMessage: 'Additional configuration for y axes', + }), + multi: true, + }, + columnToLabel: { + types: ['string'], + help: i18n.translate('expressionXY.referenceLineLayer.columnToLabel.help', { + defaultMessage: 'JSON key-value pairs of column ID to label', + }), + }, + table: { + types: ['datatable'], + help: i18n.translate('expressionXY.dataLayer.table.help', { + defaultMessage: 'Table', + }), + }, + }, + fn(input, args) { + return { + type: EXTENDED_REFERENCE_LINE_LAYER, + ...args, + layerType: LayerTypes.REFERENCELINE, + }; + }, +}; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/index.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/index.ts index a7144eef13143..7ec6b41b1c05b 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/index.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/index.ts @@ -7,12 +7,15 @@ */ export * from './xy_vis'; +export * from './layered_xy_vis'; export * from './legend_config'; export * from './y_axis_config'; -export * from './data_layer_config'; +export * from './data_layer'; +export * from './extended_data_layer'; export * from './grid_lines_config'; export * from './axis_extent_config'; export * from './tick_labels_config'; export * from './labels_orientation_config'; -export * from './reference_line_layer_config'; +export * from './reference_line_layer'; +export * from './extended_reference_line_layer'; export * from './axis_titles_visibility_config'; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts new file mode 100644 index 0000000000000..2f254ef9d0a38 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts @@ -0,0 +1,202 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import type { + ExpressionFunctionDefinition, + TablesAdapter, + Datatable, +} from '../../../../expressions'; +import { LayeredXYArgs, XYExtendedLayerConfigResult, XYRender } from '../types'; +import { + XYCurveTypes, + LEGEND_CONFIG, + ValueLabelModes, + FittingFunctions, + GRID_LINES_CONFIG, + XY_VIS_RENDERER, + AXIS_EXTENT_CONFIG, + TICK_LABELS_CONFIG, + LABELS_ORIENTATION_CONFIG, + AXIS_TITLES_VISIBILITY_CONFIG, + EXTENDED_DATA_LAYER, + EXTENDED_REFERENCE_LINE_LAYER, + LAYERED_XY_VIS, +} from '../constants'; + +const logDataTable = (tableAdapter: TablesAdapter, datatables: Record = {}) => { + Object.entries(datatables).forEach(([key, table]) => tableAdapter.logDatatable(key, table)); +}; + +export const layeredXyVisFunction: ExpressionFunctionDefinition< + typeof LAYERED_XY_VIS, + Datatable, + LayeredXYArgs, + XYRender +> = { + name: LAYERED_XY_VIS, + type: 'render', + inputTypes: ['datatable'], + help: i18n.translate('expressionXY.xyVis.help', { + defaultMessage: 'An X/Y chart', + }), + args: { + title: { + types: ['string'], + help: 'The chart title.', + }, + description: { + types: ['string'], + help: '', + }, + xTitle: { + types: ['string'], + help: i18n.translate('expressionXY.xyVis.xTitle.help', { + defaultMessage: 'X axis title', + }), + }, + yTitle: { + types: ['string'], + help: i18n.translate('expressionXY.xyVis.yLeftTitle.help', { + defaultMessage: 'Y left axis title', + }), + }, + yRightTitle: { + types: ['string'], + help: i18n.translate('expressionXY.xyVis.yRightTitle.help', { + defaultMessage: 'Y right axis title', + }), + }, + yLeftExtent: { + types: [AXIS_EXTENT_CONFIG], + help: i18n.translate('expressionXY.xyVis.yLeftExtent.help', { + defaultMessage: 'Y left axis extents', + }), + }, + yRightExtent: { + types: [AXIS_EXTENT_CONFIG], + help: i18n.translate('expressionXY.xyVis.yRightExtent.help', { + defaultMessage: 'Y right axis extents', + }), + }, + legend: { + types: [LEGEND_CONFIG], + help: i18n.translate('expressionXY.xyVis.legend.help', { + defaultMessage: 'Configure the chart legend.', + }), + }, + fittingFunction: { + types: ['string'], + options: [...Object.values(FittingFunctions)], + help: i18n.translate('expressionXY.xyVis.fittingFunction.help', { + defaultMessage: 'Define how missing values are treated', + }), + }, + valueLabels: { + types: ['string'], + options: [...Object.values(ValueLabelModes)], + help: i18n.translate('expressionXY.xyVis.valueLabels.help', { + defaultMessage: 'Value labels mode', + }), + }, + tickLabelsVisibilitySettings: { + types: [TICK_LABELS_CONFIG], + help: i18n.translate('expressionXY.xyVis.tickLabelsVisibilitySettings.help', { + defaultMessage: 'Show x and y axes tick labels', + }), + }, + labelsOrientation: { + types: [LABELS_ORIENTATION_CONFIG], + help: i18n.translate('expressionXY.xyVis.labelsOrientation.help', { + defaultMessage: 'Defines the rotation of the axis labels', + }), + }, + gridlinesVisibilitySettings: { + types: [GRID_LINES_CONFIG], + help: i18n.translate('expressionXY.xyVis.gridlinesVisibilitySettings.help', { + defaultMessage: 'Show x and y axes gridlines', + }), + }, + axisTitlesVisibilitySettings: { + types: [AXIS_TITLES_VISIBILITY_CONFIG], + help: i18n.translate('expressionXY.xyVis.axisTitlesVisibilitySettings.help', { + defaultMessage: 'Show x and y axes titles', + }), + }, + layers: { + types: [EXTENDED_DATA_LAYER, EXTENDED_REFERENCE_LINE_LAYER], + help: i18n.translate('expressionXY.xyVis.layers.help', { + defaultMessage: 'Layers of visual series', + }), + multi: true, + }, + curveType: { + types: ['string'], + options: [...Object.values(XYCurveTypes)], + help: i18n.translate('expressionXY.xyVis.curveType.help', { + defaultMessage: 'Define how curve type is rendered for a line chart', + }), + }, + fillOpacity: { + types: ['number'], + help: i18n.translate('expressionXY.xyVis.fillOpacity.help', { + defaultMessage: 'Define the area chart fill opacity', + }), + }, + hideEndzones: { + types: ['boolean'], + default: false, + help: i18n.translate('expressionXY.xyVis.hideEndzones.help', { + defaultMessage: 'Hide endzone markers for partial data', + }), + }, + valuesInLegend: { + types: ['boolean'], + default: false, + help: i18n.translate('expressionXY.xyVis.valuesInLegend.help', { + defaultMessage: 'Show values in legend', + }), + }, + ariaLabel: { + types: ['string'], + help: i18n.translate('expressionXY.xyVis.ariaLabel.help', { + defaultMessage: 'Specifies the aria label of the xy chart', + }), + required: false, + }, + }, + fn(data, args, handlers) { + const layers = args.layers.filter( + (layer): layer is XYExtendedLayerConfigResult => layer !== undefined + ); + const tables = layers.reduce>((t, { layerId }) => { + t[layerId] = data; + return t; + }, {}); + + if (handlers?.inspectorAdapters?.tables) { + logDataTable(handlers.inspectorAdapters.tables, tables); + } + + return { + type: 'render', + as: XY_VIS_RENDERER, + value: { + data, + args: { + ...args, + layers, + ariaLabel: + args.ariaLabel ?? + (handlers.variables?.embeddableTitle as string) ?? + handlers.getExecutionContext?.()?.description, + }, + }, + }; + }, +}; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line_layer_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line_layer.ts similarity index 96% rename from src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line_layer_config.ts rename to src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line_layer.ts index c5d0f17ff138d..b735e3c3864a0 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line_layer_config.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line_layer.ts @@ -11,7 +11,7 @@ import type { ExpressionFunctionDefinition } from '../../../../expressions/commo import { LayerTypes, REFERENCE_LINE_LAYER, Y_CONFIG } from '../constants'; import { ReferenceLineLayerArgs, ReferenceLineLayerConfigResult } from '../types'; -export const referenceLineLayerConfigFunction: ExpressionFunctionDefinition< +export const referenceLineLayerFunction: ExpressionFunctionDefinition< typeof REFERENCE_LINE_LAYER, null, ReferenceLineLayerArgs, diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts index 8049d117b3389..c7f4733d5ed6c 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts @@ -12,11 +12,10 @@ import type { TablesAdapter, Datatable, } from '../../../../expressions'; -import { LensMultiTable, XYArgs, XYRender } from '../types'; +import { XYArgs, XYLayerConfigResult, XYRender } from '../types'; import { XY_VIS, DATA_LAYER, - MULTITABLE, XYCurveTypes, LEGEND_CONFIG, ValueLabelModes, @@ -30,22 +29,19 @@ import { AXIS_TITLES_VISIBILITY_CONFIG, } from '../constants'; -export const logDataTable = ( - tableAdapter: TablesAdapter, - datatables: Record = {} -) => { +const logDataTable = (tableAdapter: TablesAdapter, datatables: Record = {}) => { Object.entries(datatables).forEach(([key, table]) => tableAdapter.logDatatable(key, table)); }; export const xyVisFunction: ExpressionFunctionDefinition< typeof XY_VIS, - LensMultiTable, + Datatable, XYArgs, XYRender > = { name: XY_VIS, type: 'render', - inputTypes: [MULTITABLE], + inputTypes: ['datatable'], help: i18n.translate('expressionXY.xyVis.help', { defaultMessage: 'An X/Y chart', }), @@ -132,12 +128,17 @@ export const xyVisFunction: ExpressionFunctionDefinition< defaultMessage: 'Show x and y axes titles', }), }, - layers: { - types: [DATA_LAYER, REFERENCE_LINE_LAYER], - help: i18n.translate('expressionXY.xyVis.layers.help', { - defaultMessage: 'Layers of visual series', + dataLayer: { + types: [DATA_LAYER], + help: i18n.translate('expressionXY.xyVis.dataLayer.help', { + defaultMessage: 'Data layer of visual series', + }), + }, + referenceLineLayer: { + types: [REFERENCE_LINE_LAYER], + help: i18n.translate('expressionXY.xyVis.referenceLineLayer.help', { + defaultMessage: 'Reference line layer', }), - multi: true, }, curveType: { types: ['string'], @@ -175,8 +176,16 @@ export const xyVisFunction: ExpressionFunctionDefinition< }, }, fn(data, args, handlers) { + const layers = [args.dataLayer, args.referenceLineLayer].filter( + (layer): layer is XYLayerConfigResult => layer !== undefined + ); + const tables = layers.reduce>((t, { layerId }) => { + t[layerId] = data; + return t; + }, {}); + if (handlers?.inspectorAdapters?.tables) { - logDataTable(handlers.inspectorAdapters.tables, data.tables); + logDataTable(handlers.inspectorAdapters.tables, tables); } return { @@ -186,6 +195,7 @@ export const xyVisFunction: ExpressionFunctionDefinition< data, args: { ...args, + layers, ariaLabel: args.ariaLabel ?? (handlers.variables?.embeddableTitle as string) ?? diff --git a/src/plugins/chart_expressions/expression_xy/common/index.ts b/src/plugins/chart_expressions/expression_xy/common/index.ts index 8ecc0777c05a9..66710c27177e9 100755 --- a/src/plugins/chart_expressions/expression_xy/common/index.ts +++ b/src/plugins/chart_expressions/expression_xy/common/index.ts @@ -11,14 +11,17 @@ export const PLUGIN_NAME = 'expressionXy'; export { xyVisFunction, + layeredXyVisFunction, yAxisConfigFunction, legendConfigFunction, gridlinesConfigFunction, - dataLayerConfigFunction, + dataLayerFunction, + extendedDataLayerFunction, axisExtentConfigFunction, tickLabelsConfigFunction, labelsOrientationConfigFunction, - referenceLineLayerConfigFunction, + referenceLineLayerFunction, + extendedReferenceLineLayerFunction, axisTitlesVisibilityConfigFunction, } from './expression_functions'; diff --git a/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts b/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts index 2fee0b418d642..b0bd9e2af3aa9 100644 --- a/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts +++ b/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts @@ -33,6 +33,8 @@ import { LEGEND_CONFIG, DATA_LAYER, AXIS_EXTENT_CONFIG, + EXTENDED_DATA_LAYER, + EXTENDED_REFERENCE_LINE_LAYER, } from '../constants'; export type LayerType = $Values; @@ -87,6 +89,18 @@ export interface XYDataLayerConfig { splitAccessor?: string; palette?: PaletteOutput; } + +export interface XYExtendedDataLayerConfig { + layerId: string; + accessors: string[]; + seriesType: SeriesType; + xAccessor?: string; + hide?: boolean; + yConfig?: YConfigResult[]; + splitAccessor?: string; + palette?: PaletteOutput; +} + export interface ValidLayer extends DataLayerConfigResult { xAccessor: NonNullable; } @@ -100,6 +114,16 @@ export type DataLayerArgs = XYDataLayerConfig & { palette: PaletteOutput; }; +export type ExtendedDataLayerArgs = XYExtendedDataLayerConfig & { + columnToLabel?: string; // Actually a JSON key-value pair + yScaleType: YScaleType; + xScaleType: XScaleType; + isHistogram: boolean; + // palette will always be set on the expression + palette: PaletteOutput; + table?: Datatable; +}; + export interface LegendConfig { /** * Flag whether the legend should be shown. If there is just a single series, it will be hidden @@ -163,7 +187,54 @@ export interface XYArgs { yRightExtent: AxisExtentConfigResult; legend: LegendConfigResult; valueLabels: ValueLabelMode; - layers: XYLayerConfigResult[]; + dataLayer?: DataLayerConfigResult; + referenceLineLayer?: ReferenceLineLayerConfigResult; + fittingFunction?: FittingFunction; + axisTitlesVisibilitySettings?: AxisTitlesVisibilityConfigResult; + tickLabelsVisibilitySettings?: TickLabelsConfigResult; + gridlinesVisibilitySettings?: GridlinesConfigResult; + labelsOrientation?: LabelsOrientationConfigResult; + curveType?: XYCurveType; + fillOpacity?: number; + hideEndzones?: boolean; + valuesInLegend?: boolean; + ariaLabel?: string; +} + +export interface LayeredXYArgs { + title?: string; + description?: string; + xTitle: string; + yTitle: string; + yRightTitle: string; + yLeftExtent: AxisExtentConfigResult; + yRightExtent: AxisExtentConfigResult; + legend: LegendConfigResult; + valueLabels: ValueLabelMode; + layers: XYExtendedLayerConfigResult[]; + fittingFunction?: FittingFunction; + axisTitlesVisibilitySettings?: AxisTitlesVisibilityConfigResult; + tickLabelsVisibilitySettings?: TickLabelsConfigResult; + gridlinesVisibilitySettings?: GridlinesConfigResult; + labelsOrientation?: LabelsOrientationConfigResult; + curveType?: XYCurveType; + fillOpacity?: number; + hideEndzones?: boolean; + valuesInLegend?: boolean; + ariaLabel?: string; +} + +export interface XYProps { + title?: string; + description?: string; + xTitle: string; + yTitle: string; + yRightTitle: string; + yLeftExtent: AxisExtentConfigResult; + yRightExtent: AxisExtentConfigResult; + legend: LegendConfigResult; + valueLabels: ValueLabelMode; + layers: Array; fittingFunction?: FittingFunction; axisTitlesVisibilitySettings?: AxisTitlesVisibilityConfigResult; tickLabelsVisibilitySettings?: TickLabelsConfigResult; @@ -182,11 +253,25 @@ export interface XYReferenceLineLayerConfig { yConfig?: YConfigResult[]; } +export interface XYExtendedReferenceLineLayerConfig { + layerId: string; + accessors: string[]; + yConfig?: YConfigResult[]; +} + export type ReferenceLineLayerArgs = XYReferenceLineLayerConfig & { columnToLabel?: string; }; +export type ExtendedReferenceLineLayerArgs = XYExtendedReferenceLineLayerConfig & { + columnToLabel?: string; + table?: Datatable; +}; + export type XYLayerConfigResult = DataLayerConfigResult | ReferenceLineLayerConfigResult; +export type XYExtendedLayerConfigResult = + | ExtendedDataLayerConfigResult + | ExtendedReferenceLineLayerConfigResult; export interface LensMultiTable { type: typeof MULTITABLE; @@ -202,9 +287,20 @@ export type ReferenceLineLayerConfigResult = ReferenceLineLayerArgs & { layerType: typeof LayerTypes.REFERENCELINE; }; +export type ExtendedReferenceLineLayerConfigResult = ExtendedReferenceLineLayerArgs & { + type: typeof EXTENDED_REFERENCE_LINE_LAYER; + layerType: typeof LayerTypes.REFERENCELINE; +}; + export type DataLayerConfigResult = DataLayerArgs & { type: typeof DATA_LAYER; layerType: typeof LayerTypes.DATA; + table?: Datatable; +}; + +export type ExtendedDataLayerConfigResult = ExtendedDataLayerArgs & { + type: typeof EXTENDED_DATA_LAYER; + layerType: typeof LayerTypes.DATA; }; export type YConfigResult = YConfig & { type: typeof Y_CONFIG }; diff --git a/src/plugins/chart_expressions/expression_xy/common/types/expression_renderers.ts b/src/plugins/chart_expressions/expression_xy/common/types/expression_renderers.ts index 1acb98903d06b..ec5a3b652248e 100644 --- a/src/plugins/chart_expressions/expression_xy/common/types/expression_renderers.ts +++ b/src/plugins/chart_expressions/expression_xy/common/types/expression_renderers.ts @@ -6,12 +6,13 @@ * Side Public License, v 1. */ +import { Datatable } from '../../../../expressions'; import { XY_VIS_RENDERER } from '../constants'; -import { LensMultiTable, XYArgs } from './expression_functions'; +import { XYProps } from './expression_functions'; export interface XYChartProps { - data: LensMultiTable; - args: XYArgs; + data: Datatable; + args: XYProps; } export interface XYRender { diff --git a/src/plugins/chart_expressions/expression_xy/public/plugin.ts b/src/plugins/chart_expressions/expression_xy/public/plugin.ts index 9f175b03d6743..953da47d6ae11 100755 --- a/src/plugins/chart_expressions/expression_xy/public/plugin.ts +++ b/src/plugins/chart_expressions/expression_xy/public/plugin.ts @@ -15,14 +15,17 @@ import { CoreSetup, CoreStart, IUiSettingsClient } from '../../../../core/public import { ExpressionXyPluginSetup, ExpressionXyPluginStart, SetupDeps } from './types'; import { xyVisFunction, + layeredXyVisFunction, + dataLayerFunction, + extendedDataLayerFunction, yAxisConfigFunction, legendConfigFunction, gridlinesConfigFunction, - dataLayerConfigFunction, axisExtentConfigFunction, tickLabelsConfigFunction, + referenceLineLayerFunction, + extendedReferenceLineLayerFunction, labelsOrientationConfigFunction, - referenceLineLayerConfigFunction, axisTitlesVisibilityConfigFunction, } from '../common'; import { GetStartDepsFn, getXyChartRenderer } from './expression_renderers'; @@ -50,13 +53,16 @@ export class ExpressionXyPlugin { expressions.registerFunction(yAxisConfigFunction); expressions.registerFunction(legendConfigFunction); expressions.registerFunction(gridlinesConfigFunction); - expressions.registerFunction(dataLayerConfigFunction); + expressions.registerFunction(dataLayerFunction); + expressions.registerFunction(extendedDataLayerFunction); expressions.registerFunction(axisExtentConfigFunction); expressions.registerFunction(tickLabelsConfigFunction); expressions.registerFunction(labelsOrientationConfigFunction); - expressions.registerFunction(referenceLineLayerConfigFunction); + expressions.registerFunction(referenceLineLayerFunction); + expressions.registerFunction(extendedReferenceLineLayerFunction); expressions.registerFunction(axisTitlesVisibilityConfigFunction); expressions.registerFunction(xyVisFunction); + expressions.registerFunction(layeredXyVisFunction); const getStartDeps: GetStartDepsFn = async () => { const [coreStart, deps] = await core.getStartServices(); From d3065be1bbda5a8d60bc78259c1449b2bdc6f4b1 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Mon, 21 Mar 2022 19:29:22 +0200 Subject: [PATCH 051/133] Added support of tables at layers. --- .../expression_xy/common/__mocks__/index.ts | 34 +- .../expression_functions/data_layer.test.ts | 13 +- .../common/expression_functions/data_layer.ts | 15 +- .../extended_data_layer.ts | 11 +- .../extended_reference_line_layer.ts | 13 +- .../expression_functions/layered_xy_vis.ts | 6 +- .../reference_line_layer.ts | 15 +- .../expression_functions/xy_vis.test.ts | 7 +- .../common/expression_functions/xy_vis.ts | 11 +- .../expression_xy/common/index.ts | 4 + .../common/types/expression_functions.ts | 17 +- .../common/types/expression_renderers.ts | 2 - .../expression_xy/public/__mocks__/index.tsx | 58 +- .../public/components/legend_action.test.tsx | 33 +- .../public/components/legend_action.tsx | 7 +- .../public/components/reference_lines.tsx | 26 +- .../public/components/x_domain.tsx | 20 +- .../public/components/xy_chart.test.tsx | 970 ++++++++---------- .../public/components/xy_chart.tsx | 64 +- .../public/helpers/axes_configuration.test.ts | 15 +- .../public/helpers/axes_configuration.ts | 36 +- .../public/helpers/color_assignment.test.ts | 183 ++-- .../public/helpers/color_assignment.ts | 58 +- .../public/helpers/interval.test.ts | 21 +- .../expression_xy/public/helpers/interval.ts | 6 +- .../expression_xy/public/helpers/layers.ts | 39 +- .../public/helpers/reference_lines.ts | 12 +- .../expression_xy/public/helpers/state.ts | 6 +- .../public/helpers/visualization.ts | 24 +- 29 files changed, 801 insertions(+), 925 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/common/__mocks__/index.ts b/src/plugins/chart_expressions/expression_xy/common/__mocks__/index.ts index 4bafffc065835..74488d52b74c8 100644 --- a/src/plugins/chart_expressions/expression_xy/common/__mocks__/index.ts +++ b/src/plugins/chart_expressions/expression_xy/common/__mocks__/index.ts @@ -10,7 +10,7 @@ import { Position } from '@elastic/charts'; import { PaletteOutput } from 'src/plugins/charts/common'; import { Datatable, DatatableRow } from 'src/plugins/expressions'; import { LayerTypes } from '../constants'; -import { DataLayerConfigResult, LensMultiTable, XYArgs } from '../types'; +import { DataLayerConfigResult, XYProps } from '../types'; export const mockPaletteOutput: PaletteOutput = { type: 'palette', @@ -48,7 +48,6 @@ export const createSampleDatatableWithRows = (rows: DatatableRow[]): Datatable = export const sampleLayer: DataLayerConfigResult = { type: 'dataLayer', - layerId: 'first', layerType: LayerTypes.DATA, seriesType: 'line', xAccessor: 'c', @@ -59,9 +58,12 @@ export const sampleLayer: DataLayerConfigResult = { yScaleType: 'linear', isHistogram: false, palette: mockPaletteOutput, + table: createSampleDatatableWithRows([]), }; -export const createArgsWithLayers = (layers: DataLayerConfigResult[] = [sampleLayer]): XYArgs => ({ +export const createArgsWithLayers = ( + layers: DataLayerConfigResult | DataLayerConfigResult[] = sampleLayer +): XYProps => ({ xTitle: '', yTitle: '', yRightTitle: '', @@ -104,25 +106,17 @@ export const createArgsWithLayers = (layers: DataLayerConfigResult[] = [sampleLa mode: 'full', type: 'axisExtentConfig', }, - layers, + layers: Array.isArray(layers) ? layers : [layers], }); export function sampleArgs() { - const data: LensMultiTable = { - type: 'lens_multitable', - tables: { - first: createSampleDatatableWithRows([ - { a: 1, b: 2, c: 'I', d: 'Foo' }, - { a: 1, b: 5, c: 'J', d: 'Bar' }, - ]), - }, - dateRange: { - fromDate: new Date('2019-01-02T05:00:00.000Z'), - toDate: new Date('2019-01-03T05:00:00.000Z'), - }, - }; + const data = createSampleDatatableWithRows([ + { a: 1, b: 2, c: 'I', d: 'Foo' }, + { a: 1, b: 5, c: 'J', d: 'Bar' }, + ]); - const args: XYArgs = createArgsWithLayers(); - - return { data, args }; + return { + data, + args: createArgsWithLayers({ ...sampleLayer, table: data }), + }; } diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer.test.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer.test.ts index 05be77a966278..14673ba25f864 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer.test.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer.test.ts @@ -9,13 +9,13 @@ import { DataLayerArgs } from '../types'; import { dataLayerFunction } from '.'; import { createMockExecutionContext } from '../../../../expressions/common/mocks'; -import { mockPaletteOutput } from '../__mocks__'; +import { mockPaletteOutput, sampleArgs } from '../__mocks__'; import { LayerTypes } from '../constants'; describe('dataLayerConfig', () => { test('produces the correct arguments', () => { + const { data } = sampleArgs(); const args: DataLayerArgs = { - layerId: 'first', seriesType: 'line', xAccessor: 'c', accessors: ['a', 'b'], @@ -26,8 +26,13 @@ describe('dataLayerConfig', () => { palette: mockPaletteOutput, }; - const result = dataLayerFunction.fn(null, args, createMockExecutionContext()); + const result = dataLayerFunction.fn(data, args, createMockExecutionContext()); - expect(result).toEqual({ type: 'dataLayer', layerType: LayerTypes.DATA, ...args }); + expect(result).toEqual({ + type: 'dataLayer', + layerType: LayerTypes.DATA, + ...args, + table: data, + }); }); }); diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer.ts index d9e7f0eaa7bac..1568a1232854b 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer.ts @@ -7,7 +7,7 @@ */ import { i18n } from '@kbn/i18n'; -import type { ExpressionFunctionDefinition } from '../../../../expressions/common'; +import type { Datatable, ExpressionFunctionDefinition } from '../../../../expressions/common'; import { DataLayerArgs, DataLayerConfigResult } from '../types'; import { DATA_LAYER, @@ -20,7 +20,7 @@ import { export const dataLayerFunction: ExpressionFunctionDefinition< typeof DATA_LAYER, - null, + Datatable, DataLayerArgs, DataLayerConfigResult > = { @@ -30,7 +30,7 @@ export const dataLayerFunction: ExpressionFunctionDefinition< help: i18n.translate('expressionXY.dataLayer.help', { defaultMessage: `Configure a layer in the xy chart`, }), - inputTypes: ['null'], + inputTypes: ['datatable'], args: { hide: { types: ['boolean'], @@ -39,12 +39,6 @@ export const dataLayerFunction: ExpressionFunctionDefinition< defaultMessage: 'Show / hide axis', }), }, - layerId: { - types: ['string'], - help: i18n.translate('expressionXY.dataLayer.layerId.help', { - defaultMessage: 'Layer ID', - }), - }, xAccessor: { types: ['string'], help: i18n.translate('expressionXY.dataLayer.xAccessor.help', { @@ -113,11 +107,12 @@ export const dataLayerFunction: ExpressionFunctionDefinition< types: ['palette'], }, }, - fn(input, args) { + fn(table, args) { return { type: DATA_LAYER, ...args, layerType: LayerTypes.DATA, + table, }; }, }; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_data_layer.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_data_layer.ts index cb20603f94a95..01ee4f2a8c40b 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_data_layer.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_data_layer.ts @@ -20,7 +20,7 @@ import { export const extendedDataLayerFunction: ExpressionFunctionDefinition< typeof EXTENDED_DATA_LAYER, - Datatable | null, + Datatable, ExtendedDataLayerArgs, ExtendedDataLayerConfigResult > = { @@ -30,7 +30,7 @@ export const extendedDataLayerFunction: ExpressionFunctionDefinition< help: i18n.translate('expressionXY.dataLayer.help', { defaultMessage: `Configure a layer in the xy chart`, }), - inputTypes: ['null', 'datatable'], + inputTypes: ['datatable'], args: { hide: { types: ['boolean'], @@ -39,12 +39,6 @@ export const extendedDataLayerFunction: ExpressionFunctionDefinition< defaultMessage: 'Show / hide axis', }), }, - layerId: { - types: ['string'], - help: i18n.translate('expressionXY.dataLayer.layerId.help', { - defaultMessage: 'Layer ID', - }), - }, xAccessor: { types: ['string'], help: i18n.translate('expressionXY.dataLayer.xAccessor.help', { @@ -124,6 +118,7 @@ export const extendedDataLayerFunction: ExpressionFunctionDefinition< type: EXTENDED_DATA_LAYER, ...args, layerType: LayerTypes.DATA, + table: args.table ?? input, }; }, }; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_reference_line_layer.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_reference_line_layer.ts index c59d763607648..7765591b05810 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_reference_line_layer.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_reference_line_layer.ts @@ -7,13 +7,13 @@ */ import { i18n } from '@kbn/i18n'; -import type { ExpressionFunctionDefinition } from '../../../../expressions/common'; +import type { Datatable, ExpressionFunctionDefinition } from '../../../../expressions/common'; import { LayerTypes, EXTENDED_REFERENCE_LINE_LAYER, Y_CONFIG } from '../constants'; import { ExtendedReferenceLineLayerArgs, ExtendedReferenceLineLayerConfigResult } from '../types'; export const extendedReferenceLineLayerFunction: ExpressionFunctionDefinition< typeof EXTENDED_REFERENCE_LINE_LAYER, - null, + Datatable, ExtendedReferenceLineLayerArgs, ExtendedReferenceLineLayerConfigResult > = { @@ -23,14 +23,8 @@ export const extendedReferenceLineLayerFunction: ExpressionFunctionDefinition< help: i18n.translate('expressionXY.referenceLineLayer.help', { defaultMessage: `Configure a reference line in the xy chart`, }), - inputTypes: ['null'], + inputTypes: ['datatable'], args: { - layerId: { - types: ['string'], - help: i18n.translate('expressionXY.referenceLineLayer.layerId.help', { - defaultMessage: `Layer ID`, - }), - }, accessors: { types: ['string'], help: i18n.translate('expressionXY.referenceLineLayer.accessors.help', { @@ -63,6 +57,7 @@ export const extendedReferenceLineLayerFunction: ExpressionFunctionDefinition< type: EXTENDED_REFERENCE_LINE_LAYER, ...args, layerType: LayerTypes.REFERENCELINE, + table: args.table ?? input, }; }, }; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts index 2f254ef9d0a38..efbd718cbc530 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts @@ -174,8 +174,9 @@ export const layeredXyVisFunction: ExpressionFunctionDefinition< const layers = args.layers.filter( (layer): layer is XYExtendedLayerConfigResult => layer !== undefined ); - const tables = layers.reduce>((t, { layerId }) => { - t[layerId] = data; + + const tables = layers.reduce>((t, layer, index) => { + t[index] = data; return t; }, {}); @@ -187,7 +188,6 @@ export const layeredXyVisFunction: ExpressionFunctionDefinition< type: 'render', as: XY_VIS_RENDERER, value: { - data, args: { ...args, layers, diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line_layer.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line_layer.ts index b735e3c3864a0..5cb75884f31bb 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line_layer.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line_layer.ts @@ -7,13 +7,13 @@ */ import { i18n } from '@kbn/i18n'; -import type { ExpressionFunctionDefinition } from '../../../../expressions/common'; +import type { Datatable, ExpressionFunctionDefinition } from '../../../../expressions/common'; import { LayerTypes, REFERENCE_LINE_LAYER, Y_CONFIG } from '../constants'; import { ReferenceLineLayerArgs, ReferenceLineLayerConfigResult } from '../types'; export const referenceLineLayerFunction: ExpressionFunctionDefinition< typeof REFERENCE_LINE_LAYER, - null, + Datatable, ReferenceLineLayerArgs, ReferenceLineLayerConfigResult > = { @@ -23,14 +23,8 @@ export const referenceLineLayerFunction: ExpressionFunctionDefinition< help: i18n.translate('expressionXY.referenceLineLayer.help', { defaultMessage: `Configure a reference line in the xy chart`, }), - inputTypes: ['null'], + inputTypes: ['datatable'], args: { - layerId: { - types: ['string'], - help: i18n.translate('expressionXY.referenceLineLayer.layerId.help', { - defaultMessage: `Layer ID`, - }), - }, accessors: { types: ['string'], help: i18n.translate('expressionXY.referenceLineLayer.accessors.help', { @@ -52,11 +46,12 @@ export const referenceLineLayerFunction: ExpressionFunctionDefinition< }), }, }, - fn(input, args) { + fn(table, args) { return { type: REFERENCE_LINE_LAYER, ...args, layerType: LayerTypes.REFERENCELINE, + table, }; }, }; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.test.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.test.ts index 69d0bf51b3d64..4f6549106965d 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.test.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.test.ts @@ -16,6 +16,11 @@ describe('xyVis', () => { const { data, args } = sampleArgs(); const result = xyVisFunction.fn(data, args, createMockExecutionContext()); - expect(result).toEqual({ type: 'render', as: XY_VIS, value: { data, args } }); + const { layers, ...rest } = args; + expect(result).toEqual({ + type: 'render', + as: XY_VIS, + value: { args: { ...rest, layers } }, + }); }); }); diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts index c7f4733d5ed6c..3af1d360b714f 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts @@ -176,11 +176,13 @@ export const xyVisFunction: ExpressionFunctionDefinition< }, }, fn(data, args, handlers) { - const layers = [args.dataLayer, args.referenceLineLayer].filter( + const { dataLayer, referenceLineLayer, ...restArgs } = args; + const layers = [dataLayer, referenceLineLayer].filter( (layer): layer is XYLayerConfigResult => layer !== undefined ); - const tables = layers.reduce>((t, { layerId }) => { - t[layerId] = data; + + const tables = layers.reduce>((t, layer, index) => { + t[index] = data; return t; }, {}); @@ -192,9 +194,8 @@ export const xyVisFunction: ExpressionFunctionDefinition< type: 'render', as: XY_VIS_RENDERER, value: { - data, args: { - ...args, + ...restArgs, layers, ariaLabel: args.ariaLabel ?? diff --git a/src/plugins/chart_expressions/expression_xy/common/index.ts b/src/plugins/chart_expressions/expression_xy/common/index.ts index 66710c27177e9..b9f4c02c9b76e 100755 --- a/src/plugins/chart_expressions/expression_xy/common/index.ts +++ b/src/plugins/chart_expressions/expression_xy/common/index.ts @@ -59,8 +59,12 @@ export type { AxisExtentConfigResult, ReferenceLineLayerArgs, LabelsOrientationConfig, + CommonXYLayerConfigResult, XYReferenceLineLayerConfig, + XYExtendedLayerConfigResult, LabelsOrientationConfigResult, + CommonXYDataLayerConfigResult, ReferenceLineLayerConfigResult, AxisTitlesVisibilityConfigResult, + CommonXYReferenceLineLayerConfigResult, } from './types'; diff --git a/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts b/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts index b0bd9e2af3aa9..e758cf057cf6f 100644 --- a/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts +++ b/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts @@ -80,7 +80,6 @@ export interface YConfig { } export interface XYDataLayerConfig { - layerId: string; accessors: string[]; seriesType: SeriesType; xAccessor?: string; @@ -91,7 +90,6 @@ export interface XYDataLayerConfig { } export interface XYExtendedDataLayerConfig { - layerId: string; accessors: string[]; seriesType: SeriesType; xAccessor?: string; @@ -234,7 +232,7 @@ export interface XYProps { yRightExtent: AxisExtentConfigResult; legend: LegendConfigResult; valueLabels: ValueLabelMode; - layers: Array; + layers: CommonXYLayerConfigResult[]; fittingFunction?: FittingFunction; axisTitlesVisibilitySettings?: AxisTitlesVisibilityConfigResult; tickLabelsVisibilitySettings?: TickLabelsConfigResult; @@ -248,13 +246,11 @@ export interface XYProps { } export interface XYReferenceLineLayerConfig { - layerId: string; accessors: string[]; yConfig?: YConfigResult[]; } export interface XYExtendedReferenceLineLayerConfig { - layerId: string; accessors: string[]; yConfig?: YConfigResult[]; } @@ -285,22 +281,25 @@ export interface LensMultiTable { export type ReferenceLineLayerConfigResult = ReferenceLineLayerArgs & { type: typeof REFERENCE_LINE_LAYER; layerType: typeof LayerTypes.REFERENCELINE; + table: Datatable; }; export type ExtendedReferenceLineLayerConfigResult = ExtendedReferenceLineLayerArgs & { type: typeof EXTENDED_REFERENCE_LINE_LAYER; layerType: typeof LayerTypes.REFERENCELINE; + table: Datatable; }; export type DataLayerConfigResult = DataLayerArgs & { type: typeof DATA_LAYER; layerType: typeof LayerTypes.DATA; - table?: Datatable; + table: Datatable; }; export type ExtendedDataLayerConfigResult = ExtendedDataLayerArgs & { type: typeof EXTENDED_DATA_LAYER; layerType: typeof LayerTypes.DATA; + table: Datatable; }; export type YConfigResult = YConfig & { type: typeof Y_CONFIG }; @@ -317,3 +316,9 @@ export type LegendConfigResult = LegendConfig & { type: typeof LEGEND_CONFIG }; export type AxisExtentConfigResult = AxisExtentConfig & { type: typeof AXIS_EXTENT_CONFIG }; export type GridlinesConfigResult = AxesSettingsConfig & { type: typeof GRID_LINES_CONFIG }; export type TickLabelsConfigResult = AxesSettingsConfig & { type: typeof TICK_LABELS_CONFIG }; + +export type CommonXYLayerConfigResult = XYLayerConfigResult | XYExtendedLayerConfigResult; +export type CommonXYDataLayerConfigResult = DataLayerConfigResult | ExtendedDataLayerConfigResult; +export type CommonXYReferenceLineLayerConfigResult = + | ReferenceLineLayerConfigResult + | ExtendedReferenceLineLayerConfigResult; diff --git a/src/plugins/chart_expressions/expression_xy/common/types/expression_renderers.ts b/src/plugins/chart_expressions/expression_xy/common/types/expression_renderers.ts index ec5a3b652248e..e8201b1c5bfa7 100644 --- a/src/plugins/chart_expressions/expression_xy/common/types/expression_renderers.ts +++ b/src/plugins/chart_expressions/expression_xy/common/types/expression_renderers.ts @@ -6,12 +6,10 @@ * Side Public License, v 1. */ -import { Datatable } from '../../../../expressions'; import { XY_VIS_RENDERER } from '../constants'; import { XYProps } from './expression_functions'; export interface XYChartProps { - data: Datatable; args: XYProps; } diff --git a/src/plugins/chart_expressions/expression_xy/public/__mocks__/index.tsx b/src/plugins/chart_expressions/expression_xy/public/__mocks__/index.tsx index cc73950438f38..4bc4f722f44c4 100644 --- a/src/plugins/chart_expressions/expression_xy/public/__mocks__/index.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/__mocks__/index.tsx @@ -7,8 +7,9 @@ */ import { chartPluginMock } from '../../../../../plugins/charts/public/mocks'; -import { DataLayerConfigResult, LensMultiTable, XYArgs } from '../../common'; +import { DataLayerConfigResult, LensMultiTable } from '../../common'; import { LayerTypes } from '../../common/constants'; +import { XYProps } from '../../common/types'; import { mockPaletteOutput, sampleArgs } from '../../common/__mocks__'; const chartSetupContract = chartPluginMock.createSetupContract(); @@ -168,7 +169,6 @@ export const dateHistogramData: LensMultiTable = { export const dateHistogramLayer: DataLayerConfigResult = { type: 'dataLayer', - layerId: 'timeLayer', layerType: LayerTypes.DATA, hide: false, xAccessor: 'xAccessorId', @@ -179,46 +179,36 @@ export const dateHistogramLayer: DataLayerConfigResult = { seriesType: 'bar_stacked', accessors: ['yAccessorId'], palette: mockPaletteOutput, + table: dateHistogramData.tables.timeLayer, }; export function sampleArgsWithReferenceLine(value: number = 150) { - const { data, args } = sampleArgs(); + const { args: sArgs, data } = sampleArgs(); + const args: XYProps = { + ...sArgs, + layers: [ + { + type: 'referenceLineLayer', + layerType: LayerTypes.REFERENCELINE, + accessors: ['referenceLine-a'], + yConfig: [{ axisMode: 'left', forAccessor: 'referenceLine-a', type: 'yConfig' }], + table: data, + }, + ], + }; return { data: { - ...data, - tables: { - ...data.tables, - referenceLine: { - type: 'datatable', - columns: [ - { - id: 'referenceLine-a', - meta: { params: { id: 'number' }, type: 'number' }, - name: 'Static value', - }, - ], - rows: [{ 'referenceLine-a': value }], - }, - }, - } as LensMultiTable, - args: { - ...args, - layers: [ - ...args.layers, + type: 'datatable', + columns: [ { - layerType: LayerTypes.REFERENCELINE, - accessors: ['referenceLine-a'], - layerId: 'referenceLine', - seriesType: 'line', - xScaleType: 'linear', - yScaleType: 'linear', - palette: mockPaletteOutput, - isHistogram: false, - hide: true, - yConfig: [{ axisMode: 'left', forAccessor: 'referenceLine-a', type: 'yConfig' }], + id: 'referenceLine-a', + meta: { params: { id: 'number' }, type: 'number' }, + name: 'Static value', }, ], - } as XYArgs, + rows: [{ 'referenceLine-a': value }], + }, + args, }; } diff --git a/src/plugins/chart_expressions/expression_xy/public/components/legend_action.test.tsx b/src/plugins/chart_expressions/expression_xy/public/components/legend_action.test.tsx index 0f1cdebc5bf59..c62d0fda94960 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/legend_action.test.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/legend_action.test.tsx @@ -11,27 +11,12 @@ import { LegendActionProps, SeriesIdentifier } from '@elastic/charts'; import { EuiPopover } from '@elastic/eui'; import { mountWithIntl } from '@kbn/test-jest-helpers'; import { ComponentType, ReactWrapper } from 'enzyme'; -import type { LensMultiTable } from '../../common'; +import type { DataLayerConfigResult, LensMultiTable } from '../../common'; import { LayerTypes } from '../../common/constants'; -import type { DataLayerArgs } from '../../common'; import { getLegendAction } from './legend_action'; import { LegendActionPopover } from './legend_action_popover'; import { mockPaletteOutput } from '../../common/__mocks__'; -const sampleLayer = { - layerId: 'first', - layerType: LayerTypes.DATA, - seriesType: 'line', - xAccessor: 'c', - accessors: ['a', 'b'], - splitAccessor: 'splitAccessorId', - columnToLabel: '{"a": "Label A", "b": "Label B", "d": "Label D"}', - xScaleType: 'ordinal', - yScaleType: 'linear', - isHistogram: false, - palette: mockPaletteOutput, -} as DataLayerArgs; - const tables = { first: { type: 'datatable', @@ -168,11 +153,25 @@ const tables = { }, } as LensMultiTable['tables']; +const sampleLayer: DataLayerConfigResult = { + type: 'dataLayer', + layerType: LayerTypes.DATA, + seriesType: 'line', + xAccessor: 'c', + accessors: ['a', 'b'], + splitAccessor: 'splitAccessorId', + columnToLabel: '{"a": "Label A", "b": "Label B", "d": "Label D"}', + xScaleType: 'ordinal', + yScaleType: 'linear', + isHistogram: false, + palette: mockPaletteOutput, + table: tables.first, +}; + describe('getLegendAction', function () { let wrapperProps: LegendActionProps; const Component: ComponentType = getLegendAction( [sampleLayer], - tables, jest.fn(), jest.fn(), {} diff --git a/src/plugins/chart_expressions/expression_xy/public/components/legend_action.tsx b/src/plugins/chart_expressions/expression_xy/public/components/legend_action.tsx index 9bbdec3635fa8..e0467eac25c1f 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/legend_action.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/legend_action.tsx @@ -9,13 +9,12 @@ import React from 'react'; import type { LegendAction, XYChartSeriesIdentifier } from '@elastic/charts'; import type { FilterEvent } from '../types'; -import type { LensMultiTable, DataLayerArgs } from '../../common'; +import type { CommonXYDataLayerConfigResult } from '../../common'; import type { FormatFactory } from '../types'; import { LegendActionPopover } from './legend_action_popover'; export const getLegendAction = ( - filteredLayers: DataLayerArgs[], - tables: LensMultiTable['tables'], + filteredLayers: CommonXYDataLayerConfigResult[], onFilter: (data: FilterEvent['data']) => void, formatFactory: FormatFactory, layersAlreadyFormatted: Record @@ -33,7 +32,7 @@ export const getLegendAction = ( const splitLabel = series.seriesKeys[0] as string; const accessor = layer.splitAccessor; - const table = tables[layer.layerId]; + const { table } = layer; const splitColumn = table.columns.find(({ id }) => id === layer.splitAccessor); const formatter = formatFactory(splitColumn && splitColumn.meta?.params); diff --git a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.tsx b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.tsx index 7666415465381..74ac4ef22ffae 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.tsx @@ -14,7 +14,12 @@ import { EuiIcon } from '@elastic/eui'; import { RectAnnotation, AnnotationDomainType, LineAnnotation, Position } from '@elastic/charts'; import { euiLightVars } from '@kbn/ui-theme'; import type { FieldFormat } from '../../../../field_formats/common'; -import type { ReferenceLineLayerConfigResult, IconPosition, YAxisMode } from '../../common'; +import type { + CommonXYReferenceLineLayerConfigResult, + ReferenceLineLayerConfigResult, + IconPosition, + YAxisMode, +} from '../../common'; import type { LensMultiTable } from '../../common/types'; import { hasIcon } from '../helpers'; @@ -56,7 +61,7 @@ export const computeChartMargins = ( // Note: it does not take into consideration whether the reference line is in view or not export const getReferenceLineRequiredPaddings = ( - referenceLineLayers: ReferenceLineLayerConfigResult[], + referenceLineLayers: CommonXYReferenceLineLayerConfigResult[], axesMap: Record<'left' | 'right', unknown> ) => { // collect all paddings for the 4 axis: if any text is detected double it. @@ -188,8 +193,7 @@ function getMarkerToShow( } export interface ReferenceLineAnnotationsProps { - layers: ReferenceLineLayerConfigResult[]; - data: LensMultiTable; + layers: CommonXYReferenceLineLayerConfigResult[]; formatters: Record<'left' | 'right' | 'bottom', FieldFormat | undefined>; axesMap: Record<'left' | 'right', boolean>; isHorizontal: boolean; @@ -198,7 +202,6 @@ export interface ReferenceLineAnnotationsProps { export const ReferenceLineAnnotations = ({ layers, - data, formatters, axesMap, isHorizontal, @@ -206,15 +209,14 @@ export const ReferenceLineAnnotations = ({ }: ReferenceLineAnnotationsProps) => { return ( <> - {layers.flatMap((layer) => { + {layers.flatMap((layer, index) => { if (!layer.yConfig) { return []; } - const { columnToLabel, yConfig: yConfigs, layerId } = layer; + const { columnToLabel, yConfig: yConfigs, table } = layer; const columnToLabelMap: Record = columnToLabel ? JSON.parse(columnToLabel) : {}; - const table = data.tables[layerId]; const row = table.rows[0]; @@ -288,8 +290,8 @@ export const ReferenceLineAnnotations = ({ annotations.push( ({ dataValue: row[yConfig.forAccessor], header: columnToLabelMap[yConfig.forAccessor], @@ -319,8 +321,8 @@ export const ReferenceLineAnnotations = ({ annotations.push( { const nextValue = shouldCheckNextReferenceLine ? row[groupedByDirection[yConfig.fill!][indexFromSameType + 1].forAccessor] diff --git a/src/plugins/chart_expressions/expression_xy/public/components/x_domain.tsx b/src/plugins/chart_expressions/expression_xy/public/components/x_domain.tsx index dbc4c348cb891..05029de14fdb7 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/x_domain.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/x_domain.tsx @@ -10,7 +10,7 @@ import { uniq } from 'lodash'; import React from 'react'; import moment from 'moment'; import { Endzones } from '../../../../../plugins/charts/public'; -import type { LensMultiTable, DataLayerArgs } from '../../common'; +import type { CommonXYDataLayerConfigResult } from '../../common'; import { search } from '../../../../../plugins/data/public'; export interface XDomain { @@ -19,11 +19,10 @@ export interface XDomain { minInterval?: number; } -export const getAppliedTimeRange = (layers: DataLayerArgs[], data: LensMultiTable) => { - return Object.entries(data.tables) - .map(([tableId, table]) => { - const layer = layers.find((l) => l.layerId === tableId); - const xColumn = table.columns.find((col) => col.id === layer?.xAccessor); +export const getAppliedTimeRange = (layers: CommonXYDataLayerConfigResult[]) => { + return layers + .map(({ xAccessor, table }) => { + const xColumn = table.columns.find((col) => col.id === xAccessor); const timeRange = xColumn && search.aggs.getDateHistogramMetaDataByDatatableColumn(xColumn)?.timeRange; if (timeRange) { @@ -37,13 +36,12 @@ export const getAppliedTimeRange = (layers: DataLayerArgs[], data: LensMultiTabl }; export const getXDomain = ( - layers: DataLayerArgs[], - data: LensMultiTable, + layers: CommonXYDataLayerConfigResult[], minInterval: number | undefined, isTimeViz: boolean, isHistogram: boolean ) => { - const appliedTimeRange = getAppliedTimeRange(layers, data)?.timeRange; + const appliedTimeRange = getAppliedTimeRange(layers)?.timeRange; const from = appliedTimeRange?.from; const to = appliedTimeRange?.to; const baseDomain = isTimeViz @@ -59,8 +57,8 @@ export const getXDomain = ( if (isHistogram && isFullyQualified(baseDomain)) { const xValues = uniq( layers - .flatMap((layer) => - data.tables[layer.layerId].rows.map((row) => row[layer.xAccessor!].valueOf() as number) + .flatMap(({ table, xAccessor }) => + table.rows.map((row) => row[xAccessor!].valueOf() as number) ) .sort() ); diff --git a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx index e99a85b33c1f0..98f57a7a74944 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx @@ -9,7 +9,8 @@ import React from 'react'; import { shallow } from 'enzyme'; import { mountWithIntl } from '@kbn/test-jest-helpers'; -import { DataLayerConfigResult, LensMultiTable, XYArgs } from '../../common'; +import { Datatable } from '../../../../expressions/common'; +import { DataLayerConfigResult } from '../../common'; import { LayerTypes } from '../../common/constants'; import { AreaSeries, @@ -45,6 +46,7 @@ import { sampleLayer, } from '../../common/__mocks__'; import { XYChart, XYChartRenderProps } from './xy_chart'; +import { ExtendedDataLayerConfigResult, XYProps } from '../../common/types'; const onClickValue = jest.fn(); const onSelectRange = jest.fn(); @@ -54,45 +56,36 @@ describe('XYChart component', () => { let convertSpy: jest.Mock; let defaultProps: Omit; - const dataWithoutFormats: LensMultiTable = { - type: 'lens_multitable', - tables: { - first: { - type: 'datatable', - columns: [ - { id: 'a', name: 'a', meta: { type: 'number' } }, - { id: 'b', name: 'b', meta: { type: 'number' } }, - { id: 'c', name: 'c', meta: { type: 'string' } }, - { id: 'd', name: 'd', meta: { type: 'string' } }, - ], - rows: [ - { a: 1, b: 2, c: 'I', d: 'Row 1' }, - { a: 1, b: 5, c: 'J', d: 'Row 2' }, - ], - }, - }, + const dataWithoutFormats: Datatable = { + type: 'datatable', + columns: [ + { id: 'a', name: 'a', meta: { type: 'number' } }, + { id: 'b', name: 'b', meta: { type: 'number' } }, + { id: 'c', name: 'c', meta: { type: 'string' } }, + { id: 'd', name: 'd', meta: { type: 'string' } }, + ], + rows: [ + { a: 1, b: 2, c: 'I', d: 'Row 1' }, + { a: 1, b: 5, c: 'J', d: 'Row 2' }, + ], }; - const dataWithFormats: LensMultiTable = { - type: 'lens_multitable', - tables: { - first: { - type: 'datatable', - columns: [ - { id: 'a', name: 'a', meta: { type: 'number' } }, - { id: 'b', name: 'b', meta: { type: 'number' } }, - { id: 'c', name: 'c', meta: { type: 'string' } }, - { id: 'd', name: 'd', meta: { type: 'string', params: { id: 'custom' } } }, - ], - rows: [ - { a: 1, b: 2, c: 'I', d: 'Row 1' }, - { a: 1, b: 5, c: 'J', d: 'Row 2' }, - ], - }, - }, + + const dataWithFormats: Datatable = { + type: 'datatable', + columns: [ + { id: 'a', name: 'a', meta: { type: 'number' } }, + { id: 'b', name: 'b', meta: { type: 'number' } }, + { id: 'c', name: 'c', meta: { type: 'string' } }, + { id: 'd', name: 'd', meta: { type: 'string', params: { id: 'custom' } } }, + ], + rows: [ + { a: 1, b: 2, c: 'I', d: 'Row 1' }, + { a: 1, b: 5, c: 'J', d: 'Row 2' }, + ], }; - const getRenderedComponent = (data: LensMultiTable, args: XYArgs) => { - return shallow(); + const getRenderedComponent = (args: XYProps) => { + return shallow(); }; beforeEach(() => { @@ -121,7 +114,6 @@ describe('XYChart component', () => { const component = shallow( { ...args.layers[0], seriesType: 'line', type: 'dataLayer', + table: data, } as DataLayerConfigResult, ], }} @@ -141,9 +134,10 @@ describe('XYChart component', () => { }); describe('date range', () => { + const { data, args } = sampleArgs(); + const timeSampleLayer: DataLayerConfigResult = { type: 'dataLayer', - layerId: 'first', layerType: LayerTypes.DATA, seriesType: 'line', xAccessor: 'c', @@ -154,49 +148,39 @@ describe('XYChart component', () => { yScaleType: 'linear', isHistogram: false, palette: mockPaletteOutput, + table: { + ...data, + columns: data.columns.map((c) => + c.id !== 'c' + ? c + : { + ...c, + meta: { + type: 'date', + source: 'esaggs', + sourceParams: { + type: 'date_histogram', + params: {}, + appliedTimeRange: { + from: '2019-01-02T05:00:00.000Z', + to: '2019-01-03T05:00:00.000Z', + }, + }, + }, + } + ), + }, }; + const multiLayerArgs = createArgsWithLayers([ timeSampleLayer, - { - ...timeSampleLayer, - layerId: 'second', - seriesType: 'bar', - xScaleType: 'time', - }, + { ...timeSampleLayer, seriesType: 'bar', xScaleType: 'time' }, ]); - test('it uses the full date range', () => { - const { data, args } = sampleArgs(); + test('it uses the full date range', () => { const component = shallow( - c.id !== 'c' - ? c - : { - ...c, - meta: { - type: 'date', - source: 'esaggs', - sourceParams: { - type: 'date_histogram', - params: {}, - appliedTimeRange: { - from: '2019-01-02T05:00:00.000Z', - to: '2019-01-03T05:00:00.000Z', - }, - }, - }, - } - ), - }, - }, - }} args={{ ...args, layers: [ @@ -221,15 +205,21 @@ describe('XYChart component', () => { }); test('it uses passed in minInterval', () => { - const data: LensMultiTable = { - type: 'lens_multitable', - tables: { - first: createSampleDatatableWithRows([{ a: 1, b: 2, c: 'I', d: 'Foo' }]), - second: createSampleDatatableWithRows([]), - }, - }; + const table1 = createSampleDatatableWithRows([{ a: 1, b: 2, c: 'I', d: 'Foo' }]); + const table2 = createSampleDatatableWithRows([]); - const component = shallow(); + const component = shallow( + + ); // real auto interval is 30mins = 1800000 expect(component.find(Settings).prop('xDomain')).toMatchInlineSnapshot(` @@ -244,7 +234,6 @@ describe('XYChart component', () => { describe('axis time', () => { const defaultTimeLayer: DataLayerConfigResult = { type: 'dataLayer', - layerId: 'first', layerType: LayerTypes.DATA, seriesType: 'line', xAccessor: 'c', @@ -255,21 +244,28 @@ describe('XYChart component', () => { yScaleType: 'linear', isHistogram: true, palette: mockPaletteOutput, + table: data, }; - test('it should disable the new time axis for a line time layer when isHistogram is set to false', () => { - const { data } = sampleArgs(); + const newData = { + ...data, + dateRange: { + fromDate: new Date('2019-01-02T05:00:00.000Z'), + toDate: new Date('2019-01-03T05:00:00.000Z'), + }, + }; + + test('it should disable the new time axis for a line time layer when isHistogram is set to false', () => { const instance = shallow( ({ + ...layer, + table: newData, + })), }} - args={multiLayerArgs} /> ); @@ -278,20 +274,18 @@ describe('XYChart component', () => { expect(axisStyle).toBe(0); }); test('it should enable the new time axis for a line time layer when isHistogram is set to true', () => { - const { data } = sampleArgs(); const timeLayerArgs = createArgsWithLayers([defaultTimeLayer]); const instance = shallow( ({ + ...layer, + table: newData, + })), }} - args={timeLayerArgs} /> ); @@ -300,7 +294,6 @@ describe('XYChart component', () => { expect(axisStyle).toBe(3); }); test('it should disable the new time axis for a vertical bar with break down dimension', () => { - const { data } = sampleArgs(); const timeLayer: DataLayerConfigResult = { ...defaultTimeLayer, seriesType: 'bar', @@ -310,14 +303,13 @@ describe('XYChart component', () => { const instance = shallow( ({ + ...layer, + table: newData, + })), }} - args={timeLayerArgs} /> ); @@ -327,7 +319,6 @@ describe('XYChart component', () => { }); test('it should enable the new time axis for a stacked vertical bar with break down dimension', () => { - const { data } = sampleArgs(); const timeLayer: DataLayerConfigResult = { ...defaultTimeLayer, seriesType: 'bar_stacked', @@ -337,14 +328,13 @@ describe('XYChart component', () => { const instance = shallow( ({ + ...layer, + table: newData, + })), }} - args={timeLayerArgs} /> ); @@ -354,45 +344,36 @@ describe('XYChart component', () => { }); }); describe('endzones', () => { - const { args } = sampleArgs(); const table = createSampleDatatableWithRows([ { a: 1, b: 2, c: new Date('2021-04-22').valueOf(), d: 'Foo' }, { a: 1, b: 2, c: new Date('2021-04-23').valueOf(), d: 'Foo' }, { a: 1, b: 2, c: new Date('2021-04-24').valueOf(), d: 'Foo' }, ]); - const data: LensMultiTable = { - type: 'lens_multitable', - tables: { - first: { - ...table, - columns: table.columns.map((c) => - c.id !== 'c' - ? c - : { - ...c, - meta: { - type: 'date', - source: 'esaggs', - sourceParams: { - type: 'date_histogram', - params: {}, - appliedTimeRange: { - from: '2021-04-22T12:00:00.000Z', - to: '2021-04-24T12:00:00.000Z', - }, - }, + const newData = { + ...table, + type: 'datatable', + + columns: table.columns.map((c) => + c.id !== 'c' + ? c + : { + ...c, + meta: { + type: 'date', + source: 'esaggs', + sourceParams: { + type: 'date_histogram', + params: {}, + appliedTimeRange: { + from: '2021-04-22T12:00:00.000Z', + to: '2021-04-24T12:00:00.000Z', }, - } - ), - }, - }, - dateRange: { - // first and last bucket are partial - fromDate: new Date('2021-04-22T12:00:00.000Z'), - toDate: new Date('2021-04-24T12:00:00.000Z'), - }, + }, + }, + } + ), }; - const timeArgs: XYArgs = { + const timeArgs: XYProps = { ...args, layers: [ { @@ -402,18 +383,14 @@ describe('XYChart component', () => { xScaleType: 'time', isHistogram: true, splitAccessor: undefined, + table: newData, } as DataLayerConfigResult, ], }; test('it extends interval if data is exceeding it', () => { const component = shallow( - + ); expect(component.find(Settings).prop('xDomain')).toEqual({ @@ -425,14 +402,17 @@ describe('XYChart component', () => { }); }); + const defaultTimeArgs = { + ...timeArgs, + layers: timeArgs.layers.map((layer) => ({ + ...layer, + table: data, + })), + }; + test('it renders endzone component bridging gap between domain and extended domain', () => { const component = shallow( - + ); expect(component.find(XyEndzones).dive().find('Endzones').props()).toEqual( @@ -447,12 +427,7 @@ describe('XYChart component', () => { test('should pass enabled histogram mode and min interval to endzones component', () => { const component = shallow( - + ); expect(component.find(XyEndzones).dive().find('Endzones').props()).toEqual( @@ -468,7 +443,6 @@ describe('XYChart component', () => { { yScaleType: 'linear', isHistogram: true, palette: { type: 'palette', name: 'default' }, + table: data, }, ], }} @@ -500,8 +475,7 @@ describe('XYChart component', () => { ); @@ -512,12 +486,11 @@ describe('XYChart component', () => { describe('y axis extents', () => { test('it passes custom y axis extents to elastic-charts axis spec', () => { - const { data, args } = sampleArgs(); + const { args } = sampleArgs(); const component = shallow( { }); test('it passes fit to bounds y axis extents to elastic-charts axis spec', () => { - const { data, args } = sampleArgs(); + const { args } = sampleArgs(); const component = shallow( { const component = shallow( { yScaleType: 'linear', isHistogram: false, palette: { type: 'palette', name: 'default' }, + table: data, }, ], }} @@ -600,7 +572,6 @@ describe('XYChart component', () => { const component = shallow( { yScaleType: 'linear', isHistogram: false, palette: { type: 'palette', name: 'default' }, + table: data, }, ], }} @@ -632,9 +604,9 @@ describe('XYChart component', () => { }); test('it does include referenceLine values when in full extent mode', () => { - const { data, args } = sampleArgsWithReferenceLine(); + const { args } = sampleArgsWithReferenceLine(); - const component = shallow(); + const component = shallow(); expect(component.find(Axis).find('[id="left"]').prop('domain')).toEqual({ fit: false, min: 0, @@ -643,12 +615,11 @@ describe('XYChart component', () => { }); test('it should ignore referenceLine values when set to custom extents', () => { - const { data, args } = sampleArgsWithReferenceLine(); + const { args } = sampleArgsWithReferenceLine(); const component = shallow( { }); test('it should work for negative values in referenceLines', () => { - const { data, args } = sampleArgsWithReferenceLine(-150); + const { args } = sampleArgsWithReferenceLine(-150); - const component = shallow(); + const component = shallow(); expect(component.find(Axis).find('[id="left"]').prop('domain')).toEqual({ fit: false, min: -150, @@ -685,13 +656,6 @@ describe('XYChart component', () => { const component = shallow( { yScaleType: 'linear', isHistogram: false, palette: { type: 'palette', name: 'default' }, + table: data, }, ], }} @@ -720,7 +685,6 @@ describe('XYChart component', () => { { layerType: 'data', yScaleType: 'linear', palette: { type: 'palette', name: 'default' }, + table: data, }, ], }} @@ -746,15 +711,15 @@ describe('XYChart component', () => { }); test('disabled legend extra by default', () => { - const { data, args } = sampleArgs(); - const component = shallow(); + const { args } = sampleArgs(); + const component = shallow(); expect(component.find(Settings).at(0).prop('showLegendExtra')).toEqual(false); }); test('ignores legend extra for ordinal chart', () => { - const { data, args } = sampleArgs(); + const { args } = sampleArgs(); const component = shallow( - + ); expect(component.find(Settings).at(0).prop('showLegendExtra')).toEqual(false); }); @@ -764,7 +729,6 @@ describe('XYChart component', () => { const component = shallow( { const component = shallow( { yScaleType: 'linear', isHistogram: false, palette: { type: 'palette', name: 'default' }, + table: data, }, ], }} @@ -809,7 +773,6 @@ describe('XYChart component', () => { const component = shallow( { yScaleType: 'linear', isHistogram: false, palette: { type: 'palette', name: 'default' }, + table: data, }, ], }} @@ -838,7 +802,6 @@ describe('XYChart component', () => { const component = shallow( { yScaleType: 'linear', isHistogram: false, palette: { type: 'palette', name: 'default' }, + table: data, }, ], }} @@ -865,11 +829,15 @@ describe('XYChart component', () => { test('it renders regular bar empty placeholder for no results', () => { const { data, args } = sampleArgs(); - - // send empty data to the chart - data.tables.first.rows = []; - - const component = shallow(); + const component = shallow( + ({ ...layer, table: { ...data, rows: [] } })), + }} + /> + ); expect(component.find(BarSeries)).toHaveLength(0); expect(component.find(EmptyPlaceholder).prop('icon')).toBeDefined(); @@ -881,7 +849,6 @@ describe('XYChart component', () => { const wrapper = mountWithIntl( { test('onBrushEnd returns correct context data for number histogram data', () => { const { args } = sampleArgs(); + const numberHistogramData: Datatable = { + type: 'datatable', + rows: [ + { + xAccessorId: 5, + yAccessorId: 1, + }, + { + xAccessorId: 7, + yAccessorId: 1, + }, + { + xAccessorId: 8, + yAccessorId: 1, + }, + { + xAccessorId: 10, + yAccessorId: 1, + }, + ], + columns: [ + { + id: 'xAccessorId', + name: 'bytes', + meta: { type: 'number' }, + }, + { + id: 'yAccessorId', + name: 'Count of records', + meta: { type: 'number' }, + }, + ], + }; + const numberLayer: DataLayerConfigResult = { - layerId: 'numberLayer', type: 'dataLayer', layerType: LayerTypes.DATA, hide: false, @@ -912,55 +912,12 @@ describe('XYChart component', () => { seriesType: 'bar_stacked', accessors: ['yAccessorId'], palette: mockPaletteOutput, - }; - - const numberHistogramData: LensMultiTable = { - type: 'lens_multitable', - tables: { - numberLayer: { - type: 'datatable', - rows: [ - { - xAccessorId: 5, - yAccessorId: 1, - }, - { - xAccessorId: 7, - yAccessorId: 1, - }, - { - xAccessorId: 8, - yAccessorId: 1, - }, - { - xAccessorId: 10, - yAccessorId: 1, - }, - ], - columns: [ - { - id: 'xAccessorId', - name: 'bytes', - meta: { type: 'number' }, - }, - { - id: 'yAccessorId', - name: 'Count of records', - meta: { type: 'number' }, - }, - ], - }, - }, - dateRange: { - fromDate: new Date('2020-04-01T16:14:16.246Z'), - toDate: new Date('2020-04-01T17:15:41.263Z'), - }, + table: numberHistogramData, }; const wrapper = mountWithIntl( { expect(onSelectRange).toHaveBeenCalledWith({ column: 0, - table: numberHistogramData.tables.numberLayer, + table: numberHistogramData, range: [5, 8], }); }); test('onBrushEnd is not set on non-interactive mode', () => { - const { args, data } = sampleArgs(); + const { args } = sampleArgs(); - const wrapper = mountWithIntl( - - ); + const wrapper = mountWithIntl(); expect(wrapper.find(Settings).first().prop('onBrushEnd')).toBeUndefined(); }); @@ -993,7 +948,6 @@ describe('XYChart component', () => { const wrapper = mountWithIntl( { const wrapper = mountWithIntl( { accessors: ['d'], columnToLabel: '{"a": "Label A", "b": "Label B", "d": "Label D"}', palette: mockPaletteOutput, + table: data, }, ], }} @@ -1050,13 +1003,13 @@ describe('XYChart component', () => { { column: 1, row: 1, - table: data.tables.first, + table: data, value: 5, }, { column: 1, row: 0, - table: data.tables.first, + table: data, value: 2, }, ], @@ -1084,7 +1037,6 @@ describe('XYChart component', () => { const wrapper = mountWithIntl( { test('onElementClick returns correct context data for numeric histogram', () => { const { args } = sampleArgs(); + const numberHistogramData: Datatable = { + type: 'datatable', + rows: [ + { + xAccessorId: 5, + yAccessorId: 1, + }, + { + xAccessorId: 7, + yAccessorId: 1, + }, + { + xAccessorId: 8, + yAccessorId: 1, + }, + { + xAccessorId: 10, + yAccessorId: 1, + }, + ], + columns: [ + { + id: 'xAccessorId', + name: 'bytes', + meta: { type: 'number' }, + }, + { + id: 'yAccessorId', + name: 'Count of records', + meta: { type: 'number' }, + }, + ], + }; + const numberLayer: DataLayerConfigResult = { type: 'dataLayer', - layerId: 'numberLayer', layerType: LayerTypes.DATA, hide: false, xAccessor: 'xAccessorId', @@ -1123,50 +1108,9 @@ describe('XYChart component', () => { seriesType: 'bar_stacked', accessors: ['yAccessorId'], palette: mockPaletteOutput, + table: numberHistogramData, }; - const numberHistogramData: LensMultiTable = { - type: 'lens_multitable', - tables: { - numberLayer: { - type: 'datatable', - rows: [ - { - xAccessorId: 5, - yAccessorId: 1, - }, - { - xAccessorId: 7, - yAccessorId: 1, - }, - { - xAccessorId: 8, - yAccessorId: 1, - }, - { - xAccessorId: 10, - yAccessorId: 1, - }, - ], - columns: [ - { - id: 'xAccessorId', - name: 'bytes', - meta: { type: 'number' }, - }, - { - id: 'yAccessorId', - name: 'Count of records', - meta: { type: 'number' }, - }, - ], - }, - }, - dateRange: { - fromDate: new Date('2020-04-01T16:14:16.246Z'), - toDate: new Date('2020-04-01T17:15:41.263Z'), - }, - }; const geometry: GeometryValue = { x: 5, y: 1, @@ -1185,7 +1129,6 @@ describe('XYChart component', () => { const wrapper = mountWithIntl( { { column: 0, row: 0, - table: numberHistogramData.tables.numberLayer, + table: numberHistogramData, value: 5, }, ], @@ -1227,12 +1170,10 @@ describe('XYChart component', () => { const wrapper = mountWithIntl( { yScaleType: 'linear', isHistogram: false, palette: mockPaletteOutput, + table: data, }, ], }} @@ -1258,7 +1200,7 @@ describe('XYChart component', () => { { column: 3, row: 1, - table: data.tables.first, + table: data, value: 'Bar', }, ], @@ -1267,20 +1209,29 @@ describe('XYChart component', () => { test('sets up correct yScaleType equal to binary_linear for bytes formatting', () => { const { args, data } = sampleArgs(); - data.tables.first.columns[0].meta = { - type: 'number', - params: { id: 'bytes', params: { pattern: '0,0.00b' } }, + + const [firstCol, ...rest] = data.columns; + const newData: Datatable = { + ...data, + columns: [ + { + ...firstCol, + meta: { + type: 'number', + params: { id: 'bytes', params: { pattern: '0,0.00b' } }, + }, + }, + ...rest, + ], }; const wrapper = mountWithIntl( { yScaleType: 'linear', isHistogram: false, palette: mockPaletteOutput, + table: newData, }, ], }} @@ -1306,12 +1258,10 @@ describe('XYChart component', () => { const wrapper = mountWithIntl( { yScaleType: 'linear', isHistogram: false, palette: mockPaletteOutput, + table: data, }, ], }} @@ -1332,21 +1283,17 @@ describe('XYChart component', () => { }); test('onElementClick is not triggering event on non-interactive mode', () => { - const { args, data } = sampleArgs(); + const { args } = sampleArgs(); - const wrapper = mountWithIntl( - - ); + const wrapper = mountWithIntl(); expect(wrapper.find(Settings).first().prop('onElementClick')).toBeUndefined(); }); test('legendAction is not triggering event on non-interactive mode', () => { - const { args, data } = sampleArgs(); + const { args } = sampleArgs(); - const wrapper = mountWithIntl( - - ); + const wrapper = mountWithIntl(); expect(wrapper.find(Settings).first().prop('legendAction')).toBeUndefined(); }); @@ -1356,7 +1303,6 @@ describe('XYChart component', () => { const component = shallow( { yScaleType: 'linear', isHistogram: false, palette: mockPaletteOutput, + table: data, }, ], }} @@ -1385,7 +1332,6 @@ describe('XYChart component', () => { const component = shallow( { yScaleType: 'linear', isHistogram: false, palette: mockPaletteOutput, + table: data, }, ], }} @@ -1414,7 +1361,6 @@ describe('XYChart component', () => { const component = shallow( { yScaleType: 'linear', isHistogram: false, palette: mockPaletteOutput, + table: data, }, ], }} @@ -1445,7 +1392,6 @@ describe('XYChart component', () => { const component = shallow( { yScaleType: 'linear', isHistogram: false, palette: mockPaletteOutput, + table: data, }, ], }} @@ -1471,10 +1418,8 @@ describe('XYChart component', () => { }); test('it passes time zone to the series', () => { - const { data, args } = sampleArgs(); - const component = shallow( - - ); + const { args } = sampleArgs(); + const component = shallow(); expect(component.find(LineSeries).at(0).prop('timeZone')).toEqual('CEST'); expect(component.find(LineSeries).at(1).prop('timeZone')).toEqual('CEST'); }); @@ -1491,10 +1436,11 @@ describe('XYChart component', () => { xScaleType: 'ordinal', yScaleType: 'linear', palette: mockPaletteOutput, + table: data, }; delete firstLayer.splitAccessor; const component = shallow( - + ); expect(component.find(BarSeries).at(0).prop('enableHistogramMode')).toEqual(true); }); @@ -1510,10 +1456,11 @@ describe('XYChart component', () => { xScaleType: 'ordinal', yScaleType: 'linear', palette: mockPaletteOutput, + table: data, }; delete firstLayer.splitAccessor; const component = shallow( - + ); expect(component.find(BarSeries).at(0).prop('enableHistogramMode')).toEqual(false); expect(component.find(BarSeries).at(1).prop('enableHistogramMode')).toEqual(false); @@ -1530,6 +1477,7 @@ describe('XYChart component', () => { xScaleType: 'ordinal', yScaleType: 'linear', palette: mockPaletteOutput, + table: data, }; delete firstLayer.splitAccessor; const secondLayer: DataLayerConfigResult = { @@ -1541,14 +1489,11 @@ describe('XYChart component', () => { xScaleType: 'ordinal', yScaleType: 'linear', palette: mockPaletteOutput, + table: data, }; delete secondLayer.splitAccessor; const component = shallow( - + ); expect(component.find(LineSeries).at(0).prop('enableHistogramMode')).toEqual(true); expect(component.find(LineSeries).at(1).prop('enableHistogramMode')).toEqual(true); @@ -1559,7 +1504,6 @@ describe('XYChart component', () => { const component = shallow( { xScaleType: 'ordinal', yScaleType: 'linear', palette: mockPaletteOutput, + table: data, }, ], }} @@ -1586,7 +1531,6 @@ describe('XYChart component', () => { const component = shallow( { xScaleType: 'ordinal', yScaleType: 'linear', palette: mockPaletteOutput, + table: data, }, ], }} @@ -1611,15 +1556,21 @@ describe('XYChart component', () => { describe('y axes', () => { test('single axis if possible', () => { const args = createArgsWithLayers(); - - const component = getRenderedComponent(dataWithoutFormats, args); + const newArgs = { + ...args, + layers: args.layers.map((layer) => ({ + ...layer, + table: dataWithoutFormats, + })), + }; + const component = getRenderedComponent(newArgs); const axes = component.find(Axis); expect(axes).toHaveLength(2); }); test('multiple axes because of config', () => { const args = createArgsWithLayers(); - const newArgs = { + const newArgs: XYProps = { ...args, layers: [ { @@ -1627,19 +1578,22 @@ describe('XYChart component', () => { accessors: ['a', 'b'], yConfig: [ { + type: 'yConfig', forAccessor: 'a', axisMode: 'left', }, { + type: 'yConfig', forAccessor: 'b', axisMode: 'right', }, ], + table: dataWithoutFormats, }, ], - } as XYArgs; + }; - const component = getRenderedComponent(dataWithoutFormats, newArgs); + const component = getRenderedComponent(newArgs); const axes = component.find(Axis); expect(axes).toHaveLength(3); expect(component.find(LineSeries).at(0).prop('groupId')).toEqual(axes.at(1).prop('groupId')); @@ -1648,17 +1602,18 @@ describe('XYChart component', () => { test('multiple axes because of incompatible formatters', () => { const args = createArgsWithLayers(); - const newArgs = { + const newArgs: XYProps = { ...args, layers: [ { ...args.layers[0], accessors: ['c', 'd'], + table: dataWithFormats, }, ], - } as XYArgs; + }; - const component = getRenderedComponent(dataWithFormats, newArgs); + const component = getRenderedComponent(newArgs); const axes = component.find(Axis); expect(axes).toHaveLength(3); expect(component.find(LineSeries).at(0).prop('groupId')).toEqual(axes.at(1).prop('groupId')); @@ -1667,7 +1622,7 @@ describe('XYChart component', () => { test('single axis despite different formatters if enforced', () => { const args = createArgsWithLayers(); - const newArgs = { + const newArgs: XYProps = { ...args, layers: [ { @@ -1675,19 +1630,22 @@ describe('XYChart component', () => { accessors: ['c', 'd'], yConfig: [ { + type: 'yConfig', forAccessor: 'c', axisMode: 'left', }, { + type: 'yConfig', forAccessor: 'd', axisMode: 'left', }, ], + table: dataWithoutFormats, }, ], - } as XYArgs; + }; - const component = getRenderedComponent(dataWithoutFormats, newArgs); + const component = getRenderedComponent(newArgs); const axes = component.find(Axis); expect(axes).toHaveLength(2); }); @@ -1696,39 +1654,44 @@ describe('XYChart component', () => { describe('y series coloring', () => { test('color is applied to chart for multiple series', () => { const args = createArgsWithLayers(); - const newArgs = { + const newArgs: XYProps = { ...args, layers: [ { ...args.layers[0], - splitAccessor: undefined, accessors: ['a', 'b'], + splitAccessor: undefined, yConfig: [ { + type: 'yConfig', forAccessor: 'a', color: '#550000', }, { + type: 'yConfig', forAccessor: 'b', color: '#FFFF00', }, ], - }, + table: dataWithoutFormats, + } as ExtendedDataLayerConfigResult, { ...args.layers[0], - splitAccessor: undefined, accessors: ['c'], + splitAccessor: undefined, yConfig: [ { + type: 'yConfig', forAccessor: 'c', color: '#FEECDF', }, ], - }, + table: dataWithoutFormats, + } as ExtendedDataLayerConfigResult, ], - } as XYArgs; + }; - const component = getRenderedComponent(dataWithoutFormats, newArgs); + const component = getRenderedComponent(newArgs); expect( (component.find(LineSeries).at(0).prop('color') as Function)!({ yAccessor: 'a', @@ -1750,7 +1713,7 @@ describe('XYChart component', () => { }); test('color is not applied to chart when splitAccessor is defined or when yConfig is not configured', () => { const args = createArgsWithLayers(); - const newArgs = { + const newArgs: XYProps = { ...args, layers: [ { @@ -1758,20 +1721,22 @@ describe('XYChart component', () => { accessors: ['a'], yConfig: [ { + type: 'yConfig', forAccessor: 'a', color: '#550000', }, ], + table: dataWithoutFormats, }, { ...args.layers[0], - splitAccessor: undefined, accessors: ['c'], + table: dataWithoutFormats, }, ], - } as XYArgs; + }; - const component = getRenderedComponent(dataWithoutFormats, newArgs); + const component = getRenderedComponent(newArgs); expect( (component.find(LineSeries).at(0).prop('color') as Function)!({ yAccessor: 'a', @@ -1806,11 +1771,12 @@ describe('XYChart component', () => { accessors: ['a'], splitAccessor: undefined, columnToLabel: '', + table: dataWithoutFormats, }, ], }; - const component = getRenderedComponent(dataWithoutFormats, newArgs); + const component = getRenderedComponent(newArgs); const nameFn = component.find(LineSeries).prop('name') as SeriesNameFn; // In this case, the ID is used as the name. This shouldn't happen in practice @@ -1828,11 +1794,12 @@ describe('XYChart component', () => { accessors: ['a'], splitAccessor: undefined, columnToLabel: '{"a":""}', + table: dataWithoutFormats, }, ], }; - const component = getRenderedComponent(dataWithoutFormats, newArgs); + const component = getRenderedComponent(newArgs); const nameFn = component.find(LineSeries).prop('name') as SeriesNameFn; // In this case, the ID is used as the name. This shouldn't happen in practice @@ -1850,11 +1817,12 @@ describe('XYChart component', () => { accessors: ['a'], splitAccessor: undefined, columnToLabel: '{"a":"Column A"}', + table: dataWithoutFormats, }, ], }; - const component = getRenderedComponent(dataWithoutFormats, newArgs); + const component = getRenderedComponent(newArgs); const nameFn = component.find(LineSeries).prop('name') as SeriesNameFn; expect(nameFn({ ...nameFnArgs, seriesKeys: ['a'] }, false)).toEqual('Column A'); @@ -1870,11 +1838,12 @@ describe('XYChart component', () => { accessors: ['a', 'b'], splitAccessor: undefined, columnToLabel: '{"a": "Label A"}', + table: dataWithoutFormats, }, ], }; - const component = getRenderedComponent(dataWithoutFormats, newArgs); + const component = getRenderedComponent(newArgs); const nameFn1 = component.find(LineSeries).at(0).prop('name') as SeriesNameFn; const nameFn2 = component.find(LineSeries).at(1).prop('name') as SeriesNameFn; @@ -1895,11 +1864,12 @@ describe('XYChart component', () => { accessors: ['a'], splitAccessor: 'd', columnToLabel: '{"a": "Label A"}', + table: dataWithoutFormats, }, ], }; - const component = getRenderedComponent(dataWithoutFormats, newArgs); + const component = getRenderedComponent(newArgs); const nameFn = component.find(LineSeries).prop('name') as SeriesNameFn; expect(nameFn({ ...nameFnArgs, seriesKeys: ['split1', 'a'] }, false)).toEqual('split1'); @@ -1915,11 +1885,12 @@ describe('XYChart component', () => { accessors: ['a'], splitAccessor: 'd', columnToLabel: '{"a": "Label A"}', + table: dataWithFormats, }, ], }; - const component = getRenderedComponent(dataWithFormats, newArgs); + const component = getRenderedComponent(newArgs); const nameFn = component.find(LineSeries).prop('name') as SeriesNameFn; convertSpy.mockReturnValueOnce('formatted'); @@ -1937,11 +1908,12 @@ describe('XYChart component', () => { accessors: ['a', 'b'], splitAccessor: 'd', columnToLabel: '{"a": "Label A","b": "Label B"}', + table: dataWithoutFormats, }, ], }; - const component = getRenderedComponent(dataWithoutFormats, newArgs); + const component = getRenderedComponent(newArgs); const nameFn1 = component.find(LineSeries).at(0).prop('name') as SeriesNameFn; const nameFn2 = component.find(LineSeries).at(0).prop('name') as SeriesNameFn; @@ -1963,11 +1935,12 @@ describe('XYChart component', () => { accessors: ['a', 'b'], splitAccessor: 'd', columnToLabel: '{"a": "Label A","b": "Label B"}', + table: dataWithFormats, }, ], }; - const component = getRenderedComponent(dataWithFormats, newArgs); + const component = getRenderedComponent(newArgs); const nameFn1 = component.find(LineSeries).at(0).prop('name') as SeriesNameFn; const nameFn2 = component.find(LineSeries).at(1).prop('name') as SeriesNameFn; @@ -1982,12 +1955,11 @@ describe('XYChart component', () => { }); test('it set the scale of the x axis according to the args prop', () => { - const { data, args } = sampleArgs(); + const { args } = sampleArgs(); const component = shallow( { }); test('it set the scale of the y axis according to the args prop', () => { - const { data, args } = sampleArgs(); + const { args } = sampleArgs(); const component = shallow( { }); test('it gets the formatter for the x axis', () => { - const { data, args } = sampleArgs(); + const { args } = sampleArgs(); - shallow(); + shallow(); expect(getFormatSpy).toHaveBeenCalledWith({ id: 'string' }); }); test('it gets the formatter for the y axis if there is only one accessor', () => { - const { data, args } = sampleArgs(); + const { args } = sampleArgs(); shallow( ); @@ -2051,9 +2021,9 @@ describe('XYChart component', () => { }); test('it should pass the formatter function to the axis', () => { - const { data, args } = sampleArgs(); + const { args } = sampleArgs(); - const instance = shallow(); + const instance = shallow(); const tickFormatter = instance.find(Axis).first().prop('tickFormat'); @@ -2067,7 +2037,7 @@ describe('XYChart component', () => { }); test('it should set the tickLabel visibility on the x axis if the tick labels is hidden', () => { - const { data, args } = sampleArgs(); + const { args } = sampleArgs(); args.tickLabelsVisibilitySettings = { x: false, @@ -2076,7 +2046,7 @@ describe('XYChart component', () => { type: 'tickLabelsConfig', }; - const instance = shallow(); + const instance = shallow(); const axisStyle = instance.find(Axis).first().prop('style'); @@ -2088,7 +2058,7 @@ describe('XYChart component', () => { }); test('it should set the tickLabel visibility on the y axis if the tick labels is hidden', () => { - const { data, args } = sampleArgs(); + const { args } = sampleArgs(); args.tickLabelsVisibilitySettings = { x: true, @@ -2097,7 +2067,7 @@ describe('XYChart component', () => { type: 'tickLabelsConfig', }; - const instance = shallow(); + const instance = shallow(); const axisStyle = instance.find(Axis).at(1).prop('style'); @@ -2109,7 +2079,7 @@ describe('XYChart component', () => { }); test('it should set the tickLabel visibility on the x axis if the tick labels is shown', () => { - const { data, args } = sampleArgs(); + const { args } = sampleArgs(); args.tickLabelsVisibilitySettings = { x: true, @@ -2118,7 +2088,7 @@ describe('XYChart component', () => { type: 'tickLabelsConfig', }; - const instance = shallow(); + const instance = shallow(); const axisStyle = instance.find(Axis).first().prop('style'); @@ -2130,7 +2100,7 @@ describe('XYChart component', () => { }); test('it should set the tickLabel orientation on the x axis', () => { - const { data, args } = sampleArgs(); + const { args } = sampleArgs(); args.labelsOrientation = { x: -45, @@ -2139,7 +2109,7 @@ describe('XYChart component', () => { type: 'labelsOrientationConfig', }; - const instance = shallow(); + const instance = shallow(); const axisStyle = instance.find(Axis).first().prop('style'); @@ -2151,7 +2121,7 @@ describe('XYChart component', () => { }); test('it should set the tickLabel visibility on the y axis if the tick labels is shown', () => { - const { data, args } = sampleArgs(); + const { args } = sampleArgs(); args.tickLabelsVisibilitySettings = { x: false, @@ -2160,7 +2130,7 @@ describe('XYChart component', () => { type: 'tickLabelsConfig', }; - const instance = shallow(); + const instance = shallow(); const axisStyle = instance.find(Axis).at(1).prop('style'); @@ -2172,7 +2142,7 @@ describe('XYChart component', () => { }); test('it should set the tickLabel orientation on the y axis', () => { - const { data, args } = sampleArgs(); + const { args } = sampleArgs(); args.labelsOrientation = { x: -45, @@ -2181,7 +2151,7 @@ describe('XYChart component', () => { type: 'labelsOrientationConfig', }; - const instance = shallow(); + const instance = shallow(); const axisStyle = instance.find(Axis).at(1).prop('style'); @@ -2193,37 +2163,20 @@ describe('XYChart component', () => { }); test('it should remove invalid rows', () => { - const data: LensMultiTable = { - type: 'lens_multitable', - tables: { - first: { - type: 'datatable', - columns: [ - { id: 'a', name: 'a', meta: { type: 'number' } }, - { id: 'b', name: 'b', meta: { type: 'number' } }, - { id: 'c', name: 'c', meta: { type: 'string' } }, - ], - rows: [ - { a: undefined, b: 2, c: 'I', d: 'Row 1' }, - { a: 1, b: 5, c: 'J', d: 'Row 2' }, - ], - }, - second: { - type: 'datatable', - columns: [ - { id: 'a', name: 'a', meta: { type: 'number' } }, - { id: 'b', name: 'b', meta: { type: 'number' } }, - { id: 'c', name: 'c', meta: { type: 'string' } }, - ], - rows: [ - { a: undefined, b: undefined, c: undefined }, - { a: undefined, b: undefined, c: undefined }, - ], - }, - }, + const data: Datatable = { + type: 'datatable', + columns: [ + { id: 'a', name: 'a', meta: { type: 'number' } }, + { id: 'b', name: 'b', meta: { type: 'number' } }, + { id: 'c', name: 'c', meta: { type: 'string' } }, + ], + rows: [ + { a: undefined, b: 2, c: 'I', d: 'Row 1' }, + { a: 1, b: 5, c: 'J', d: 'Row 2' }, + ], }; - const args: XYArgs = { + const args: XYProps = { xTitle: '', yTitle: '', yRightTitle: '', @@ -2257,7 +2210,6 @@ describe('XYChart component', () => { }, layers: [ { - layerId: 'first', type: 'dataLayer', layerType: LayerTypes.DATA, seriesType: 'line', @@ -2269,9 +2221,9 @@ describe('XYChart component', () => { yScaleType: 'linear', isHistogram: false, palette: mockPaletteOutput, + table: data, }, { - layerId: 'second', type: 'dataLayer', layerType: LayerTypes.DATA, seriesType: 'line', @@ -2283,39 +2235,34 @@ describe('XYChart component', () => { yScaleType: 'linear', isHistogram: false, palette: mockPaletteOutput, + table: data, }, ], }; - const component = shallow(); + const component = shallow(); const series = component.find(LineSeries); - // Only one series should be rendered, even though 2 are configured // This one series should only have one row, even though 2 are sent expect(series.prop('data')).toEqual([{ a: 1, b: 5, c: 'J', d: 'Row 2' }]); }); test('it should not remove rows with falsy but non-undefined values', () => { - const data: LensMultiTable = { - type: 'lens_multitable', - tables: { - first: { - type: 'datatable', - columns: [ - { id: 'a', name: 'a', meta: { type: 'number' } }, - { id: 'b', name: 'b', meta: { type: 'number' } }, - { id: 'c', name: 'c', meta: { type: 'number' } }, - ], - rows: [ - { a: 0, b: 2, c: 5 }, - { a: 1, b: 0, c: 7 }, - ], - }, - }, + const data: Datatable = { + type: 'datatable', + columns: [ + { id: 'a', name: 'a', meta: { type: 'number' } }, + { id: 'b', name: 'b', meta: { type: 'number' } }, + { id: 'c', name: 'c', meta: { type: 'number' } }, + ], + rows: [ + { a: 0, b: 2, c: 5 }, + { a: 1, b: 0, c: 7 }, + ], }; - const args: XYArgs = { + const args: XYProps = { xTitle: '', yTitle: '', yRightTitle: '', @@ -2349,7 +2296,6 @@ describe('XYChart component', () => { }, layers: [ { - layerId: 'first', type: 'dataLayer', layerType: LayerTypes.DATA, seriesType: 'line', @@ -2361,11 +2307,12 @@ describe('XYChart component', () => { yScaleType: 'linear', isHistogram: false, palette: mockPaletteOutput, + table: data, }, ], }; - const component = shallow(); + const component = shallow(); const series = component.find(LineSeries); @@ -2376,22 +2323,17 @@ describe('XYChart component', () => { }); test('it should show legend for split series, even with one row', () => { - const data: LensMultiTable = { - type: 'lens_multitable', - tables: { - first: { - type: 'datatable', - columns: [ - { id: 'a', name: 'a', meta: { type: 'number' } }, - { id: 'b', name: 'b', meta: { type: 'number' } }, - { id: 'c', name: 'c', meta: { type: 'string' } }, - ], - rows: [{ a: 1, b: 5, c: 'J' }], - }, - }, + const data: Datatable = { + type: 'datatable', + columns: [ + { id: 'a', name: 'a', meta: { type: 'number' } }, + { id: 'b', name: 'b', meta: { type: 'number' } }, + { id: 'c', name: 'c', meta: { type: 'string' } }, + ], + rows: [{ a: 1, b: 5, c: 'J' }], }; - const args: XYArgs = { + const args: XYProps = { xTitle: '', yTitle: '', yRightTitle: '', @@ -2425,7 +2367,6 @@ describe('XYChart component', () => { }, layers: [ { - layerId: 'first', type: 'dataLayer', layerType: LayerTypes.DATA, seriesType: 'line', @@ -2437,11 +2378,12 @@ describe('XYChart component', () => { yScaleType: 'linear', isHistogram: false, palette: mockPaletteOutput, + table: data, }, ], }; - const component = shallow(); + const component = shallow(); expect(component.find(Settings).prop('showLegend')).toEqual(true); }); @@ -2452,7 +2394,6 @@ describe('XYChart component', () => { const component = shallow( { yScaleType: 'linear', isHistogram: false, palette: mockPaletteOutput, + table: data, }, ], legend: { ...args.legend, isVisible: true, showSingleSeries: true }, @@ -2483,7 +2425,6 @@ describe('XYChart component', () => { const component = shallow( { yScaleType: 'linear', isHistogram: false, palette: mockPaletteOutput, + table: data, }, ], legend: { ...args.legend, isVisible: true, isInside: true }, @@ -2515,12 +2457,11 @@ describe('XYChart component', () => { }); test('it not show legend if isVisible is set to false', () => { - const { data, args } = sampleArgs(); + const { args } = sampleArgs(); const component = shallow( { }); test('it should show legend on right side', () => { - const { data, args } = sampleArgs(); + const { args } = sampleArgs(); const component = shallow( { }); test('it should apply the fitting function to all non-bar series', () => { - const data: LensMultiTable = { - type: 'lens_multitable', - tables: { - first: createSampleDatatableWithRows([ - { a: 1, b: 2, c: 'I', d: 'Foo' }, - { a: 1, b: 5, c: 'J', d: 'Bar' }, - ]), - }, - }; - - const args: XYArgs = createArgsWithLayers([ + const args: XYProps = createArgsWithLayers([ { ...sampleLayer, accessors: ['a'] }, { ...sampleLayer, seriesType: 'bar', accessors: ['a'] }, { ...sampleLayer, seriesType: 'area', accessors: ['a'] }, @@ -2567,7 +2497,7 @@ describe('XYChart component', () => { ]); const component = shallow( - + ); expect(component.find(LineSeries).prop('fit')).toEqual({ type: Fit.Carry }); @@ -2579,27 +2509,27 @@ describe('XYChart component', () => { }); test('it should apply None fitting function if not specified', () => { - const { data, args } = sampleArgs(); + const { args } = sampleArgs(); args.layers[0].accessors = ['a']; - const component = shallow(); + const component = shallow(); expect(component.find(LineSeries).prop('fit')).toEqual({ type: Fit.None }); }); test('it should apply the xTitle if is specified', () => { - const { data, args } = sampleArgs(); + const { args } = sampleArgs(); args.xTitle = 'My custom x-axis title'; - const component = shallow(); + const component = shallow(); expect(component.find(Axis).at(0).prop('title')).toEqual('My custom x-axis title'); }); test('it should hide the X axis title if the corresponding switch is off', () => { - const { data, args } = sampleArgs(); + const { args } = sampleArgs(); args.axisTitlesVisibilitySettings = { x: false, @@ -2608,7 +2538,7 @@ describe('XYChart component', () => { type: 'axisTitlesVisibilityConfig', }; - const component = shallow(); + const component = shallow(); const axisStyle = component.find(Axis).first().prop('style'); @@ -2620,7 +2550,7 @@ describe('XYChart component', () => { }); test('it should show the X axis gridlines if the setting is on', () => { - const { data, args } = sampleArgs(); + const { args } = sampleArgs(); args.gridlinesVisibilitySettings = { x: true, @@ -2629,7 +2559,7 @@ describe('XYChart component', () => { type: 'gridlinesConfig', }; - const component = shallow(); + const component = shallow(); expect(component.find(Axis).at(0).prop('gridLine')).toMatchObject({ visible: true, @@ -2637,44 +2567,35 @@ describe('XYChart component', () => { }); test('it should format the boolean values correctly', () => { - const data: LensMultiTable = { - type: 'lens_multitable', - tables: { - first: { - type: 'datatable', - columns: [ - { - id: 'a', - name: 'a', - meta: { type: 'number', params: { id: 'number', params: { pattern: '0,0.000' } } }, - }, - { - id: 'b', - name: 'b', - meta: { type: 'number', params: { id: 'number', params: { pattern: '000,0' } } }, - }, - { - id: 'c', - name: 'c', - meta: { - type: 'boolean', - params: { id: 'boolean' }, - }, - }, - ], - rows: [ - { a: 5, b: 2, c: 0 }, - { a: 19, b: 5, c: 1 }, - ], + const data: Datatable = { + type: 'datatable', + columns: [ + { + id: 'a', + name: 'a', + meta: { type: 'number', params: { id: 'number', params: { pattern: '0,0.000' } } }, }, - }, - dateRange: { - fromDate: new Date('2019-01-02T05:00:00.000Z'), - toDate: new Date('2019-01-03T05:00:00.000Z'), - }, + { + id: 'b', + name: 'b', + meta: { type: 'number', params: { id: 'number', params: { pattern: '000,0' } } }, + }, + { + id: 'c', + name: 'c', + meta: { + type: 'boolean', + params: { id: 'boolean' }, + }, + }, + ], + rows: [ + { a: 5, b: 2, c: 0 }, + { a: 19, b: 5, c: 1 }, + ], }; + const timeSampleLayer: DataLayerConfigResult = { - layerId: 'first', type: 'dataLayer', layerType: LayerTypes.DATA, seriesType: 'line', @@ -2684,19 +2605,16 @@ describe('XYChart component', () => { yScaleType: 'linear', isHistogram: false, palette: mockPaletteOutput, + table: data, }; + const args = createArgsWithLayers([timeSampleLayer]); const getCustomFormatSpy = jest.fn(); getCustomFormatSpy.mockReturnValue({ convert: jest.fn((x) => Boolean(x)) }); const component = shallow( - + ); expect(component.find(LineSeries).at(1).prop('data')).toEqual([ diff --git a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx index 89c1b62e3ecce..5fc55355b788d 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx @@ -42,8 +42,8 @@ import { RenderMode } from '../../../../expressions/common'; import { FieldFormat } from '../../../../field_formats/common'; import { EmptyPlaceholder } from '../../../../../plugins/charts/public'; import type { FilterEvent, BrushEvent, FormatFactory } from '../types'; -import type { DataLayerConfigResult, SeriesType, XYChartProps } from '../../common'; -import { isHorizontalChart, getSeriesColor } from '../helpers'; +import type { SeriesType, XYChartProps } from '../../common'; +import { isHorizontalChart, getSeriesColor, Series } from '../helpers'; import { ChartsPluginSetup, ChartsPluginStart, @@ -71,7 +71,7 @@ import { ReferenceLineAnnotations, } from './reference_lines'; import { visualizationDefinitions } from '../definitions'; -import { XYLayerConfigResult } from '../../common/types'; +import { CommonXYDataLayerConfigResult } from '../../common/types'; import './xy_chart.scss'; @@ -131,7 +131,6 @@ function getIconForSeriesType(seriesType: SeriesType): IconType { export const XYChartReportable = React.memo(XYChart); export function XYChart({ - data, args, formatFactory, timeZone, @@ -160,30 +159,31 @@ export function XYChart({ const chartTheme = chartsThemeService.useChartsTheme(); const chartBaseTheme = chartsThemeService.useChartsBaseTheme(); const darkMode = chartsThemeService.useDarkMode(); - const filteredLayers = getFilteredLayers(layers, data); - const layersById = filteredLayers.reduce>( - (hashMap, layer) => { - hashMap[layer.layerId] = layer; + const filteredLayers = getFilteredLayers(layers); + const layersById = filteredLayers.reduce>( + (hashMap, layer, index) => { + hashMap[index] = layer; return hashMap; }, {} ); const handleCursorUpdate = useActiveCursor(chartsActiveCursorService, chartRef, { - datatables: Object.values(data.tables), + datatables: layers.map(({ table }) => table), }); if (filteredLayers.length === 0) { - const dataLayers: DataLayerConfigResult[] = layers.filter(isDataLayer); + const dataLayers: CommonXYDataLayerConfigResult[] = layers.filter(isDataLayer); const icon: IconType = dataLayers.length > 0 ? getIconForSeriesType(dataLayers[0].seriesType) : 'bar'; return ; } // use formatting hint of first x axis column to format ticks - const xAxisColumn = data.tables[filteredLayers[0].layerId].columns.find( + const xAxisColumn = filteredLayers[0].table.columns.find( ({ id }) => isDataLayer(filteredLayers[0]) && id === filteredLayers[0].xAccessor ); + const xAxisFormatter = formatFactory(xAxisColumn && xAxisColumn.meta?.params); const layersAlreadyFormatted: Record = {}; @@ -199,12 +199,7 @@ export function XYChart({ filteredLayers.some((layer) => isDataLayer(layer) && layer.splitAccessor); const shouldRotate = isHorizontalChart(filteredLayers); - const yAxesConfiguration = getAxesConfiguration( - filteredLayers, - shouldRotate, - data.tables, - formatFactory - ); + const yAxesConfiguration = getAxesConfiguration(filteredLayers, shouldRotate, formatFactory); const xTitle = args.xTitle || (xAxisColumn && xAxisColumn.name); const axisTitlesVisibilitySettings = args.axisTitlesVisibilitySettings || { @@ -236,7 +231,6 @@ export function XYChart({ const { baseDomain: rawXDomain, extendedDomain: xDomain } = getXDomain( filteredLayers, - data, minInterval, isTimeViz, isHistogramViz @@ -247,17 +241,14 @@ export function XYChart({ right: yAxesConfiguration.find(({ groupId }) => groupId === 'right'), }; - const getYAxesTitles = ( - axisSeries: Array<{ layer: string; accessor: string }>, - groupId: string - ) => { + const getYAxesTitles = (axisSeries: Series[], groupId: string) => { const yTitle = groupId === 'right' ? args.yRightTitle : args.yTitle; return ( yTitle || axisSeries .map( (series) => - data.tables[series.layer].columns.find((column) => column.id === series.accessor)?.name + layers[series.layer].table.columns.find((column) => column.id === series.accessor)?.name ) .filter((name) => Boolean(name))[0] ); @@ -333,16 +324,14 @@ export function XYChart({ // Remove this once the chart will support automatic annotation fit for other type of charts const { min: computedMin, max: computedMax } = computeOverallDataDomain( filteredLayers, - axis.series.map(({ accessor }) => accessor), - data.tables + axis.series.map(({ accessor }) => accessor) ); if (computedMin != null && computedMax != null) { max = Math.max(computedMax, max || 0); min = Math.min(computedMin, min || 0); } - for (const { layerId, yConfig } of referenceLineLayers) { - const table = data.tables[layerId]; + for (const { yConfig, table } of referenceLineLayers) { for (const { axisMode, forAccessor } of yConfig || []) { if (axis.groupId === axisMode) { for (const row of table.rows) { @@ -373,7 +362,7 @@ export function XYChart({ const valueLabelsStyling = shouldShowValueLabels && valueLabels !== 'hide' && getValueLabelsStyling(shouldRotate); - const colorAssignments = getColorAssignments(args.layers, data, formatFactory); + const colorAssignments = getColorAssignments(filteredLayers, formatFactory); const clickHandler: ElementClickListener = ([[geometry, series]]) => { // for xyChart series is always XYChartSeriesIdentifier and geometry is always type of GeometryValue @@ -387,7 +376,7 @@ export function XYChart({ return; } - const table = data.tables[layer.layerId]; + const { table } = layer; const xColumn = table.columns.find((col) => col.id === layer.xAccessor); const currentXFormatter = @@ -452,7 +441,7 @@ export function XYChart({ return; } - const table = data.tables[filteredLayers[0].layerId]; + const { table } = filteredLayers[0]; const xAxisColumnIndex = table.columns.findIndex((el) => el.id === filteredLayers[0].xAccessor); @@ -568,13 +557,7 @@ export function XYChart({ onElementClick={interactive ? clickHandler : undefined} legendAction={ interactive - ? getLegendAction( - filteredLayers, - data.tables, - onClickValue, - formatFactory, - layersAlreadyFormatted - ) + ? getLegendAction(filteredLayers, onClickValue, formatFactory, layersAlreadyFormatted) : undefined } showLegendExtra={isHistogramViz && valuesInLegend} @@ -639,7 +622,7 @@ export function XYChart({ seriesType, accessors, xAccessor, - layerId, + table, columnToLabel, yScaleType, xScaleType, @@ -650,8 +633,6 @@ export function XYChart({ ? JSON.parse(columnToLabel) : {}; - const table = data.tables[layerId]; - const formatterPerColumn = new Map(); for (const column of table.columns) { formatterPerColumn.set(column, formatFactory(column.meta.params)); @@ -727,7 +708,6 @@ export function XYChart({ const formatter = table?.columns.find((column) => column.id === accessor)?.meta?.params; const splitHint = table.columns.find((col) => col.id === splitAccessor)?.meta?.params; const splitFormatter = formatFactory(splitHint); - const seriesProps: SeriesSpec = { splitSeriesAccessors: splitAccessor ? [splitAccessor] : [], stackAccessors: isStacked ? [xAccessor as string] : [], @@ -752,6 +732,7 @@ export function XYChart({ totalSeriesAtDepth: colorAssignment.totalSeriesCount, rankAtDepth: colorAssignment.getRank( layer, + layerIndex, String(seriesKeys[0]), String(yAccessor) ), @@ -884,7 +865,6 @@ export function XYChart({ {referenceLineLayers.length ? ( { const sampleLayer: DataLayerConfigResult = { type: 'dataLayer', - layerId: 'first', layerType: LayerTypes.DATA, seriesType: 'line', xAccessor: 'c', @@ -233,21 +232,22 @@ describe('axes_configuration', () => { yScaleType: 'linear', isHistogram: false, palette: { type: 'palette', name: 'default' }, + table: tables.first, }; it('should map auto series to left axis', () => { const formatFactory = jest.fn(); - const groups = getAxesConfiguration([sampleLayer], false, tables, formatFactory); + const groups = getAxesConfiguration([sampleLayer], false, formatFactory); expect(groups.length).toEqual(1); expect(groups[0].position).toEqual('left'); expect(groups[0].series[0].accessor).toEqual('yAccessorId'); - expect(groups[0].series[0].layer).toEqual('first'); + expect(groups[0].series[0].layer).toEqual(0); }); it('should map auto series to right axis if formatters do not match', () => { const formatFactory = jest.fn(); const twoSeriesLayer = { ...sampleLayer, accessors: ['yAccessorId', 'yAccessorId2'] }; - const groups = getAxesConfiguration([twoSeriesLayer], false, tables, formatFactory); + const groups = getAxesConfiguration([twoSeriesLayer], false, formatFactory); expect(groups.length).toEqual(2); expect(groups[0].position).toEqual('left'); expect(groups[1].position).toEqual('right'); @@ -261,7 +261,7 @@ describe('axes_configuration', () => { ...sampleLayer, accessors: ['yAccessorId', 'yAccessorId2', 'yAccessorId3'], }; - const groups = getAxesConfiguration([threeSeriesLayer], false, tables, formatFactory); + const groups = getAxesConfiguration([threeSeriesLayer], false, formatFactory); expect(groups.length).toEqual(2); expect(groups[0].position).toEqual('left'); expect(groups[1].position).toEqual('right'); @@ -280,13 +280,12 @@ describe('axes_configuration', () => { }, ], false, - tables, formatFactory ); expect(groups.length).toEqual(1); expect(groups[0].position).toEqual('right'); expect(groups[0].series[0].accessor).toEqual('yAccessorId'); - expect(groups[0].series[0].layer).toEqual('first'); + expect(groups[0].series[0].layer).toEqual(0); }); it('should map series with matching formatters to same axis', () => { @@ -300,7 +299,6 @@ describe('axes_configuration', () => { }, ], false, - tables, formatFactory ); expect(groups.length).toEqual(2); @@ -324,7 +322,6 @@ describe('axes_configuration', () => { }, ], false, - tables, formatFactory ); expect(formatFactory).toHaveBeenCalledTimes(2); diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/axes_configuration.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/axes_configuration.ts index 16529ecd40e90..ef46a77e990b5 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/axes_configuration.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/axes_configuration.ts @@ -7,16 +7,18 @@ */ import { FormatFactory } from '../types'; -import { AxisExtentConfig, DataLayerConfigResult } from '../../common'; -import { Datatable } from '../../../../../plugins/expressions/public'; +import { AxisExtentConfig, CommonXYDataLayerConfigResult } from '../../common'; import type { IFieldFormat, SerializedFieldFormat, } from '../../../../../plugins/field_formats/common'; -interface FormattedMetric { - layer: string; +export interface Series { + layer: number; accessor: string; +} + +interface FormattedMetric extends Series { fieldFormat: SerializedFieldFormat; } @@ -24,7 +26,7 @@ export type GroupsConfiguration = Array<{ groupId: string; position: 'left' | 'right' | 'bottom' | 'top'; formatter?: IFieldFormat; - series: Array<{ layer: string; accessor: string }>; + series: Series[]; }>; export function isFormatterCompatible( @@ -34,10 +36,7 @@ export function isFormatterCompatible( return formatter1.id === formatter2.id; } -export function groupAxesByType( - layers: DataLayerConfigResult[], - tables?: Record -) { +export function groupAxesByType(layers: CommonXYDataLayerConfigResult[]) { const series: { auto: FormattedMetric[]; left: FormattedMetric[]; @@ -50,13 +49,13 @@ export function groupAxesByType( bottom: [], }; - layers?.forEach((layer) => { - const table = tables?.[layer.layerId]; + layers.forEach((layer, index) => { + const { table } = layer; layer.accessors.forEach((accessor) => { const mode = layer.yConfig?.find((yAxisConfig) => yAxisConfig.forAccessor === accessor)?.axisMode || 'auto'; - let formatter: SerializedFieldFormat = table?.columns.find((column) => column.id === accessor) + let formatter: SerializedFieldFormat = table.columns?.find((column) => column.id === accessor) ?.meta?.params || { id: 'number' }; if (layer.seriesType.includes('percentage') && formatter.id !== 'percent') { formatter = { @@ -67,17 +66,19 @@ export function groupAxesByType( }; } series[mode].push({ - layer: layer.layerId, + layer: index, accessor, fieldFormat: formatter, }); }); }); + const tablesExist = layers.filter(({ table }) => Boolean(table)).length > 0; + series.auto.forEach((currentSeries) => { if ( series.left.length === 0 || - (tables && + (tablesExist && series.left.every((leftSeries) => isFormatterCompatible(leftSeries.fieldFormat, currentSeries.fieldFormat) )) @@ -85,7 +86,7 @@ export function groupAxesByType( series.left.push(currentSeries); } else if ( series.right.length === 0 || - (tables && + (tablesExist && series.left.every((leftSeries) => isFormatterCompatible(leftSeries.fieldFormat, currentSeries.fieldFormat) )) @@ -101,12 +102,11 @@ export function groupAxesByType( } export function getAxesConfiguration( - layers: DataLayerConfigResult[], + layers: CommonXYDataLayerConfigResult[], shouldRotate: boolean, - tables?: Record, formatFactory?: FormatFactory ): GroupsConfiguration { - const series = groupAxesByType(layers, tables); + const series = groupAxesByType(layers); const axisGroups: GroupsConfiguration = []; diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.test.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.test.ts index bd13e3217c2af..78ec008c5e561 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.test.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.test.ts @@ -7,11 +7,47 @@ */ import { getColorAssignments } from './color_assignment'; -import type { DataLayerConfigResult, LensMultiTable } from '../../common'; +import type { DataLayerConfigResult } from '../../common'; import type { FormatFactory } from '../types'; import { LayerTypes } from '../../common/constants'; +import { Datatable } from '../../../../expressions'; describe('color_assignment', () => { + const tables: Record = { + '1': { + type: 'datatable', + columns: [ + { id: 'split1', name: '', meta: { type: 'number' } }, + { id: 'y1', name: '', meta: { type: 'number' } }, + { id: 'y2', name: '', meta: { type: 'number' } }, + ], + rows: [ + { split1: 1 }, + { split1: 2 }, + { split1: 3 }, + { split1: 1 }, + { split1: 2 }, + { split1: 3 }, + ], + }, + '2': { + type: 'datatable', + columns: [ + { id: 'split2', name: '', meta: { type: 'number' } }, + { id: 'y1', name: '', meta: { type: 'number' } }, + { id: 'y2', name: '', meta: { type: 'number' } }, + ], + rows: [ + { split2: 1 }, + { split2: 2 }, + { split2: 3 }, + { split2: 1 }, + { split2: 2 }, + { split2: 3 }, + ], + }, + }; + const layers: DataLayerConfigResult[] = [ { type: 'dataLayer', @@ -20,10 +56,10 @@ describe('color_assignment', () => { isHistogram: true, seriesType: 'bar', palette: { type: 'palette', name: 'palette1' }, - layerId: '1', layerType: LayerTypes.DATA, splitAccessor: 'split1', accessors: ['y1', 'y2'], + table: tables['1'], }, { type: 'dataLayer', @@ -32,51 +68,13 @@ describe('color_assignment', () => { isHistogram: true, seriesType: 'bar', palette: { type: 'palette', name: 'palette2' }, - layerId: '2', layerType: LayerTypes.DATA, splitAccessor: 'split2', accessors: ['y3', 'y4'], + table: tables['2'], }, ]; - const data: LensMultiTable = { - type: 'lens_multitable', - tables: { - '1': { - type: 'datatable', - columns: [ - { id: 'split1', name: '', meta: { type: 'number' } }, - { id: 'y1', name: '', meta: { type: 'number' } }, - { id: 'y2', name: '', meta: { type: 'number' } }, - ], - rows: [ - { split1: 1 }, - { split1: 2 }, - { split1: 3 }, - { split1: 1 }, - { split1: 2 }, - { split1: 3 }, - ], - }, - '2': { - type: 'datatable', - columns: [ - { id: 'split2', name: '', meta: { type: 'number' } }, - { id: 'y1', name: '', meta: { type: 'number' } }, - { id: 'y2', name: '', meta: { type: 'number' } }, - ], - rows: [ - { split2: 1 }, - { split2: 2 }, - { split2: 3 }, - { split2: 1 }, - { split2: 2 }, - { split2: 3 }, - ], - }, - }, - }; - const formatFactory = (() => ({ convert(x: unknown) { @@ -86,7 +84,7 @@ describe('color_assignment', () => { describe('totalSeriesCount', () => { it('should calculate total number of series per palette', () => { - const assignments = getColorAssignments(layers, data, formatFactory); + const assignments = getColorAssignments(layers, formatFactory); // two y accessors, with 3 splitted series expect(assignments.palette1.totalSeriesCount).toEqual(2 * 3); expect(assignments.palette2.totalSeriesCount).toEqual(2 * 3); @@ -95,7 +93,6 @@ describe('color_assignment', () => { it('should calculate total number of series spanning multible layers', () => { const assignments = getColorAssignments( [layers[0], { ...layers[1], palette: layers[0].palette }], - data, formatFactory ); // two y accessors, with 3 splitted series, two times @@ -106,7 +103,6 @@ describe('color_assignment', () => { it('should calculate total number of series for non split series', () => { const assignments = getColorAssignments( [layers[0], { ...layers[1], palette: layers[0].palette, splitAccessor: undefined }], - data, formatFactory ); // two y accessors, with 3 splitted series for the first layer, 2 non splitted y accessors for the second layer @@ -117,15 +113,16 @@ describe('color_assignment', () => { it('should format non-primitive values and count them correctly', () => { const complexObject = { aProp: 123 }; const formatMock = jest.fn((x) => 'formatted'); - const assignments = getColorAssignments( - layers, + const newLayers = [ { - ...data, - tables: { - ...data.tables, - '1': { ...data.tables['1'], rows: [{ split1: complexObject }, { split1: 'abc' }] }, - }, + ...layers[0], + table: { ...tables['1'], rows: [{ split1: complexObject }, { split1: 'abc' }] }, }, + layers[1], + ]; + + const assignments = getColorAssignments( + newLayers, (() => ({ convert: formatMock, @@ -137,26 +134,18 @@ describe('color_assignment', () => { }); it('should handle missing tables', () => { - const assignments = getColorAssignments(layers, { ...data, tables: {} }, formatFactory); + const assignments = getColorAssignments( + layers.map((l) => ({ ...l, table: {} as any })), + formatFactory + ); // if there is no data, just assume a single split expect(assignments.palette1.totalSeriesCount).toEqual(2); }); it('should handle missing columns', () => { - const assignments = getColorAssignments( - layers, - { - ...data, - tables: { - ...data.tables, - '1': { - ...data.tables['1'], - columns: [], - }, - }, - }, - formatFactory - ); + const newLayers = [{ ...layers[0], table: { ...tables['1'], columns: [] } }, layers[1]]; + const assignments = getColorAssignments(newLayers, formatFactory); + // if the split column is missing, just assume a single split expect(assignments.palette1.totalSeriesCount).toEqual(2); }); @@ -164,20 +153,20 @@ describe('color_assignment', () => { describe('getRank', () => { it('should return the correct rank for a series key', () => { - const assignments = getColorAssignments(layers, data, formatFactory); + const assignments = getColorAssignments(layers, formatFactory); // 3 series in front of 2/y2 - 1/y1, 1/y2 and 2/y1 - expect(assignments.palette1.getRank(layers[0], '2', 'y2')).toEqual(3); + expect(assignments.palette1.getRank(layers[0], 0, '2', 'y2')).toEqual(3); // 1 series in front of 1/y4 - 1/y3 - expect(assignments.palette2.getRank(layers[1], '1', 'y4')).toEqual(1); + expect(assignments.palette2.getRank(layers[1], 1, '1', 'y4')).toEqual(1); }); it('should return the correct rank for a series key spanning multiple layers', () => { const newLayers = [layers[0], { ...layers[1], palette: layers[0].palette }]; - const assignments = getColorAssignments(newLayers, data, formatFactory); + const assignments = getColorAssignments(newLayers, formatFactory); // 3 series in front of 2/y2 - 1/y1, 1/y2 and 2/y1 - expect(assignments.palette1.getRank(newLayers[0], '2', 'y2')).toEqual(3); + expect(assignments.palette1.getRank(newLayers[0], 0, '2', 'y2')).toEqual(3); // 2 series in front for the current layer (1/y3, 1/y4), plus all 6 series from the first layer - expect(assignments.palette1.getRank(newLayers[1], '2', 'y3')).toEqual(8); + expect(assignments.palette1.getRank(newLayers[1], 1, '2', 'y3')).toEqual(8); }); it('should return the correct rank for a series without a split', () => { @@ -185,55 +174,49 @@ describe('color_assignment', () => { layers[0], { ...layers[1], palette: layers[0].palette, splitAccessor: undefined }, ]; - const assignments = getColorAssignments(newLayers, data, formatFactory); + const assignments = getColorAssignments(newLayers, formatFactory); // 3 series in front of 2/y2 - 1/y1, 1/y2 and 2/y1 - expect(assignments.palette1.getRank(newLayers[0], '2', 'y2')).toEqual(3); + expect(assignments.palette1.getRank(newLayers[0], 0, '2', 'y2')).toEqual(3); // 1 series in front for the current layer (y3), plus all 6 series from the first layer - expect(assignments.palette1.getRank(newLayers[1], 'Metric y4', 'y4')).toEqual(7); + expect(assignments.palette1.getRank(newLayers[1], 1, 'Metric y4', 'y4')).toEqual(7); }); it('should return the correct rank for a series with a non-primitive value', () => { - const assignments = getColorAssignments( - layers, + const newLayers = [ { - ...data, - tables: { - ...data.tables, - '1': { ...data.tables['1'], rows: [{ split1: 'abc' }, { split1: { aProp: 123 } }] }, - }, + ...layers[0], + table: { ...tables['1'], rows: [{ split1: 'abc' }, { split1: { aProp: 123 } }] }, }, + layers[1], + ]; + + const assignments = getColorAssignments( + newLayers, (() => ({ convert: () => 'formatted', } as unknown)) as FormatFactory ); // 3 series in front of (complex object)/y1 - abc/y1, abc/y2 - expect(assignments.palette1.getRank(layers[0], 'formatted', 'y1')).toEqual(2); + expect(assignments.palette1.getRank(layers[0], 0, 'formatted', 'y1')).toEqual(2); }); it('should handle missing tables', () => { - const assignments = getColorAssignments(layers, { ...data, tables: {} }, formatFactory); + const assignments = getColorAssignments( + layers.map((l) => ({ ...l, table: {} as any })), + formatFactory + ); // if there is no data, assume it is the first splitted series. One series in front - 0/y1 - expect(assignments.palette1.getRank(layers[0], '2', 'y2')).toEqual(1); + expect(assignments.palette1.getRank(layers[0], 0, '2', 'y2')).toEqual(1); }); it('should handle missing columns', () => { - const assignments = getColorAssignments( - layers, - { - ...data, - tables: { - ...data.tables, - '1': { - ...data.tables['1'], - columns: [], - }, - }, - }, - formatFactory - ); + const newLayers = [{ ...layers[0], table: { ...tables['1'], columns: [] } }, layers[1]]; + + const assignments = getColorAssignments(newLayers, formatFactory); + // if the split column is missing, assume it is the first splitted series. One series in front - 0/y1 - expect(assignments.palette1.getRank(layers[0], '2', 'y2')).toEqual(1); + expect(assignments.palette1.getRank(layers[0], 0, '2', 'y2')).toEqual(1); }); }); }); diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.ts index ef0cd36e3598e..0108d6b07d08d 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.ts @@ -8,10 +8,9 @@ import { uniq, mapValues } from 'lodash'; import { euiLightVars } from '@kbn/ui-theme'; -import type { Datatable } from '../../../../expressions'; import { FormatFactory } from '../types'; import { isDataLayer } from './visualization'; -import { DataLayerConfigResult, XYLayerConfigResult } from '../../common'; +import { CommonXYDataLayerConfigResult } from '../../common'; const isPrimitive = (value: unknown): boolean => value != null && typeof value !== 'object'; @@ -21,40 +20,52 @@ export type ColorAssignments = Record< string, { totalSeriesCount: number; - getRank(sortedLayer: DataLayerConfigResult, seriesKey: string, yAccessor: string): number; + getRank( + sortedLayer: CommonXYDataLayerConfigResult, + layerId: number, + seriesKey: string, + yAccessor: string + ): number; } >; export function getColorAssignments( - layers: XYLayerConfigResult[], - data: { tables: Record }, + layers: CommonXYDataLayerConfigResult[], formatFactory: FormatFactory ): ColorAssignments { - const layersPerPalette: Record = {}; + const layersPerPalette: Record< + string, + Array<{ + index: number; + layer: CommonXYDataLayerConfigResult; + }> + > = {}; - layers - .filter((layer): layer is DataLayerConfigResult => isDataLayer(layer)) - .forEach((layer) => { - const palette = layer.palette?.name || 'default'; - if (!layersPerPalette[palette]) { - layersPerPalette[palette] = []; - } - layersPerPalette[palette].push(layer); - }); + layers.forEach((layer, index) => { + if (!isDataLayer(layer)) { + return; + } + + const palette = layer.palette?.name || 'default'; + if (!layersPerPalette[palette]) { + layersPerPalette[palette] = []; + } + layersPerPalette[palette].push({ layer, index }); + }); return mapValues(layersPerPalette, (paletteLayers) => { - const seriesPerLayer = paletteLayers.map((layer, layerIndex) => { + const seriesPerLayer = paletteLayers.map(({ layer }) => { if (!layer.splitAccessor) { return { numberOfSeries: layer.accessors.length, splits: [] }; } const splitAccessor = layer.splitAccessor; - const column = data.tables[layer.layerId]?.columns.find(({ id }) => id === splitAccessor); + const column = layer.table.columns?.find(({ id }) => id === splitAccessor); const columnFormatter = column && formatFactory(column.meta.params); const splits = - !column || !data.tables[layer.layerId] + !column || !layer.table ? [] : uniq( - data.tables[layer.layerId].rows.map((row) => { + layer.table.rows.map((row) => { let value = row[splitAccessor]; if (value && !isPrimitive(value)) { value = columnFormatter?.convert(value) ?? value; @@ -72,8 +83,13 @@ export function getColorAssignments( ); return { totalSeriesCount, - getRank(sortedLayer: DataLayerConfigResult, seriesKey: string, yAccessor: string) { - const layerIndex = paletteLayers.findIndex((l) => sortedLayer.layerId === l.layerId); + getRank( + sortedLayer: CommonXYDataLayerConfigResult, + layerId: number, + seriesKey: string, + yAccessor: string + ) { + const layerIndex = paletteLayers.findIndex(({ index }) => layerId === index); const currentSeriesPerLayer = seriesPerLayer[layerIndex]; const splitRank = currentSeriesPerLayer.splits.indexOf(seriesKey); return ( diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/interval.test.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/interval.test.ts index 0fe979b8c3fc1..4bdd595db80c6 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/interval.test.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/interval.test.ts @@ -14,12 +14,15 @@ describe('calculateMinInterval', () => { let xyProps: XYChartProps; beforeEach(() => { - xyProps = sampleArgs(); + const { layers, ...restArgs } = sampleArgs().args; + + xyProps = { args: { ...restArgs, layers } }; + (xyProps.args.layers[0] as DataLayerConfigResult).xScaleType = 'time'; }); it('should use first valid layer and determine interval', async () => { - xyProps.data.tables.first.columns[2].meta.source = 'esaggs'; - xyProps.data.tables.first.columns[2].meta.sourceParams = { + xyProps.args.layers[0].table.columns[2].meta.source = 'esaggs'; + xyProps.args.layers[0].table.columns[2].meta.sourceParams = { type: 'date_histogram', params: { used_interval: '5m', @@ -31,7 +34,7 @@ describe('calculateMinInterval', () => { it('should return interval of number histogram if available on first x axis columns', async () => { (xyProps.args.layers[0] as DataLayerConfigResult).xScaleType = 'linear'; - xyProps.data.tables.first.columns[2].meta = { + xyProps.args.layers[0].table.columns[2].meta = { source: 'esaggs', type: 'number', field: 'someField', @@ -48,9 +51,9 @@ describe('calculateMinInterval', () => { }); it('should return undefined if data table is empty', async () => { - xyProps.data.tables.first.rows = []; - xyProps.data.tables.first.columns[2].meta.source = 'esaggs'; - xyProps.data.tables.first.columns[2].meta.sourceParams = { + xyProps.args.layers[0].table.rows = []; + xyProps.args.layers[0].table.columns[2].meta.source = 'esaggs'; + xyProps.args.layers[0].table.columns[2].meta.sourceParams = { type: 'date_histogram', params: { used_interval: '5m', @@ -66,14 +69,14 @@ describe('calculateMinInterval', () => { }); it('should return undefined if date column is not found', async () => { - xyProps.data.tables.first.columns.splice(2, 1); + xyProps.args.layers[0].table.columns.splice(2, 1); const result = await calculateMinInterval(xyProps); expect(result).toEqual(undefined); }); it('should return undefined if x axis is not a date', async () => { (xyProps.args.layers[0] as DataLayerConfigResult).xScaleType = 'ordinal'; - xyProps.data.tables.first.columns.splice(2, 1); + xyProps.args.layers[0].table.columns.splice(2, 1); const result = await calculateMinInterval(xyProps); expect(result).toEqual(undefined); }); diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/interval.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/interval.ts index 7e15b49c311d4..8bb350f5edc51 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/interval.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/interval.ts @@ -11,11 +11,11 @@ import { XYChartProps } from '../../common'; import { getFilteredLayers } from './layers'; import { isDataLayer } from './visualization'; -export function calculateMinInterval({ args: { layers }, data }: XYChartProps) { - const filteredLayers = getFilteredLayers(layers, data); +export function calculateMinInterval({ args: { layers } }: XYChartProps) { + const filteredLayers = getFilteredLayers(layers); if (filteredLayers.length === 0) return; const isTimeViz = filteredLayers.every((l) => isDataLayer(l) && l.xScaleType === 'time'); - const xColumn = data.tables[filteredLayers[0].layerId].columns.find( + const xColumn = filteredLayers[0].table.columns.find( (column) => isDataLayer(filteredLayers[0]) && column.id === filteredLayers[0].xAccessor ); diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/layers.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/layers.ts index 28cea4a269829..e5ab47d8f8185 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/layers.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/layers.ts @@ -6,27 +6,28 @@ * Side Public License, v 1. */ -import { DataLayerConfigResult, LensMultiTable, XYLayerConfigResult } from '../../common'; +import { CommonXYLayerConfigResult, CommonXYDataLayerConfigResult } from '../../common'; import { isDataLayer } from './visualization'; -export function getFilteredLayers(layers: XYLayerConfigResult[], data: LensMultiTable) { - return layers.filter((layer): layer is DataLayerConfigResult => { - if (!isDataLayer(layer)) { - return false; - } +export function getFilteredLayers(layers: CommonXYLayerConfigResult[]) { + return layers.filter( + (layer): layer is CommonXYDataLayerConfigResult => { + if (!isDataLayer(layer)) { + return false; + } - const { layerId, accessors, xAccessor, splitAccessor } = layer; + const { accessors, xAccessor, splitAccessor, table } = layer; - return !( - !accessors.length || - !data.tables[layerId] || - data.tables[layerId].rows.length === 0 || - (xAccessor && - data.tables[layerId].rows.every((row) => typeof row[xAccessor] === 'undefined')) || - // stacked percentage bars have no xAccessors but splitAccessor with undefined values in them when empty - (!xAccessor && - splitAccessor && - data.tables[layerId].rows.every((row) => typeof row[splitAccessor] === 'undefined')) - ); - }); + return !( + !accessors.length || + !table || + table.rows.length === 0 || + (xAccessor && table.rows.every((row) => typeof row[xAccessor] === 'undefined')) || + // stacked percentage bars have no xAccessors but splitAccessor with undefined values in them when empty + (!xAccessor && + splitAccessor && + table.rows.every((row) => typeof row[splitAccessor] === 'undefined')) + ); + } + ); } diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/reference_lines.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/reference_lines.ts index 86a87e22157c7..3e4e0bd11295e 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/reference_lines.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/reference_lines.ts @@ -7,14 +7,12 @@ */ import { partition } from 'lodash'; -import type { DataLayerConfigResult } from '../../common'; -import type { FramePublicAPI } from '../types'; +import type { CommonXYDataLayerConfigResult } from '../../common'; import { isStackedChart } from './state'; export function computeOverallDataDomain( - dataLayers: DataLayerConfigResult[], + dataLayers: CommonXYDataLayerConfigResult[], accessorIds: string[], - activeData: NonNullable, allowStacking: boolean = true ) { const accessorMap = new Set(accessorIds); @@ -24,8 +22,7 @@ export function computeOverallDataDomain( dataLayers, ({ seriesType }) => isStackedChart(seriesType) && allowStacking ); - for (const { layerId, accessors } of unstacked) { - const table = activeData[layerId]; + for (const { accessors, table } of unstacked) { if (table) { for (const accessor of accessors) { if (accessorMap.has(accessor)) { @@ -43,8 +40,7 @@ export function computeOverallDataDomain( } // stacked can span multiple layers, so compute an overall max/min by bucket const stackedResults: Record = {}; - for (const { layerId, accessors, xAccessor } of stacked) { - const table = activeData[layerId]; + for (const { accessors, xAccessor, table } of stacked) { if (table) { for (const accessor of accessors) { if (accessorMap.has(accessor)) { diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/state.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/state.ts index eda33dde1ba47..173557b5629de 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/state.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/state.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import type { SeriesType, XYLayerConfigResult, YConfig } from '../../common'; +import type { CommonXYLayerConfigResult, SeriesType, YConfig } from '../../common'; import { getDataLayers, isDataLayer } from './visualization'; export function isHorizontalSeries(seriesType: SeriesType) { @@ -21,11 +21,11 @@ export function isStackedChart(seriesType: SeriesType) { return seriesType.includes('stacked'); } -export function isHorizontalChart(layers: XYLayerConfigResult[]) { +export function isHorizontalChart(layers: CommonXYLayerConfigResult[]) { return getDataLayers(layers).every((l) => isHorizontalSeries(l.seriesType)); } -export const getSeriesColor = (layer: XYLayerConfigResult, accessor: string) => { +export const getSeriesColor = (layer: CommonXYLayerConfigResult, accessor: string) => { if (isDataLayer(layer) && layer.splitAccessor) { return null; } diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/visualization.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/visualization.ts index 6d0e19dd47086..1bbdc438e952d 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/visualization.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/visualization.ts @@ -6,24 +6,26 @@ * Side Public License, v 1. */ +import { LayerTypes } from '../../common/constants'; import { - DataLayerConfigResult, - ReferenceLineLayerConfigResult, - XYLayerConfigResult, + CommonXYDataLayerConfigResult, + CommonXYLayerConfigResult, + CommonXYReferenceLineLayerConfigResult, } from '../../common'; -import { LayerTypes } from '../../common/constants'; -export const isDataLayer = (layer: XYLayerConfigResult): layer is DataLayerConfigResult => +export const isDataLayer = ( + layer: CommonXYLayerConfigResult +): layer is CommonXYDataLayerConfigResult => layer.layerType === LayerTypes.DATA || !layer.layerType; -export const getDataLayers = (layers: XYLayerConfigResult[]) => - (layers || []).filter((layer): layer is DataLayerConfigResult => isDataLayer(layer)); +export const getDataLayers = (layers: CommonXYLayerConfigResult[]) => + (layers || []).filter((layer): layer is CommonXYDataLayerConfigResult => isDataLayer(layer)); export const isReferenceLayer = ( - layer: XYLayerConfigResult -): layer is ReferenceLineLayerConfigResult => layer.layerType === LayerTypes.REFERENCELINE; + layer: CommonXYLayerConfigResult +): layer is CommonXYReferenceLineLayerConfigResult => layer.layerType === LayerTypes.REFERENCELINE; -export const getReferenceLayers = (layers: XYLayerConfigResult[]) => - (layers || []).filter((layer): layer is ReferenceLineLayerConfigResult => +export const getReferenceLayers = (layers: CommonXYLayerConfigResult[]) => + (layers || []).filter((layer): layer is CommonXYReferenceLineLayerConfigResult => isReferenceLayer(layer) ); From f49d5f495d3fc279c47c3677574cb5c4c04b03d6 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Tue, 22 Mar 2022 13:08:16 +0200 Subject: [PATCH 052/133] Fixed tests. --- .../expression_xy/public/__mocks__/index.tsx | 33 +++++++------- .../public/components/xy_chart.test.tsx | 33 +++++++++++--- .../public/components/xy_chart.tsx | 44 ++++++++++--------- .../public/helpers/axes_configuration.ts | 13 ++++-- .../public/helpers/color_assignment.ts | 4 +- .../expression_xy/public/helpers/layers.ts | 34 ++++++-------- .../public/helpers/reference_lines.ts | 12 ++--- 7 files changed, 98 insertions(+), 75 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/public/__mocks__/index.tsx b/src/plugins/chart_expressions/expression_xy/public/__mocks__/index.tsx index 4bc4f722f44c4..ee4a14c476e65 100644 --- a/src/plugins/chart_expressions/expression_xy/public/__mocks__/index.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/__mocks__/index.tsx @@ -6,7 +6,8 @@ * Side Public License, v 1. */ -import { chartPluginMock } from '../../../../../plugins/charts/public/mocks'; +import { Datatable } from '../../../../expressions'; +import { chartPluginMock } from '../../../../charts/public/mocks'; import { DataLayerConfigResult, LensMultiTable } from '../../common'; import { LayerTypes } from '../../common/constants'; import { XYProps } from '../../common/types'; @@ -183,10 +184,23 @@ export const dateHistogramLayer: DataLayerConfigResult = { }; export function sampleArgsWithReferenceLine(value: number = 150) { - const { args: sArgs, data } = sampleArgs(); + const { args: sArgs } = sampleArgs(); + const data: Datatable = { + type: 'datatable', + columns: [ + { + id: 'referenceLine-a', + meta: { params: { id: 'number' }, type: 'number' }, + name: 'Static value', + }, + ], + rows: [{ 'referenceLine-a': value }], + }; + const args: XYProps = { ...sArgs, layers: [ + ...sArgs.layers, { type: 'referenceLineLayer', layerType: LayerTypes.REFERENCELINE, @@ -197,18 +211,5 @@ export function sampleArgsWithReferenceLine(value: number = 150) { ], }; - return { - data: { - type: 'datatable', - columns: [ - { - id: 'referenceLine-a', - meta: { params: { id: 'number' }, type: 'number' }, - name: 'Static value', - }, - ], - rows: [{ 'referenceLine-a': value }], - }, - args, - }; + return { data, args }; } diff --git a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx index 98f57a7a74944..69eb8b17267a7 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx @@ -2163,7 +2163,7 @@ describe('XYChart component', () => { }); test('it should remove invalid rows', () => { - const data: Datatable = { + const data1: Datatable = { type: 'datatable', columns: [ { id: 'a', name: 'a', meta: { type: 'number' } }, @@ -2176,6 +2176,19 @@ describe('XYChart component', () => { ], }; + const data2: Datatable = { + type: 'datatable', + columns: [ + { id: 'a', name: 'a', meta: { type: 'number' } }, + { id: 'b', name: 'b', meta: { type: 'number' } }, + { id: 'c', name: 'c', meta: { type: 'string' } }, + ], + rows: [ + { a: undefined, b: undefined, c: undefined }, + { a: undefined, b: undefined, c: undefined }, + ], + }; + const args: XYProps = { xTitle: '', yTitle: '', @@ -2221,7 +2234,7 @@ describe('XYChart component', () => { yScaleType: 'linear', isHistogram: false, palette: mockPaletteOutput, - table: data, + table: data1, }, { type: 'dataLayer', @@ -2235,7 +2248,7 @@ describe('XYChart component', () => { yScaleType: 'linear', isHistogram: false, palette: mockPaletteOutput, - table: data, + table: data2, }, ], }; @@ -2243,6 +2256,7 @@ describe('XYChart component', () => { const component = shallow(); const series = component.find(LineSeries); + // Only one series should be rendered, even though 2 are configured // This one series should only have one row, even though 2 are sent expect(series.prop('data')).toEqual([{ a: 1, b: 5, c: 'J', d: 'Row 2' }]); @@ -2489,11 +2503,16 @@ describe('XYChart component', () => { }); test('it should apply the fitting function to all non-bar series', () => { + const data: Datatable = createSampleDatatableWithRows([ + { a: 1, b: 2, c: 'I', d: 'Foo' }, + { a: 1, b: 5, c: 'J', d: 'Bar' }, + ]); + const args: XYProps = createArgsWithLayers([ - { ...sampleLayer, accessors: ['a'] }, - { ...sampleLayer, seriesType: 'bar', accessors: ['a'] }, - { ...sampleLayer, seriesType: 'area', accessors: ['a'] }, - { ...sampleLayer, seriesType: 'area_stacked', accessors: ['a'] }, + { ...sampleLayer, accessors: ['a'], table: data }, + { ...sampleLayer, seriesType: 'bar', accessors: ['a'], table: data }, + { ...sampleLayer, seriesType: 'area', accessors: ['a'], table: data }, + { ...sampleLayer, seriesType: 'area_stacked', accessors: ['a'], table: data }, ]); const component = shallow( diff --git a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx index 5fc55355b788d..1ff0bd0514753 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx @@ -71,7 +71,7 @@ import { ReferenceLineAnnotations, } from './reference_lines'; import { visualizationDefinitions } from '../definitions'; -import { CommonXYDataLayerConfigResult } from '../../common/types'; +import { CommonXYDataLayerConfigResult, CommonXYLayerConfigResult } from '../../common/types'; import './xy_chart.scss'; @@ -160,7 +160,7 @@ export function XYChart({ const chartBaseTheme = chartsThemeService.useChartsBaseTheme(); const darkMode = chartsThemeService.useDarkMode(); const filteredLayers = getFilteredLayers(layers); - const layersById = filteredLayers.reduce>( + const layersById = filteredLayers.reduce>( (hashMap, layer, index) => { hashMap[index] = layer; return hashMap; @@ -179,9 +179,11 @@ export function XYChart({ return ; } + const dataLayers = filteredLayers.filter(isDataLayer); + // use formatting hint of first x axis column to format ticks - const xAxisColumn = filteredLayers[0].table.columns.find( - ({ id }) => isDataLayer(filteredLayers[0]) && id === filteredLayers[0].xAccessor + const xAxisColumn = dataLayers[0]?.table.columns.find( + ({ id }) => isDataLayer(dataLayers[0]) && id === dataLayers[0].xAccessor ); const xAxisFormatter = formatFactory(xAxisColumn && xAxisColumn.meta?.params); @@ -197,7 +199,7 @@ export function XYChart({ filteredLayers.length > 1 || filteredLayers.some((layer) => layer.accessors.length > 1) || filteredLayers.some((layer) => isDataLayer(layer) && layer.splitAccessor); - const shouldRotate = isHorizontalChart(filteredLayers); + const shouldRotate = isHorizontalChart(dataLayers); const yAxesConfiguration = getAxesConfiguration(filteredLayers, shouldRotate, formatFactory); @@ -219,18 +221,18 @@ export function XYChart({ yRight: 0, }; - const filteredBarLayers = filteredLayers.filter((layer) => layer.seriesType.includes('bar')); + const filteredBarLayers = dataLayers.filter((layer) => layer.seriesType.includes('bar')); const chartHasMoreThanOneBarSeries = filteredBarLayers.length > 1 || filteredBarLayers.some((layer) => layer.accessors.length > 1) || - filteredBarLayers.some((layer) => layer.splitAccessor); + filteredBarLayers.some((layer) => isDataLayer(layer) && layer.splitAccessor); - const isTimeViz = Boolean(filteredLayers.every((l) => l.xScaleType === 'time')); - const isHistogramViz = filteredLayers.every((l) => l.isHistogram); + const isTimeViz = Boolean(filteredLayers.every((l) => isDataLayer(l) && l.xScaleType === 'time')); + const isHistogramViz = filteredLayers.every((l) => isDataLayer(l) && l.isHistogram); const { baseDomain: rawXDomain, extendedDomain: xDomain } = getXDomain( - filteredLayers, + dataLayers, minInterval, isTimeViz, isHistogramViz @@ -323,7 +325,7 @@ export function XYChart({ if (!fit && axisHasReferenceLine) { // Remove this once the chart will support automatic annotation fit for other type of charts const { min: computedMin, max: computedMax } = computeOverallDataDomain( - filteredLayers, + layers, axis.series.map(({ accessor }) => accessor) ); @@ -355,7 +357,7 @@ export function XYChart({ const shouldShowValueLabels = // No stacked bar charts - filteredLayers.every((layer) => !layer.seriesType.includes('stacked')) && + dataLayers.every((layer) => !layer.seriesType.includes('stacked')) && // No histogram charts !isHistogramViz; @@ -369,7 +371,7 @@ export function XYChart({ const xySeries = series as XYChartSeriesIdentifier; const xyGeometry = geometry as GeometryValue; - const layer = filteredLayers.find((l) => + const layer = dataLayers.find((l) => xySeries.seriesKeys.some((key: string | number) => l.accessors.includes(key.toString())) ); if (!layer) { @@ -441,9 +443,9 @@ export function XYChart({ return; } - const { table } = filteredLayers[0]; + const { table } = dataLayers[0]; - const xAxisColumnIndex = table.columns.findIndex((el) => el.id === filteredLayers[0].xAccessor); + const xAxisColumnIndex = table.columns.findIndex((el) => el.id === dataLayers[0].xAccessor); const context: BrushEvent['data'] = { range: [min, max], @@ -461,7 +463,7 @@ export function XYChart({ floatingColumns: legend?.floatingColumns ?? 1, } as LegendPositionConfig; - const isHistogramModeEnabled = filteredLayers.some( + const isHistogramModeEnabled = dataLayers.some( ({ isHistogram, seriesType }) => isHistogram && (seriesType.includes('stacked') || @@ -557,7 +559,7 @@ export function XYChart({ onElementClick={interactive ? clickHandler : undefined} legendAction={ interactive - ? getLegendAction(filteredLayers, onClickValue, formatFactory, layersAlreadyFormatted) + ? getLegendAction(dataLayers, onClickValue, formatFactory, layersAlreadyFormatted) : undefined } showLegendExtra={isHistogramViz && valuesInLegend} @@ -570,7 +572,7 @@ export function XYChart({ position={shouldRotate ? Position.Left : Position.Bottom} title={xTitle} gridLine={gridLineStyle} - hide={filteredLayers[0].hide || !filteredLayers[0].xAccessor} + hide={dataLayers[0]?.hide || !dataLayers[0]?.xAccessor} tickFormat={(d) => safeXAccessorLabelRenderer(d)} style={xAxisStyle} timeAxisLayerCount={shouldUseNewTimeAxis ? 3 : 0} @@ -590,7 +592,7 @@ export function XYChart({ ? gridlinesVisibilitySettings?.yRight : gridlinesVisibilitySettings?.yLeft, }} - hide={filteredLayers[0].hide} + hide={dataLayers[0]?.hide} tickFormat={(d) => axis.formatter?.convert(d) || ''} style={getYAxesStyle(axis.groupId as 'left' | 'right')} domain={getYAxisDomain(axis)} @@ -604,7 +606,7 @@ export function XYChart({ baseDomain={rawXDomain} extendedDomain={xDomain} darkMode={darkMode} - histogramMode={filteredLayers.every( + histogramMode={dataLayers.every( (layer) => layer.isHistogram && (layer.seriesType.includes('stacked') || !layer.splitAccessor) && @@ -615,7 +617,7 @@ export function XYChart({ /> )} - {filteredLayers.flatMap((layer, layerIndex) => + {dataLayers.flatMap((layer, layerIndex) => layer.accessors.map((accessor, accessorIndex) => { const { splitAccessor, diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/axes_configuration.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/axes_configuration.ts index ef46a77e990b5..21e21884a9f39 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/axes_configuration.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/axes_configuration.ts @@ -7,11 +7,12 @@ */ import { FormatFactory } from '../types'; -import { AxisExtentConfig, CommonXYDataLayerConfigResult } from '../../common'; +import { AxisExtentConfig, CommonXYLayerConfigResult } from '../../common'; import type { IFieldFormat, SerializedFieldFormat, } from '../../../../../plugins/field_formats/common'; +import { isDataLayer } from './visualization'; export interface Series { layer: number; @@ -36,7 +37,7 @@ export function isFormatterCompatible( return formatter1.id === formatter2.id; } -export function groupAxesByType(layers: CommonXYDataLayerConfigResult[]) { +export function groupAxesByType(layers: CommonXYLayerConfigResult[]) { const series: { auto: FormattedMetric[]; left: FormattedMetric[]; @@ -57,7 +58,11 @@ export function groupAxesByType(layers: CommonXYDataLayerConfigResult[]) { 'auto'; let formatter: SerializedFieldFormat = table.columns?.find((column) => column.id === accessor) ?.meta?.params || { id: 'number' }; - if (layer.seriesType.includes('percentage') && formatter.id !== 'percent') { + if ( + isDataLayer(layer) && + layer.seriesType.includes('percentage') && + formatter.id !== 'percent' + ) { formatter = { id: 'percent', params: { @@ -102,7 +107,7 @@ export function groupAxesByType(layers: CommonXYDataLayerConfigResult[]) { } export function getAxesConfiguration( - layers: CommonXYDataLayerConfigResult[], + layers: CommonXYLayerConfigResult[], shouldRotate: boolean, formatFactory?: FormatFactory ): GroupsConfiguration { diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.ts index 0108d6b07d08d..b0fb3e00a14ad 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.ts @@ -10,7 +10,7 @@ import { uniq, mapValues } from 'lodash'; import { euiLightVars } from '@kbn/ui-theme'; import { FormatFactory } from '../types'; import { isDataLayer } from './visualization'; -import { CommonXYDataLayerConfigResult } from '../../common'; +import { CommonXYDataLayerConfigResult, CommonXYLayerConfigResult } from '../../common'; const isPrimitive = (value: unknown): boolean => value != null && typeof value !== 'object'; @@ -30,7 +30,7 @@ export type ColorAssignments = Record< >; export function getColorAssignments( - layers: CommonXYDataLayerConfigResult[], + layers: CommonXYLayerConfigResult[], formatFactory: FormatFactory ): ColorAssignments { const layersPerPalette: Record< diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/layers.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/layers.ts index e5ab47d8f8185..40aa2d1cbf719 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/layers.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/layers.ts @@ -7,27 +7,21 @@ */ import { CommonXYLayerConfigResult, CommonXYDataLayerConfigResult } from '../../common'; -import { isDataLayer } from './visualization'; export function getFilteredLayers(layers: CommonXYLayerConfigResult[]) { - return layers.filter( - (layer): layer is CommonXYDataLayerConfigResult => { - if (!isDataLayer(layer)) { - return false; - } + return layers.filter((layer): layer is CommonXYLayerConfigResult => { + const { accessors, table } = layer; + const { xAccessor, splitAccessor } = layer as CommonXYDataLayerConfigResult; - const { accessors, xAccessor, splitAccessor, table } = layer; - - return !( - !accessors.length || - !table || - table.rows.length === 0 || - (xAccessor && table.rows.every((row) => typeof row[xAccessor] === 'undefined')) || - // stacked percentage bars have no xAccessors but splitAccessor with undefined values in them when empty - (!xAccessor && - splitAccessor && - table.rows.every((row) => typeof row[splitAccessor] === 'undefined')) - ); - } - ); + return !( + !accessors.length || + !table || + table.rows.length === 0 || + (xAccessor && table.rows.every((row) => typeof row[xAccessor] === 'undefined')) || + // stacked percentage bars have no xAccessors but splitAccessor with undefined values in them when empty + (!xAccessor && + splitAccessor && + table.rows.every((row) => typeof row[splitAccessor] === 'undefined')) + ); + }); } diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/reference_lines.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/reference_lines.ts index 3e4e0bd11295e..c5be6018c01f4 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/reference_lines.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/reference_lines.ts @@ -7,11 +7,12 @@ */ import { partition } from 'lodash'; -import type { CommonXYDataLayerConfigResult } from '../../common'; +import type { CommonXYDataLayerConfigResult, CommonXYLayerConfigResult } from '../../common'; import { isStackedChart } from './state'; +import { isDataLayer } from './visualization'; export function computeOverallDataDomain( - dataLayers: CommonXYDataLayerConfigResult[], + layers: CommonXYLayerConfigResult[], accessorIds: string[], allowStacking: boolean = true ) { @@ -19,9 +20,10 @@ export function computeOverallDataDomain( let min: number | undefined; let max: number | undefined; const [stacked, unstacked] = partition( - dataLayers, - ({ seriesType }) => isStackedChart(seriesType) && allowStacking + layers, + (layer) => isDataLayer(layer) && isStackedChart(layer.seriesType) && allowStacking ); + for (const { accessors, table } of unstacked) { if (table) { for (const accessor of accessors) { @@ -40,7 +42,7 @@ export function computeOverallDataDomain( } // stacked can span multiple layers, so compute an overall max/min by bucket const stackedResults: Record = {}; - for (const { accessors, xAccessor, table } of stacked) { + for (const { accessors, xAccessor, table } of stacked as CommonXYDataLayerConfigResult[]) { if (table) { for (const accessor of accessors) { if (accessorMap.has(accessor)) { From f45c81ceefcbaae53856f10aabf97b9cce5e463f Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Tue, 22 Mar 2022 15:16:27 +0200 Subject: [PATCH 053/133] Fixed more tests. --- .../common/expression_functions/xy_vis.test.ts | 12 ++++++++---- .../public/components/xy_chart.test.tsx | 3 ++- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.test.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.test.ts index 4f6549106965d..c70c9388fb943 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.test.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.test.ts @@ -8,19 +8,23 @@ import { xyVisFunction } from '../expression_functions'; import { createMockExecutionContext } from '../../../../../plugins/expressions/common/mocks'; -import { sampleArgs } from '../__mocks__'; +import { sampleArgs, sampleLayer } from '../__mocks__'; import { XY_VIS } from '../constants'; describe('xyVis', () => { test('it renders with the specified data and args', () => { const { data, args } = sampleArgs(); - const result = xyVisFunction.fn(data, args, createMockExecutionContext()); - const { layers, ...rest } = args; + const result = xyVisFunction.fn( + data, + { ...rest, dataLayer: sampleLayer }, + createMockExecutionContext() + ); + expect(result).toEqual({ type: 'render', as: XY_VIS, - value: { args: { ...rest, layers } }, + value: { args: { ...rest, layers: [sampleLayer] } }, }); }); }); diff --git a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx index 69eb8b17267a7..407d893c8b60e 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx @@ -189,6 +189,7 @@ describe('XYChart component', () => { seriesType: 'line', xScaleType: 'time', type: 'dataLayer', + table: timeSampleLayer.table, } as DataLayerConfigResult, ], }} @@ -412,7 +413,7 @@ describe('XYChart component', () => { test('it renders endzone component bridging gap between domain and extended domain', () => { const component = shallow( - + ); expect(component.find(XyEndzones).dive().find('Endzones').props()).toEqual( From eb78296c73b3cb90ca5f89236b243f6e3bcc194e Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Tue, 22 Mar 2022 16:20:00 +0200 Subject: [PATCH 054/133] Fixed lens types. --- .../expression_xy/common/index.ts | 1 - .../common/types/expression_functions.ts | 4 -- .../public/components/reference_lines.tsx | 8 +-- .../expression_xy/server/plugin.ts | 14 +++-- x-pack/plugins/lens/public/index.ts | 3 +- .../axes_configuration.test.ts | 5 +- .../reference_line_helpers.test.ts | 54 +++++++++---------- .../public/xy_visualization/state_helpers.ts | 2 +- .../public/xy_visualization/to_expression.ts | 12 ++--- .../lens/public/xy_visualization/types.ts | 7 +++ .../xy_visualization/visualization.test.ts | 7 +-- .../xy_config_panel/layer_header.tsx | 9 ++-- .../visual_options_popover/index.tsx | 3 +- .../xy_config_panel/xy_config_panel.test.tsx | 3 +- 14 files changed, 59 insertions(+), 73 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/common/index.ts b/src/plugins/chart_expressions/expression_xy/common/index.ts index b9f4c02c9b76e..5432d86aa16a1 100755 --- a/src/plugins/chart_expressions/expression_xy/common/index.ts +++ b/src/plugins/chart_expressions/expression_xy/common/index.ts @@ -37,7 +37,6 @@ export type { YScaleType, XScaleType, AxisConfig, - ValidLayer, XYCurveType, XYChartProps, LegendConfig, diff --git a/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts b/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts index e758cf057cf6f..f444df68b87a0 100644 --- a/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts +++ b/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts @@ -99,10 +99,6 @@ export interface XYExtendedDataLayerConfig { palette?: PaletteOutput; } -export interface ValidLayer extends DataLayerConfigResult { - xAccessor: NonNullable; -} - export type DataLayerArgs = XYDataLayerConfig & { columnToLabel?: string; // Actually a JSON key-value pair yScaleType: YScaleType; diff --git a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.tsx b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.tsx index 74ac4ef22ffae..7a39558daa590 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.tsx @@ -14,13 +14,7 @@ import { EuiIcon } from '@elastic/eui'; import { RectAnnotation, AnnotationDomainType, LineAnnotation, Position } from '@elastic/charts'; import { euiLightVars } from '@kbn/ui-theme'; import type { FieldFormat } from '../../../../field_formats/common'; -import type { - CommonXYReferenceLineLayerConfigResult, - ReferenceLineLayerConfigResult, - IconPosition, - YAxisMode, -} from '../../common'; -import type { LensMultiTable } from '../../common/types'; +import type { CommonXYReferenceLineLayerConfigResult, IconPosition, YAxisMode } from '../../common'; import { hasIcon } from '../helpers'; export const REFERENCE_LINE_MARKER_SIZE = 20; diff --git a/src/plugins/chart_expressions/expression_xy/server/plugin.ts b/src/plugins/chart_expressions/expression_xy/server/plugin.ts index 38f5526504ae0..9c0b5fa6609e9 100755 --- a/src/plugins/chart_expressions/expression_xy/server/plugin.ts +++ b/src/plugins/chart_expressions/expression_xy/server/plugin.ts @@ -14,12 +14,15 @@ import { yAxisConfigFunction, legendConfigFunction, gridlinesConfigFunction, - dataLayerConfigFunction, + dataLayerFunction, axisExtentConfigFunction, tickLabelsConfigFunction, labelsOrientationConfigFunction, - referenceLineLayerConfigFunction, + referenceLineLayerFunction, axisTitlesVisibilityConfigFunction, + extendedDataLayerFunction, + extendedReferenceLineLayerFunction, + layeredXyVisFunction, } from '../common'; import { SetupDeps } from './types'; @@ -30,13 +33,16 @@ export class ExpressionXyPlugin expressions.registerFunction(yAxisConfigFunction); expressions.registerFunction(legendConfigFunction); expressions.registerFunction(gridlinesConfigFunction); - expressions.registerFunction(dataLayerConfigFunction); + expressions.registerFunction(dataLayerFunction); + expressions.registerFunction(extendedDataLayerFunction); expressions.registerFunction(axisExtentConfigFunction); expressions.registerFunction(tickLabelsConfigFunction); expressions.registerFunction(labelsOrientationConfigFunction); - expressions.registerFunction(referenceLineLayerConfigFunction); + expressions.registerFunction(referenceLineLayerFunction); + expressions.registerFunction(extendedReferenceLineLayerFunction); expressions.registerFunction(axisTitlesVisibilityConfigFunction); expressions.registerFunction(xyVisFunction); + expressions.registerFunction(layeredXyVisFunction); } public start(core: CoreStart) {} diff --git a/x-pack/plugins/lens/public/index.ts b/x-pack/plugins/lens/public/index.ts index 7c26bc1d0a544..c33f5fb4d8283 100644 --- a/x-pack/plugins/lens/public/index.ts +++ b/x-pack/plugins/lens/public/index.ts @@ -11,7 +11,7 @@ export type { EmbeddableComponentProps, TypedLensByValueInput, } from './embeddable/embeddable_component'; -export type { XYState, XYLayerConfig } from './xy_visualization/types'; +export type { XYState, XYLayerConfig, ValidLayer } from './xy_visualization/types'; export type { DatasourcePublicAPI, DataType, @@ -73,7 +73,6 @@ export type { YScaleType, XScaleType, AxisConfig, - ValidLayer, XYCurveType, XYChartProps, LegendConfig, diff --git a/x-pack/plugins/lens/public/xy_visualization/axes_configuration.test.ts b/x-pack/plugins/lens/public/xy_visualization/axes_configuration.test.ts index a733f83b46338..27ff1382bf349 100644 --- a/x-pack/plugins/lens/public/xy_visualization/axes_configuration.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/axes_configuration.test.ts @@ -5,10 +5,10 @@ * 2.0. */ -import { DataLayerConfigResult } from '../../../../../src/plugins/chart_expressions/expression_xy/common'; import { layerTypes } from '../../common'; import { Datatable } from '../../../../../src/plugins/expressions/public'; import { getAxesConfiguration } from './axes_configuration'; +import { XYDataLayerConfig } from './types'; describe('axes_configuration', () => { const tables: Record = { @@ -219,8 +219,7 @@ describe('axes_configuration', () => { }, }; - const sampleLayer: DataLayerConfigResult = { - type: 'dataLayer', + const sampleLayer: XYDataLayerConfig = { layerId: 'first', layerType: layerTypes.DATA, seriesType: 'line', diff --git a/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.test.ts b/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.test.ts index 4448d14576f5a..368b213428ed7 100644 --- a/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.test.ts @@ -5,9 +5,9 @@ * 2.0. */ -import { DataLayerConfigResult } from '../../../../../src/plugins/chart_expressions/expression_xy/common'; import { FramePublicAPI } from '../types'; import { computeOverallDataDomain, getStaticValue } from './reference_line_helpers'; +import { XYDataLayerConfig } from './types'; function getActiveData(json: Array<{ id: string; rows: Array> }>) { return json.reduce((memo, { id, rows }) => { @@ -51,7 +51,7 @@ describe('reference_line helpers', () => { // accessor id has no hit in data expect( getStaticValue( - [{ layerId: 'id-a', seriesType: 'area' } as DataLayerConfigResult], // missing xAccessor for groupId == x + [{ layerId: 'id-a', seriesType: 'area' } as XYDataLayerConfig], // missing xAccessor for groupId == x 'x', { activeData: getActiveData([ @@ -69,7 +69,7 @@ describe('reference_line helpers', () => { seriesType: 'area', layerType: 'data', accessors: ['d'], - } as DataLayerConfigResult, + } as XYDataLayerConfig, ], // missing hit of accessor "d" in data 'yLeft', { @@ -88,7 +88,7 @@ describe('reference_line helpers', () => { seriesType: 'area', layerType: 'data', accessors: ['a'], - } as DataLayerConfigResult, + } as XYDataLayerConfig, ], // missing yConfig fallbacks to left axis, but the requested group is yRight 'yRight', { @@ -107,7 +107,7 @@ describe('reference_line helpers', () => { seriesType: 'area', layerType: 'data', accessors: ['a'], - } as DataLayerConfigResult, + } as XYDataLayerConfig, ], // same as above with x groupId 'x', { @@ -130,7 +130,7 @@ describe('reference_line helpers', () => { layerType: 'data', accessors: ['a'], yConfig: [{ forAccessor: 'a', axisMode: 'right' }], - } as DataLayerConfigResult, + } as XYDataLayerConfig, ], 'yRight', { @@ -155,7 +155,7 @@ describe('reference_line helpers', () => { seriesType: 'area', layerType: 'data', accessors: ['a'], - } as DataLayerConfigResult, + } as XYDataLayerConfig, ], 'yLeft', { @@ -178,7 +178,7 @@ describe('reference_line helpers', () => { layerType: 'data', accessors: ['a'], yConfig: [{ forAccessor: 'a', axisMode: 'right' }], - } as DataLayerConfigResult, + } as XYDataLayerConfig, ], 'yRight', { @@ -205,7 +205,7 @@ describe('reference_line helpers', () => { seriesType: 'area', layerType: 'data', accessors: ['a', 'b'], - } as DataLayerConfigResult, + } as XYDataLayerConfig, ], 'yLeft', { activeData: tables }, @@ -220,7 +220,7 @@ describe('reference_line helpers', () => { seriesType: 'area', layerType: 'data', accessors: ['a', 'b'], - } as DataLayerConfigResult, + } as XYDataLayerConfig, ], 'yRight', { activeData: tables }, @@ -243,7 +243,7 @@ describe('reference_line helpers', () => { seriesType: 'area', layerType: 'data', accessors: ['a', 'b'], - } as DataLayerConfigResult, + } as XYDataLayerConfig, ], 'yLeft', { activeData: tables }, @@ -258,7 +258,7 @@ describe('reference_line helpers', () => { seriesType: 'area', layerType: 'data', accessors: ['a', 'b'], - } as DataLayerConfigResult, + } as XYDataLayerConfig, ], 'yRight', { activeData: tables }, @@ -282,7 +282,7 @@ describe('reference_line helpers', () => { xScaleType: 'linear', isHistogram: false, palette: { type: 'palette', name: 'palette1' }, - } as DataLayerConfigResult, + } as XYDataLayerConfig, ], 'x', // this is influenced by the callback { @@ -310,7 +310,7 @@ describe('reference_line helpers', () => { xScaleType: 'linear', isHistogram: false, palette: { type: 'palette', name: 'palette1' }, - } as DataLayerConfigResult, + } as XYDataLayerConfig, ], 'x', { @@ -334,7 +334,7 @@ describe('reference_line helpers', () => { for (const seriesType of ['bar_stacked', 'bar_horizontal_stacked', 'area_stacked']) expect( computeOverallDataDomain( - [{ layerId: 'id-a', seriesType, accessors: ['a', 'b', 'c'] } as DataLayerConfigResult], + [{ layerId: 'id-a', seriesType, accessors: ['a', 'b', 'c'] } as XYDataLayerConfig], ['a', 'b', 'c'], getActiveData([ { @@ -360,7 +360,7 @@ describe('reference_line helpers', () => { ]) expect( computeOverallDataDomain( - [{ layerId: 'id-a', seriesType, accessors: ['a', 'b', 'c'] } as DataLayerConfigResult], + [{ layerId: 'id-a', seriesType, accessors: ['a', 'b', 'c'] } as XYDataLayerConfig], ['a', 'b', 'c'], getActiveData([ { @@ -385,7 +385,7 @@ describe('reference_line helpers', () => { [ { layerId: 'id-a', seriesType, accessors: ['a', 'b', 'c'] }, { layerId: 'id-b', seriesType, accessors: ['d', 'e', 'f'] }, - ] as DataLayerConfigResult[], + ] as XYDataLayerConfig[], ['a', 'b', 'c', 'd', 'e', 'f'], getActiveData([ { id: 'id-a', rows: [{ a: 25, b: 100, c: 100 }] }, @@ -399,7 +399,7 @@ describe('reference_line helpers', () => { [ { layerId: 'id-a', seriesType, accessors: ['a', 'b', 'c'] }, { layerId: 'id-b', seriesType, accessors: ['d', 'e', 'f'] }, - ] as DataLayerConfigResult[], + ] as XYDataLayerConfig[], ['a', 'b', 'c', 'd', 'e', 'f'], getActiveData([ { @@ -435,7 +435,7 @@ describe('reference_line helpers', () => { [ { layerId: 'id-a', seriesType, accessors: ['a', 'b', 'c'] }, { layerId: 'id-b', seriesType, accessors: ['d', 'e', 'f'] }, - ] as DataLayerConfigResult[], + ] as XYDataLayerConfig[], ['a', 'b', 'c', 'd', 'e', 'f'], getActiveData([ { id: 'id-a', rows: Array(3).fill({ a: 100, b: 100, c: 100 }) }, @@ -453,7 +453,7 @@ describe('reference_line helpers', () => { [ { layerId: 'id-a', seriesType: nonStackedSeries, accessors: ['a', 'b', 'c'] }, { layerId: 'id-b', seriesType: stackedSeries, accessors: ['d', 'e', 'f'] }, - ] as DataLayerConfigResult[], + ] as XYDataLayerConfig[], ['a', 'b', 'c', 'd', 'e', 'f'], getActiveData([ { id: 'id-a', rows: [{ a: 100, b: 100, c: 100 }] }, @@ -475,7 +475,7 @@ describe('reference_line helpers', () => { [ { layerId: 'id-a', seriesType, xAccessor: 'c', accessors: ['a', 'b'] }, { layerId: 'id-b', seriesType, xAccessor: 'f', accessors: ['d', 'e'] }, - ] as DataLayerConfigResult[], + ] as XYDataLayerConfig[], ['a', 'b', 'd', 'e'], getActiveData([ { @@ -502,7 +502,7 @@ describe('reference_line helpers', () => { [ { layerId: 'id-a', seriesType, accessors: ['c'] }, { layerId: 'id-b', seriesType, accessors: ['f'] }, - ] as DataLayerConfigResult[], + ] as XYDataLayerConfig[], ['c', 'f'], getActiveData([ { @@ -530,7 +530,7 @@ describe('reference_line helpers', () => { [ { layerId: 'id-a', seriesType, xAccessor: 'c', accessors: ['a', 'b'] }, { layerId: 'id-b', seriesType, xAccessor: 'f', accessors: ['d', 'e'] }, - ] as DataLayerConfigResult[], + ] as XYDataLayerConfig[], ['a', 'b', 'd', 'e'], getActiveData([ { @@ -559,7 +559,7 @@ describe('reference_line helpers', () => { layerId: 'id-a', seriesType: 'area_stacked', accessors: ['a', 'b', 'c'], - } as DataLayerConfigResult, + } as XYDataLayerConfig, ], ['a', 'b', 'c'], getActiveData([ @@ -583,7 +583,7 @@ describe('reference_line helpers', () => { layerId: 'id-a', seriesType: 'area', accessors: ['a', 'b', 'c'], - } as DataLayerConfigResult, + } as XYDataLayerConfig, ], ['a', 'b', 'c'], getActiveData([ @@ -618,7 +618,7 @@ describe('reference_line helpers', () => { [ { layerId: 'id-a', seriesType: 'area', accessors: ['a', 'b', 'c'] }, { layerId: 'id-b', seriesType: 'line', accessors: ['d', 'e', 'f'] }, - ] as DataLayerConfigResult[], + ] as XYDataLayerConfig[], ['a', 'b'], getActiveData([{ id: 'id-c', rows: [{ a: 100, b: 100 }] }]) // mind the layer id here ) @@ -629,7 +629,7 @@ describe('reference_line helpers', () => { [ { layerId: 'id-a', seriesType: 'bar', accessors: ['a', 'b', 'c'] }, { layerId: 'id-b', seriesType: 'bar_stacked' }, - ] as DataLayerConfigResult[], + ] as XYDataLayerConfig[], ['a', 'b'], getActiveData([]) ) diff --git a/x-pack/plugins/lens/public/xy_visualization/state_helpers.ts b/x-pack/plugins/lens/public/xy_visualization/state_helpers.ts index 70bbb8d115695..5a3d72914f83f 100644 --- a/x-pack/plugins/lens/public/xy_visualization/state_helpers.ts +++ b/x-pack/plugins/lens/public/xy_visualization/state_helpers.ts @@ -10,13 +10,13 @@ import type { FramePublicAPI, DatasourcePublicAPI } from '../types'; import type { SeriesType, YConfig, - ValidLayer, } from '../../../../../src/plugins/chart_expressions/expression_xy/common'; import { visualizationTypes, XYLayerConfig, XYDataLayerConfig, XYReferenceLineLayerConfig, + ValidLayer, } from './types'; import { getDataLayers, isDataLayer } from './visualization_helpers'; diff --git a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts index 11bb8c506ec1b..4a08a405a2576 100644 --- a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts +++ b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts @@ -8,14 +8,10 @@ import { Ast } from '@kbn/interpreter'; import { ScaleType } from '@elastic/charts'; import { PaletteRegistry } from 'src/plugins/charts/public'; -import type { State, XYDataLayerConfig, XYReferenceLineLayerConfig } from './types'; +import type { State, XYDataLayerConfig, XYReferenceLineLayerConfig, ValidLayer } from './types'; import { OperationMetadata, DatasourcePublicAPI } from '../types'; import { getColumnToLabelMap } from './state_helpers'; -import type { - ReferenceLineLayerConfigResult, - ValidLayer, - YConfig, -} from '../../../../../src/plugins/chart_expressions/expression_xy/common'; +import type { YConfig } from '../../../../../src/plugins/chart_expressions/expression_xy/common'; import { hasIcon } from './xy_config_panel/shared/icon_select'; import { defaultReferenceLineColor } from './color_assignment'; import { getDefaultVisualValuesForLayer } from '../shared_components/datasource_default_values'; @@ -326,7 +322,7 @@ export const buildExpression = ( }; const referenceLineLayerToExpression = ( - layer: ReferenceLineLayerConfigResult, + layer: XYReferenceLineLayerConfig, datasourceLayer: DatasourcePublicAPI ): Ast => { return { @@ -336,7 +332,6 @@ const referenceLineLayerToExpression = ( type: 'function', function: 'referenceLineLayer', arguments: { - layerId: [layer.layerId], yConfig: layer.yConfig ? layer.yConfig.map((yConfig) => yConfigToExpression(yConfig, defaultReferenceLineColor) @@ -374,7 +369,6 @@ const dataLayerToExpression = ( type: 'function', function: 'dataLayer', arguments: { - layerId: [layer.layerId], hide: [Boolean(layer.hide)], xAccessor: layer.xAccessor ? [layer.xAccessor] : [], yScaleType: [ diff --git a/x-pack/plugins/lens/public/xy_visualization/types.ts b/x-pack/plugins/lens/public/xy_visualization/types.ts index ed24766025a63..1d699e0a891bf 100644 --- a/x-pack/plugins/lens/public/xy_visualization/types.ts +++ b/x-pack/plugins/lens/public/xy_visualization/types.ts @@ -44,6 +44,7 @@ export interface XYDataLayerConfig yScaleType?: YScaleType; xScaleType?: XScaleType; isHistogram?: boolean; + layerId: string; } export interface XYReferenceLineLayerConfig @@ -51,10 +52,16 @@ export interface XYReferenceLineLayerConfig layerType: LayerType; yConfig?: YConfig[]; palette?: PaletteOutput; + layerId: string; } export type XYLayerConfig = XYDataLayerConfig | XYReferenceLineLayerConfig; +export interface ValidLayer extends XYDataLayerConfig { + xAccessor: NonNullable; + layerId: string; +} + // Persisted parts of the state export interface XYState { preferredSeriesType: SeriesType; diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts b/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts index 7004523125df6..2ef3835e6ce9f 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts @@ -16,10 +16,7 @@ import type { XYDataLayerConfig, XYReferenceLineLayerConfig, } from './types'; -import type { - DataLayerConfigResult, - SeriesType, -} from '../../../../../src/plugins/chart_expressions/expression_xy/common'; +import type { SeriesType } from '../../../../../src/plugins/chart_expressions/expression_xy/common'; import { layerTypes } from '../../common'; import { createMockDatasource, createMockFramePublicAPI } from '../mocks'; import { LensIconChartBar } from '../assets/chart_bar'; @@ -115,7 +112,7 @@ describe('xy_visualization', () => { return { ...state, layers: types.map((t, i) => ({ - ...(state.layers[0] as DataLayerConfigResult), + ...(state.layers[0] as XYDataLayerConfig), layerId: `layer_${i}`, seriesType: t, })), diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/layer_header.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/layer_header.tsx index 6044b58d8207f..99c529ef8449b 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/layer_header.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/layer_header.tsx @@ -10,17 +10,14 @@ import { i18n } from '@kbn/i18n'; import { EuiIcon, EuiPopover, EuiSelectable, EuiText, EuiPopoverTitle } from '@elastic/eui'; import type { VisualizationLayerWidgetProps, VisualizationType } from '../../types'; import { State, visualizationTypes } from '../types'; -import { - DataLayerConfigResult, - SeriesType, -} from '../../../../../../src/plugins/chart_expressions/expression_xy/common'; +import { SeriesType } from '../../../../../../src/plugins/chart_expressions/expression_xy/common'; import { isHorizontalChart, isHorizontalSeries } from '../state_helpers'; import { trackUiEvent } from '../../lens_ui_telemetry'; import { StaticHeader } from '../../shared_components'; import { ToolbarButton } from '../../../../../../src/plugins/kibana_react/public'; import { LensIconChartBarReferenceLine } from '../../assets/chart_bar_reference_line'; import { updateLayer } from '.'; -import { isReferenceLayer } from '../visualization_helpers'; +import { isDataLayer, isReferenceLayer } from '../visualization_helpers'; export function LayerHeader(props: VisualizationLayerWidgetProps) { const layer = props.state.layers.find((l) => l.layerId === props.layerId); @@ -47,7 +44,7 @@ function ReferenceLayerHeader() { function DataLayerHeader(props: VisualizationLayerWidgetProps) { const [isPopoverOpen, setPopoverIsOpen] = useState(false); const { state, layerId } = props; - const layers = state.layers as DataLayerConfigResult[]; + const layers = state.layers.filter(isDataLayer); const index = layers.findIndex((l) => l.layerId === layerId); const layer = layers[index]; const currentVisType = visualizationTypes.find(({ id }) => id === layer.seriesType)!; diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/index.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/index.tsx index 54fd07d5ce688..13e9710021cb3 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/index.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/index.tsx @@ -11,9 +11,8 @@ import { ToolbarPopover, TooltipWrapper, ValueLabelsSettings } from '../../../sh import { MissingValuesOptions } from './missing_values_option'; import { LineCurveOption } from './line_curve_option'; import { FillOpacityOption } from './fill_opacity_option'; -import { XYState } from '../../types'; +import { XYState, ValidLayer } from '../../types'; import { hasHistogramSeries } from '../../state_helpers'; -import { ValidLayer } from '../../../../../../../src/plugins/chart_expressions/expression_xy/common'; import type { FramePublicAPI } from '../../../types'; import { getDataLayers } from '../../visualization_helpers'; diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/xy_config_panel.test.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/xy_config_panel.test.tsx index 7015dd780c827..4e408979c3b1d 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/xy_config_panel.test.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/xy_config_panel.test.tsx @@ -18,7 +18,6 @@ import { createMockFramePublicAPI, createMockDatasource } from '../../mocks'; import { chartPluginMock } from 'src/plugins/charts/public/mocks'; import { EuiColorPicker } from '@elastic/eui'; import { layerTypes } from '../../../common'; -import { DataLayerConfigResult } from '../../../../../../src/plugins/chart_expressions/expression_xy/common'; describe('XY Config panels', () => { let frame: FramePublicAPI; @@ -227,7 +226,7 @@ describe('XY Config panels', () => { groupId="left" state={{ ...state, - layers: [{ ...state.layers[0], seriesType: 'bar_horizontal' } as DataLayerConfigResult], + layers: [{ ...state.layers[0], seriesType: 'bar_horizontal' } as XYDataLayerConfig], }} formatFactory={jest.fn()} paletteService={chartPluginMock.createPaletteRegistry()} From bb91aee268a6dbd2cc14b859fa7946477b7ec44e Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Wed, 23 Mar 2022 16:13:03 +0200 Subject: [PATCH 055/133] Added tables to layers. --- .../public/components/xy_chart.tsx | 14 ++-- .../editor_frame/expression_helpers.ts | 65 +++++++++++++---- .../editor_frame/suggestion_panel.tsx | 54 +++++++++----- x-pack/plugins/lens/public/types.ts | 8 ++- .../public/xy_visualization/to_expression.ts | 70 +++++++++++++++---- .../lens/public/xy_visualization/types.ts | 4 +- .../public/xy_visualization/visualization.tsx | 10 ++- 7 files changed, 169 insertions(+), 56 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx index 1ff0bd0514753..7fce430ea23cc 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx @@ -71,7 +71,11 @@ import { ReferenceLineAnnotations, } from './reference_lines'; import { visualizationDefinitions } from '../definitions'; -import { CommonXYDataLayerConfigResult, CommonXYLayerConfigResult } from '../../common/types'; +import { + CommonXYDataLayerConfigResult, + CommonXYLayerConfigResult, + DataLayerConfigResult, +} from '../../common/types'; import './xy_chart.scss'; @@ -173,13 +177,13 @@ export function XYChart({ }); if (filteredLayers.length === 0) { - const dataLayers: CommonXYDataLayerConfigResult[] = layers.filter(isDataLayer); - const icon: IconType = - dataLayers.length > 0 ? getIconForSeriesType(dataLayers[0].seriesType) : 'bar'; + const icon: IconType = getIconForSeriesType( + (layers?.[0] as DataLayerConfigResult)?.seriesType || 'bar' + ); return ; } - const dataLayers = filteredLayers.filter(isDataLayer); + const dataLayers: CommonXYDataLayerConfigResult[] = filteredLayers.filter(isDataLayer); // use formatting hint of first x axis column to format ticks const xAxisColumn = dataLayers[0]?.table.columns.find( diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/expression_helpers.ts b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/expression_helpers.ts index 600341931f575..4379409bd2ee9 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/expression_helpers.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/expression_helpers.ts @@ -9,11 +9,10 @@ import { Ast, AstFunction, fromExpression } from '@kbn/interpreter'; import { DatasourceStates } from '../../state_management'; import { Visualization, DatasourcePublicAPI, DatasourceMap } from '../../types'; -export function prependDatasourceExpression( - visualizationExpression: Ast | string | null, +export function getDatasourceExpressionsByLayers( datasourceMap: DatasourceMap, datasourceStates: DatasourceStates -): Ast | null { +): null | Record { const datasourceExpressions: Array<[string, Ast | string]> = []; Object.entries(datasourceMap).forEach(([datasourceId, datasource]) => { @@ -28,19 +27,41 @@ export function prependDatasourceExpression( }); }); - if (datasourceExpressions.length === 0 || visualizationExpression === null) { + if (datasourceExpressions.length === 0) { return null; } - const parsedDatasourceExpressions: Array<[string, Ast]> = datasourceExpressions.map( - ([layerId, expr]) => [layerId, typeof expr === 'string' ? fromExpression(expr) : expr] + + return datasourceExpressions.reduce( + (exprs, [layerId, expr]) => ({ + ...exprs, + [layerId]: typeof expr === 'string' ? fromExpression(expr) : expr, + }), + {} ); +} + +export function prependDatasourceExpression( + visualizationExpression: Ast | string | null, + datasourceMap: DatasourceMap, + datasourceStates: DatasourceStates +): Ast | null { + const datasourceExpressionsByLayers = getDatasourceExpressionsByLayers( + datasourceMap, + datasourceStates + ); + + if (datasourceExpressionsByLayers === null || visualizationExpression === null) { + return null; + } + + const parsedDatasourceExpressions = Object.entries(datasourceExpressionsByLayers); const datafetchExpression: AstFunction = { type: 'function', function: 'lens_merge_tables', arguments: { layerIds: parsedDatasourceExpressions.map(([id]) => id), - tables: parsedDatasourceExpressions.map(([id, expr]) => expr), + tables: parsedDatasourceExpressions.map(([, expr]) => expr), }, }; @@ -79,16 +100,32 @@ export function buildExpression({ if (visualization === null) { return null; } + + if (visualization.shouldBuildDatasourceExpressionManually?.()) { + const datasourceExpressionsByLayers = getDatasourceExpressionsByLayers( + datasourceMap, + datasourceStates + ); + + const visualizationExpression = visualization.toExpression( + visualizationState, + datasourceLayers, + { + title, + description, + }, + datasourceExpressionsByLayers ?? undefined + ); + + return typeof visualizationExpression === 'string' + ? fromExpression(visualizationExpression) + : visualizationExpression; + } + const visualizationExpression = visualization.toExpression(visualizationState, datasourceLayers, { title, description, }); - const completeExpression = prependDatasourceExpression( - visualizationExpression, - datasourceMap, - datasourceStates - ); - - return completeExpression; + return prependDatasourceExpression(visualizationExpression, datasourceMap, datasourceStates); } diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.tsx index 1556be13a3341..6ec2486490fd1 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.tsx @@ -25,7 +25,7 @@ import { EuiSpacer, } from '@elastic/eui'; import { IconType } from '@elastic/eui/src/components/icon/icon'; -import { Ast, toExpression } from '@kbn/interpreter'; +import { Ast, fromExpression, toExpression } from '@kbn/interpreter'; import { i18n } from '@kbn/i18n'; import classNames from 'classnames'; import { ExecutionContextSearch } from 'src/plugins/data/public'; @@ -42,7 +42,10 @@ import { ReactExpressionRendererProps, ReactExpressionRendererType, } from '../../../../../../src/plugins/expressions/public'; -import { prependDatasourceExpression } from './expression_helpers'; +import { + getDatasourceExpressionsByLayers, + prependDatasourceExpression, +} from './expression_helpers'; import { trackUiEvent, trackSuggestionEvent } from '../../lens_ui_telemetry'; import { getMissingIndexPattern, validateDatasourceAndVisualization } from './state_helpers'; import { @@ -479,6 +482,7 @@ function getPreviewExpression( visualizableState: VisualizableState, visualization: Visualization, datasources: Record, + datasourceStates: DatasourceStates, frame: FramePublicAPI ) { if (!visualization.toPreviewExpression) { @@ -510,6 +514,19 @@ function getPreviewExpression( }); } + if (visualization.shouldBuildDatasourceExpressionManually?.()) { + const datasourceExpressionsByLayers = getDatasourceExpressionsByLayers( + datasources, + datasourceStates + ); + + return visualization.toPreviewExpression( + visualizableState.visualizationState, + suggestionFrameApi.datasourceLayers, + datasourceExpressionsByLayers ?? undefined + ); + } + return visualization.toPreviewExpression( visualizableState.visualizationState, suggestionFrameApi.datasourceLayers @@ -526,10 +543,25 @@ function preparePreviewExpression( const suggestionDatasourceId = visualizableState.datasourceId; const suggestionDatasourceState = visualizableState.datasourceState; + const datasourceStatesWithSuggestions = suggestionDatasourceId + ? { + ...datasourceStates, + [suggestionDatasourceId]: { + isLoading: false, + state: suggestionDatasourceState, + }, + } + : datasourceStates; + + const previewExprDatasourcesStates = visualization.shouldBuildDatasourceExpressionManually?.() + ? datasourceStatesWithSuggestions + : datasourceStates; + const expression = getPreviewExpression( visualizableState, visualization, datasourceMap, + previewExprDatasourcesStates, framePublicAPI ); @@ -537,19 +569,9 @@ function preparePreviewExpression( return; } - const expressionWithDatasource = prependDatasourceExpression( - expression, - datasourceMap, - suggestionDatasourceId - ? { - ...datasourceStates, - [suggestionDatasourceId]: { - isLoading: false, - state: suggestionDatasourceState, - }, - } - : datasourceStates - ); + if (visualization.shouldBuildDatasourceExpressionManually?.()) { + return typeof expression === 'string' ? fromExpression(expression) : expression; + } - return expressionWithDatasource; + return prependDatasourceExpression(expression, datasourceMap, datasourceStatesWithSuggestions); } diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index 0104a75dd99ab..531e48e3b166c 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -873,7 +873,8 @@ export interface Visualization { toExpression: ( state: T, datasourceLayers: Record, - attributes?: Partial<{ title: string; description: string }> + attributes?: Partial<{ title: string; description: string }>, + datasourceExpressionsByLayers?: Record ) => ExpressionAstExpression | string | null; /** * Expression to render a preview version of the chart in very constrained space. @@ -881,7 +882,8 @@ export interface Visualization { */ toPreviewExpression?: ( state: T, - datasourceLayers: Record + datasourceLayers: Record, + datasourceExpressionsByLayers?: Record ) => ExpressionAstExpression | string | null; /** * The frame will call this function on all visualizations at few stages (pre-build/build error) in order @@ -906,6 +908,8 @@ export interface Visualization { * On Edit events the frame will call this to know what's going to be the next visualization state */ onEditAction?: (state: T, event: LensEditEvent) => T; + + shouldBuildDatasourceExpressionManually?: () => boolean; } export interface LensFilterEvent { diff --git a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts index 4a08a405a2576..a97242656ddb1 100644 --- a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts +++ b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts @@ -5,10 +5,16 @@ * 2.0. */ -import { Ast } from '@kbn/interpreter'; +import { Ast, toExpression as toExpressionAst } from '@kbn/interpreter'; import { ScaleType } from '@elastic/charts'; import { PaletteRegistry } from 'src/plugins/charts/public'; -import type { State, XYDataLayerConfig, XYReferenceLineLayerConfig, ValidLayer } from './types'; +import type { + State, + XYDataLayerConfig, + XYReferenceLineLayerConfig, + ValidLayer, + ValidXYDataLayerConfig, +} from './types'; import { OperationMetadata, DatasourcePublicAPI } from '../types'; import { getColumnToLabelMap } from './state_helpers'; import type { YConfig } from '../../../../../src/plugins/chart_expressions/expression_xy/common'; @@ -33,7 +39,8 @@ export const toExpression = ( state: State, datasourceLayers: Record, paletteService: PaletteRegistry, - attributes: Partial<{ title: string; description: string }> = {} + attributes: Partial<{ title: string; description: string }> = {}, + datasourceExpressionsByLayers: Record ): Ast | null => { if (!state || !state.layers.length) { return null; @@ -49,13 +56,21 @@ export const toExpression = ( }); }); - return buildExpression(state, metadata, datasourceLayers, paletteService, attributes); + return buildExpression( + state, + metadata, + datasourceLayers, + paletteService, + attributes, + datasourceExpressionsByLayers + ); }; export function toPreviewExpression( state: State, datasourceLayers: Record, - paletteService: PaletteRegistry + paletteService: PaletteRegistry, + datasourceExpressionsByLayers: Record ) { return toExpression( { @@ -84,7 +99,8 @@ export function toPreviewExpression( }, datasourceLayers, paletteService, - {} + {}, + datasourceExpressionsByLayers ); } @@ -119,7 +135,8 @@ export const buildExpression = ( metadata: Record>, datasourceLayers: Record, paletteService: PaletteRegistry, - attributes: Partial<{ title: string; description: string }> = {} + attributes: Partial<{ title: string; description: string }> = {}, + datasourceExpressionsByLayers: Record ): Ast | null => { const validLayers = state.layers .filter((layer): layer is ValidLayer => Boolean(layer.accessors.length)) @@ -144,7 +161,7 @@ export const buildExpression = ( chain: [ { type: 'function', - function: 'xyVis', + function: 'layeredXyVis', arguments: { title: [attributes?.title || ''], description: [attributes?.description || ''], @@ -302,17 +319,20 @@ export const buildExpression = ( hideEndzones: [state?.hideEndzones || false], valuesInLegend: [state?.valuesInLegend || false], layers: validLayers.map((layer) => { + const datasourceExpression = datasourceExpressionsByLayers[layer.layerId]; if (isDataLayer(layer)) { return dataLayerToExpression( layer, datasourceLayers[layer.layerId], metadata, - paletteService + paletteService, + datasourceExpression ); } return referenceLineLayerToExpression( layer, - datasourceLayers[(layer as XYReferenceLineLayerConfig).layerId] + datasourceLayers[layer.layerId], + datasourceExpression ); }), }, @@ -323,14 +343,15 @@ export const buildExpression = ( const referenceLineLayerToExpression = ( layer: XYReferenceLineLayerConfig, - datasourceLayer: DatasourcePublicAPI + datasourceLayer: DatasourcePublicAPI, + datasourceExpression: Ast ): Ast => { return { type: 'expression', chain: [ { type: 'function', - function: 'referenceLineLayer', + function: 'extendedReferenceLineLayer', arguments: { yConfig: layer.yConfig ? layer.yConfig.map((yConfig) => @@ -339,6 +360,15 @@ const referenceLineLayerToExpression = ( : [], accessors: layer.accessors, columnToLabel: [JSON.stringify(getColumnToLabelMap(layer, datasourceLayer))], + table: [ + { + type: 'expression', + chain: [ + { type: 'function', function: 'kibana', arguments: {} }, + ...datasourceExpression.chain, + ], + }, + ], }, }, ], @@ -346,10 +376,11 @@ const referenceLineLayerToExpression = ( }; const dataLayerToExpression = ( - layer: ValidLayer, + layer: ValidXYDataLayerConfig, datasourceLayer: DatasourcePublicAPI, metadata: Record>, - paletteService: PaletteRegistry + paletteService: PaletteRegistry, + datasourceExpression: Ast ): Ast => { const columnToLabel = getColumnToLabelMap(layer, datasourceLayer); @@ -367,7 +398,7 @@ const dataLayerToExpression = ( chain: [ { type: 'function', - function: 'dataLayer', + function: 'extendedDataLayer', arguments: { hide: [Boolean(layer.hide)], xAccessor: layer.xAccessor ? [layer.xAccessor] : [], @@ -383,6 +414,15 @@ const dataLayerToExpression = ( seriesType: [layer.seriesType], accessors: layer.accessors, columnToLabel: [JSON.stringify(columnToLabel)], + table: [ + { + type: 'expression', + chain: [ + { type: 'function', function: 'kibana', arguments: {} }, + ...datasourceExpression.chain, + ], + }, + ], ...(layer.palette ? { palette: [ diff --git a/x-pack/plugins/lens/public/xy_visualization/types.ts b/x-pack/plugins/lens/public/xy_visualization/types.ts index 1d699e0a891bf..4b30d6eed9b01 100644 --- a/x-pack/plugins/lens/public/xy_visualization/types.ts +++ b/x-pack/plugins/lens/public/xy_visualization/types.ts @@ -57,11 +57,13 @@ export interface XYReferenceLineLayerConfig export type XYLayerConfig = XYDataLayerConfig | XYReferenceLineLayerConfig; -export interface ValidLayer extends XYDataLayerConfig { +export interface ValidXYDataLayerConfig extends XYDataLayerConfig { xAccessor: NonNullable; layerId: string; } +export type ValidLayer = ValidXYDataLayerConfig | XYReferenceLineLayerConfig; + // Persisted parts of the state export interface XYState { preferredSeriesType: SeriesType; diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx index 841c9678e2ddf..15a5cb4214685 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx @@ -471,9 +471,13 @@ export const getXyVisualization = ({ ); }, - toExpression: (state, layers, attributes) => - toExpression(state, layers, paletteService, attributes), - toPreviewExpression: (state, layers) => toPreviewExpression(state, layers, paletteService), + shouldBuildDatasourceExpressionManually: () => true, + + toExpression: (state, layers, attributes, datasourceExpressionsByLayers = {}) => + toExpression(state, layers, paletteService, attributes, datasourceExpressionsByLayers), + + toPreviewExpression: (state, layers, datasourceExpressionsByLayers = {}) => + toPreviewExpression(state, layers, paletteService, datasourceExpressionsByLayers), getErrorMessages(state, datasourceLayers) { // Data error handling below here From 3e6d15a310b4f1b096b91ee839860162ef08c151 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Wed, 23 Mar 2022 17:14:02 +0200 Subject: [PATCH 056/133] Checks fixed. --- .../lens/public/xy_visualization/state_helpers.ts | 9 ++++++--- .../lens/public/xy_visualization/to_expression.ts | 2 +- .../xy_config_panel/visual_options_popover/index.tsx | 6 ++---- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/lens/public/xy_visualization/state_helpers.ts b/x-pack/plugins/lens/public/xy_visualization/state_helpers.ts index 5a3d72914f83f..34674bad51e9a 100644 --- a/x-pack/plugins/lens/public/xy_visualization/state_helpers.ts +++ b/x-pack/plugins/lens/public/xy_visualization/state_helpers.ts @@ -16,7 +16,6 @@ import { XYLayerConfig, XYDataLayerConfig, XYReferenceLineLayerConfig, - ValidLayer, } from './types'; import { getDataLayers, isDataLayer } from './visualization_helpers'; @@ -80,7 +79,7 @@ export const getColumnToLabelMap = ( }; export function hasHistogramSeries( - layers: ValidLayer[] = [], + layers: XYDataLayerConfig[] = [], datasourceLayers?: FramePublicAPI['datasourceLayers'] ) { if (!datasourceLayers) { @@ -88,7 +87,11 @@ export function hasHistogramSeries( } const validLayers = layers.filter(({ accessors }) => accessors.length); - return validLayers.some(({ layerId, xAccessor }: ValidLayer) => { + return validLayers.some(({ layerId, xAccessor }: XYDataLayerConfig) => { + if (!xAccessor) { + return false; + } + const xAxisOperation = datasourceLayers[layerId].getOperationForColumnId(xAccessor); return ( xAxisOperation && diff --git a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts index a97242656ddb1..9631b82a6e30f 100644 --- a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts +++ b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { Ast, toExpression as toExpressionAst } from '@kbn/interpreter'; +import { Ast } from '@kbn/interpreter'; import { ScaleType } from '@elastic/charts'; import { PaletteRegistry } from 'src/plugins/charts/public'; import type { diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/index.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/index.tsx index 13e9710021cb3..acdc4d373996f 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/index.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/index.tsx @@ -11,7 +11,7 @@ import { ToolbarPopover, TooltipWrapper, ValueLabelsSettings } from '../../../sh import { MissingValuesOptions } from './missing_values_option'; import { LineCurveOption } from './line_curve_option'; import { FillOpacityOption } from './fill_opacity_option'; -import { XYState, ValidLayer } from '../../types'; +import { XYState } from '../../types'; import { hasHistogramSeries } from '../../state_helpers'; import type { FramePublicAPI } from '../../../types'; import { getDataLayers } from '../../visualization_helpers'; @@ -66,9 +66,7 @@ export const VisualOptionsPopover: React.FC = ({ ['area_stacked', 'area', 'area_percentage_stacked'].includes(seriesType) ); - const isHistogramSeries = Boolean( - hasHistogramSeries(dataLayers as ValidLayer[], datasourceLayers) - ); + const isHistogramSeries = Boolean(hasHistogramSeries(dataLayers, datasourceLayers)); const isValueLabelsEnabled = !hasNonBarSeries && hasBarNotStacked && !isHistogramSeries; const isFittingEnabled = hasNonBarSeries; From ac86ab91e556466822c363d21c90e1f472f35dbd Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Wed, 23 Mar 2022 17:39:06 +0200 Subject: [PATCH 057/133] updated tests. --- .../__snapshots__/to_expression.test.ts.snap | 19 ++++-- .../xy_visualization/to_expression.test.ts | 65 +++++++++++++++---- 2 files changed, 67 insertions(+), 17 deletions(-) diff --git a/x-pack/plugins/lens/public/xy_visualization/__snapshots__/to_expression.test.ts.snap b/x-pack/plugins/lens/public/xy_visualization/__snapshots__/to_expression.test.ts.snap index ebbc489a2d51c..d263a32cd006c 100644 --- a/x-pack/plugins/lens/public/xy_visualization/__snapshots__/to_expression.test.ts.snap +++ b/x-pack/plugins/lens/public/xy_visualization/__snapshots__/to_expression.test.ts.snap @@ -104,15 +104,24 @@ Object { "isHistogram": Array [ false, ], - "layerId": Array [ - "first", - ], "seriesType": Array [ "area", ], "splitAccessor": Array [ "d", ], + "table": Array [ + Object { + "chain": Array [ + Object { + "arguments": Object {}, + "function": "kibana", + "type": "function", + }, + ], + "type": "expression", + }, + ], "xAccessor": Array [ "a", ], @@ -124,7 +133,7 @@ Object { "linear", ], }, - "function": "dataLayer", + "function": "extendedDataLayer", "type": "function", }, ], @@ -241,7 +250,7 @@ Object { "", ], }, - "function": "xyVis", + "function": "layeredXyVis", "type": "function", }, ], diff --git a/x-pack/plugins/lens/public/xy_visualization/to_expression.test.ts b/x-pack/plugins/lens/public/xy_visualization/to_expression.test.ts index ac3fdcf30a4ad..6cc3ed3b3e63c 100644 --- a/x-pack/plugins/lens/public/xy_visualization/to_expression.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/to_expression.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { Ast } from '@kbn/interpreter'; +import { Ast, fromExpression } from '@kbn/interpreter'; import { Position } from '@elastic/charts'; import { chartPluginMock } from '../../../../../src/plugins/charts/public/mocks'; import { getXyVisualization } from './xy_visualization'; @@ -26,6 +26,8 @@ describe('#toExpression', () => { let mockDatasource: ReturnType; let frame: ReturnType; + let datasourceExpressionsByLayers: Record; + beforeEach(() => { frame = createMockFramePublicAPI(); mockDatasource = createMockDatasource('testDatasource'); @@ -44,6 +46,23 @@ describe('#toExpression', () => { frame.datasourceLayers = { first: mockDatasource.publicAPIMock, }; + + const datasourceExpression = mockDatasource.toExpression( + frame.datasourceLayers.first, + 'first' + ) ?? { + type: 'expression', + chain: [], + }; + const exprAst = + typeof datasourceExpression === 'string' + ? fromExpression(datasourceExpression) + : datasourceExpression; + + datasourceExpressionsByLayers = { + first: exprAst, + referenceLine: exprAst, + }; }); it('should map to a valid AST', () => { @@ -78,7 +97,9 @@ describe('#toExpression', () => { }, ], }, - frame.datasourceLayers + frame.datasourceLayers, + undefined, + datasourceExpressionsByLayers ) ).toMatchSnapshot(); }); @@ -102,7 +123,9 @@ describe('#toExpression', () => { }, ], }, - frame.datasourceLayers + frame.datasourceLayers, + undefined, + datasourceExpressionsByLayers ) as Ast ).chain[0].arguments.fittingFunction[0] ).toEqual('None'); @@ -125,7 +148,9 @@ describe('#toExpression', () => { }, ], }, - frame.datasourceLayers + frame.datasourceLayers, + undefined, + datasourceExpressionsByLayers ) as Ast; expect( (expression.chain[0].arguments.axisTitlesVisibilitySettings[0] as Ast).chain[0].arguments @@ -153,7 +178,9 @@ describe('#toExpression', () => { }, ], }, - frame.datasourceLayers + frame.datasourceLayers, + undefined, + datasourceExpressionsByLayers ) as Ast; expect((expression.chain[0].arguments.layers[0] as Ast).chain[0].arguments.xAccessor).toEqual( [] @@ -178,7 +205,9 @@ describe('#toExpression', () => { }, ], }, - frame.datasourceLayers + frame.datasourceLayers, + undefined, + datasourceExpressionsByLayers ) ).toBeNull(); }); @@ -200,7 +229,9 @@ describe('#toExpression', () => { }, ], }, - frame.datasourceLayers + frame.datasourceLayers, + undefined, + datasourceExpressionsByLayers )! as Ast; expect(mockDatasource.publicAPIMock.getOperationForColumnId).toHaveBeenCalledWith('b'); @@ -237,7 +268,9 @@ describe('#toExpression', () => { }, ], }, - frame.datasourceLayers + frame.datasourceLayers, + undefined, + datasourceExpressionsByLayers ) as Ast; expect( (expression.chain[0].arguments.tickLabelsVisibilitySettings[0] as Ast).chain[0].arguments @@ -265,7 +298,9 @@ describe('#toExpression', () => { }, ], }, - frame.datasourceLayers + frame.datasourceLayers, + undefined, + datasourceExpressionsByLayers ) as Ast; expect((expression.chain[0].arguments.labelsOrientation[0] as Ast).chain[0].arguments).toEqual({ x: [0], @@ -291,7 +326,9 @@ describe('#toExpression', () => { }, ], }, - frame.datasourceLayers + frame.datasourceLayers, + undefined, + datasourceExpressionsByLayers ) as Ast; expect( (expression.chain[0].arguments.gridlinesVisibilitySettings[0] as Ast).chain[0].arguments @@ -319,7 +356,9 @@ describe('#toExpression', () => { }, ], }, - frame.datasourceLayers + frame.datasourceLayers, + undefined, + datasourceExpressionsByLayers ) as Ast; expect(expression.chain[0].arguments.valueLabels[0] as Ast).toEqual('inside'); }); @@ -348,7 +387,9 @@ describe('#toExpression', () => { }, ], }, - { ...frame.datasourceLayers, referenceLine: mockDatasource.publicAPIMock } + { ...frame.datasourceLayers, referenceLine: mockDatasource.publicAPIMock }, + undefined, + datasourceExpressionsByLayers ) as Ast; function getYConfigColorForLayer(ast: Ast, index: number) { From fe16d49816daa44ec84e21b05a930cb9cd5683fc Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Thu, 24 Mar 2022 09:58:40 +0200 Subject: [PATCH 058/133] Fixed types. --- .../common/expression_functions/layered_xy_vis.ts | 15 ++++++++++++++- .../common/types/expression_functions.ts | 4 ++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts index efbd718cbc530..7b5373b368dad 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts @@ -27,6 +27,7 @@ import { EXTENDED_DATA_LAYER, EXTENDED_REFERENCE_LINE_LAYER, LAYERED_XY_VIS, + EndValues, } from '../constants'; const logDataTable = (tableAdapter: TablesAdapter, datatables: Record = {}) => { @@ -97,6 +98,18 @@ export const layeredXyVisFunction: ExpressionFunctionDefinition< defaultMessage: 'Define how missing values are treated', }), }, + endValue: { + types: ['string'], + options: [...Object.values(EndValues)], + help: i18n.translate('expressionXY.xyVis.endValue.help', { + defaultMessage: 'End value', + }), + }, + emphasizeFitting: { + types: ['boolean'], + default: false, + help: '', + }, valueLabels: { types: ['string'], options: [...Object.values(ValueLabelModes)], @@ -130,7 +143,7 @@ export const layeredXyVisFunction: ExpressionFunctionDefinition< }, layers: { types: [EXTENDED_DATA_LAYER, EXTENDED_REFERENCE_LINE_LAYER], - help: i18n.translate('expressionXY.xyVis.layers.help', { + help: i18n.translate('expressionXY.layeredXyVis.layers.help', { defaultMessage: 'Layers of visual series', }), multi: true, diff --git a/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts b/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts index 8b5753889be6f..9dd73dbf23c39 100644 --- a/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts +++ b/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts @@ -182,6 +182,8 @@ export interface XYArgs { yLeftExtent: AxisExtentConfigResult; yRightExtent: AxisExtentConfigResult; legend: LegendConfigResult; + endValue?: EndValue; + emphasizeFitting?: boolean; valueLabels: ValueLabelMode; dataLayer?: DataLayerConfigResult; referenceLineLayer?: ReferenceLineLayerConfigResult; @@ -206,6 +208,8 @@ export interface LayeredXYArgs { yLeftExtent: AxisExtentConfigResult; yRightExtent: AxisExtentConfigResult; legend: LegendConfigResult; + endValue?: EndValue; + emphasizeFitting?: boolean; valueLabels: ValueLabelMode; layers: XYExtendedLayerConfigResult[]; fittingFunction?: FittingFunction; From 21e34b4172f3876fef1e3da9e3f9fdc1dffae875 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Thu, 24 Mar 2022 21:34:37 +0200 Subject: [PATCH 059/133] First try to fix merge conflicts. --- .../expression_xy/common/constants.ts | 2 + .../annotation_layer_config.ts | 49 + .../common/expression_functions/index.ts | 1 + .../layer_config/annotation_layer_config.ts | 68 - .../expression_xy/common/index.ts | 6 + .../common/types/expression_functions.ts | 49 +- .../expression_xy/kibana.json | 2 +- .../public/components/annotations.tsx | 234 ++ .../components/reference_lines.test.tsx | 379 ++ .../public/components/reference_lines.tsx | 236 +- .../public/components/xy_chart.test.tsx | 384 +- .../public/components/xy_chart.tsx | 52 +- .../public/helpers/annotations.tsx | 336 ++ .../public/helpers/annotations_icon_set.tsx | 99 + .../public/helpers/color_assignment.ts | 3 +- .../expression_xy/public/helpers/index.ts | 2 + .../expression_xy/public/helpers/layers.ts | 38 +- .../public/helpers/reference_lines.ts | 2 +- .../public/helpers/visualization.ts | 46 +- .../expression_xy/public/icons/circle.tsx | 32 + .../expression_xy/public/icons/index.ts | 2 + .../expression_xy/public/icons/triangle.tsx | 31 + .../expression_xy/public/plugin.ts | 7 + .../expression_xy/server/plugin.ts | 2 + .../expression_xy/tsconfig.json | 1 + .../xy_chart/layer_config/index.ts | 17 - .../common/expressions/xy_chart/xy_args.ts | 45 - .../common/expressions/xy_chart/xy_chart.ts | 194 - .../annotations/config_panel/index.tsx | 2 +- .../annotations/expression.tsx | 2 +- .../xy_visualization/annotations/helpers.tsx | 2 +- .../xy_visualization/annotations_helpers.tsx | 6 +- .../xy_visualization/axes_configuration.ts | 6 +- .../xy_visualization/color_assignment.test.ts | 8 +- .../xy_visualization/color_assignment.ts | 7 +- .../xy_visualization/expression.test.tsx | 3167 ----------------- .../reference_line_helpers.tsx | 4 +- .../public/xy_visualization/state_helpers.ts | 6 +- .../xy_visualization/to_expression.test.ts | 2 +- .../public/xy_visualization/to_expression.ts | 14 +- .../lens/public/xy_visualization/types.ts | 28 +- .../xy_visualization/visualization.test.ts | 10 +- .../public/xy_visualization/visualization.tsx | 6 +- .../visualization_helpers.tsx | 21 +- .../axis_settings_popover.test.tsx | 4 - .../xy_config_panel/axis_settings_popover.tsx | 2 +- .../xy_config_panel/color_picker.tsx | 3 +- .../xy_config_panel/dimension_editor.tsx | 12 +- .../xy_config_panel/reference_line_panel.tsx | 3 +- .../visual_options_popover.test.tsx | 11 +- .../xy_config_panel/xy_config_panel.test.tsx | 10 +- .../xy_visualization/xy_suggestions.test.ts | 61 +- .../public/xy_visualization/xy_suggestions.ts | 8 +- 53 files changed, 1753 insertions(+), 3971 deletions(-) create mode 100644 src/plugins/chart_expressions/expression_xy/common/expression_functions/annotation_layer_config.ts delete mode 100644 src/plugins/chart_expressions/expression_xy/common/expression_functions/layer_config/annotation_layer_config.ts create mode 100644 src/plugins/chart_expressions/expression_xy/public/components/annotations.tsx create mode 100644 src/plugins/chart_expressions/expression_xy/public/components/reference_lines.test.tsx create mode 100644 src/plugins/chart_expressions/expression_xy/public/helpers/annotations.tsx create mode 100644 src/plugins/chart_expressions/expression_xy/public/helpers/annotations_icon_set.tsx create mode 100644 src/plugins/chart_expressions/expression_xy/public/icons/circle.tsx create mode 100644 src/plugins/chart_expressions/expression_xy/public/icons/triangle.tsx delete mode 100644 x-pack/plugins/lens/common/expressions/xy_chart/layer_config/index.ts delete mode 100644 x-pack/plugins/lens/common/expressions/xy_chart/xy_args.ts delete mode 100644 x-pack/plugins/lens/common/expressions/xy_chart/xy_chart.ts delete mode 100644 x-pack/plugins/lens/public/xy_visualization/expression.test.tsx diff --git a/src/plugins/chart_expressions/expression_xy/common/constants.ts b/src/plugins/chart_expressions/expression_xy/common/constants.ts index 3830f76543b58..bf1e43b205843 100644 --- a/src/plugins/chart_expressions/expression_xy/common/constants.ts +++ b/src/plugins/chart_expressions/expression_xy/common/constants.ts @@ -13,6 +13,7 @@ export const DATA_LAYER = 'dataLayer'; export const LEGEND_CONFIG = 'legendConfig'; export const XY_VIS_RENDERER = 'xyVis'; export const GRID_LINES_CONFIG = 'gridlinesConfig'; +export const ANNOTATION_LAYER = 'annotationLayer'; export const TICK_LABELS_CONFIG = 'tickLabelsConfig'; export const AXIS_EXTENT_CONFIG = 'axisExtentConfig'; export const REFERENCE_LINE_LAYER = 'referenceLineLayer'; @@ -22,6 +23,7 @@ export const AXIS_TITLES_VISIBILITY_CONFIG = 'axisTitlesVisibilityConfig'; export const LayerTypes = { DATA: 'data', REFERENCELINE: 'referenceLine', + ANNOTATIONS: 'annotations', } as const; export const FittingFunctions = { diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/annotation_layer_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/annotation_layer_config.ts new file mode 100644 index 0000000000000..0862b69ca44f2 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/annotation_layer_config.ts @@ -0,0 +1,49 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { ExpressionFunctionDefinition } from '../../../../expressions/common'; +import { LayerTypes, ANNOTATION_LAYER } from '../constants'; +import { AnnotationLayerArgs, AnnotationLayerConfigResult } from '../types'; + +export function annotationLayerConfigFunction(): ExpressionFunctionDefinition< + typeof ANNOTATION_LAYER, + null, + AnnotationLayerArgs, + AnnotationLayerConfigResult +> { + return { + name: ANNOTATION_LAYER, + aliases: [], + type: ANNOTATION_LAYER, + inputTypes: ['null'], + help: 'Annotation layer in lens', + args: { + layerId: { + types: ['string'], + help: '', + }, + hide: { + types: ['boolean'], + default: false, + help: 'Show details', + }, + annotations: { + types: ['manual_event_annotation'], + help: '', + multi: true, + }, + }, + fn: (input, args) => { + return { + type: ANNOTATION_LAYER, + ...args, + layerType: LayerTypes.ANNOTATIONS, + }; + }, + }; +} diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/index.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/index.ts index a7144eef13143..5c7e013a91332 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/index.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/index.ts @@ -8,6 +8,7 @@ export * from './xy_vis'; export * from './legend_config'; +export * from './annotation_layer_config'; export * from './y_axis_config'; export * from './data_layer_config'; export * from './grid_lines_config'; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/layer_config/annotation_layer_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/layer_config/annotation_layer_config.ts deleted file mode 100644 index ba2a0005a28cb..0000000000000 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/layer_config/annotation_layer_config.ts +++ /dev/null @@ -1,68 +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 { - EventAnnotationConfig, - EventAnnotationOutput, -} from '../../../../../../../src/plugins/event_annotation/common'; -import type { ExpressionFunctionDefinition } from '../../../../../../../src/plugins/expressions/common'; -import { layerTypes } from '../../../constants'; - -export interface XYAnnotationLayerConfig { - layerId: string; - layerType: typeof layerTypes.ANNOTATIONS; - annotations: EventAnnotationConfig[]; - hide?: boolean; -} - -export interface AnnotationLayerArgs { - annotations: EventAnnotationOutput[]; - layerId: string; - layerType: typeof layerTypes.ANNOTATIONS; - hide?: boolean; -} -export type XYAnnotationLayerArgsResult = AnnotationLayerArgs & { - type: 'lens_xy_annotation_layer'; -}; -export function annotationLayerConfig(): ExpressionFunctionDefinition< - 'lens_xy_annotation_layer', - null, - AnnotationLayerArgs, - XYAnnotationLayerArgsResult -> { - return { - name: 'lens_xy_annotation_layer', - aliases: [], - type: 'lens_xy_annotation_layer', - inputTypes: ['null'], - help: 'Annotation layer in lens', - args: { - layerId: { - types: ['string'], - help: '', - }, - layerType: { types: ['string'], options: [layerTypes.ANNOTATIONS], help: '' }, - hide: { - types: ['boolean'], - default: false, - help: 'Show details', - }, - annotations: { - types: ['manual_event_annotation'], - help: '', - multi: true, - }, - }, - fn: (input, args) => { - return { - type: 'lens_xy_annotation_layer', - ...args, - }; - }, - }; -} diff --git a/src/plugins/chart_expressions/expression_xy/common/index.ts b/src/plugins/chart_expressions/expression_xy/common/index.ts index 7d26a8f689107..bfb2b8f0eddb2 100755 --- a/src/plugins/chart_expressions/expression_xy/common/index.ts +++ b/src/plugins/chart_expressions/expression_xy/common/index.ts @@ -17,6 +17,7 @@ export { dataLayerConfigFunction, axisExtentConfigFunction, tickLabelsConfigFunction, + annotationLayerConfigFunction, labelsOrientationConfigFunction, referenceLineLayerConfigFunction, axisTitlesVisibilityConfigFunction, @@ -36,12 +37,14 @@ export type { XScaleType, AxisConfig, ValidLayer, + XYLayerArgs, XYCurveType, XYChartProps, LegendConfig, IconPosition, YConfigResult, DataLayerArgs, + XYLayerConfig, LensMultiTable, ValueLabelMode, AxisExtentMode, @@ -50,14 +53,17 @@ export type { XYDataLayerConfig, LegendConfigResult, AxesSettingsConfig, + AnnotationLayerArgs, XYLayerConfigResult, GridlinesConfigResult, DataLayerConfigResult, TickLabelsConfigResult, AxisExtentConfigResult, ReferenceLineLayerArgs, + XYAnnotationLayerConfig, LabelsOrientationConfig, XYReferenceLineLayerConfig, + AnnotationLayerConfigResult, LabelsOrientationConfigResult, ReferenceLineLayerConfigResult, AxisTitlesVisibilityConfigResult, diff --git a/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts b/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts index aaf5155eeb0ba..8646ffcf74a9c 100644 --- a/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts +++ b/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts @@ -10,6 +10,7 @@ import { HorizontalAlignment, Position, VerticalAlignment } from '@elastic/chart import { $Values } from '@kbn/utility-types'; import { Datatable } from '../../../../expressions'; import { PaletteOutput } from '../../../../charts/common'; +import { EventAnnotationConfig, EventAnnotationOutput } from '../../../../event_annotation/common'; import { AxisExtentModes, FillStyles, @@ -33,6 +34,7 @@ import { LEGEND_CONFIG, DATA_LAYER, AXIS_EXTENT_CONFIG, + ANNOTATION_LAYER, EndValues, } from '../constants'; @@ -82,18 +84,20 @@ export interface YConfig { export interface XYDataLayerConfig { layerId: string; accessors: string[]; + layerType: typeof LayerTypes.DATA; seriesType: SeriesType; xAccessor?: string; hide?: boolean; - yConfig?: YConfigResult[]; + yConfig?: YConfig[]; splitAccessor?: string; palette?: PaletteOutput; } + export interface ValidLayer extends DataLayerConfigResult { xAccessor: NonNullable; } -export type DataLayerArgs = XYDataLayerConfig & { +export type DataLayerArgs = Omit & { columnToLabel?: string; // Actually a JSON key-value pair yScaleType: YScaleType; xScaleType: XScaleType; @@ -180,17 +184,46 @@ export interface XYArgs { ariaLabel?: string; } +export interface XYAnnotationLayerConfig { + layerId: string; + layerType: typeof LayerTypes.ANNOTATIONS; + annotations: EventAnnotationConfig[]; + hide?: boolean; +} + +export interface AnnotationLayerArgs { + annotations: EventAnnotationOutput[]; + layerId: string; + hide?: boolean; +} + +export type AnnotationLayerConfigResult = AnnotationLayerArgs & { + type: typeof ANNOTATION_LAYER; + layerType: typeof LayerTypes.ANNOTATIONS; +}; + export interface XYReferenceLineLayerConfig { layerId: string; accessors: string[]; - yConfig?: YConfigResult[]; + yConfig?: YConfig[]; + layerType: typeof LayerTypes.REFERENCELINE; } -export type ReferenceLineLayerArgs = XYReferenceLineLayerConfig & { +export type ReferenceLineLayerArgs = Omit & { columnToLabel?: string; }; -export type XYLayerConfigResult = DataLayerConfigResult | ReferenceLineLayerConfigResult; +export type XYLayerArgs = DataLayerArgs | ReferenceLineLayerArgs | AnnotationLayerArgs; + +export type XYLayerConfig = + | XYDataLayerConfig + | XYReferenceLineLayerConfig + | XYAnnotationLayerConfig; + +export type XYLayerConfigResult = + | DataLayerConfigResult + | ReferenceLineLayerConfigResult + | AnnotationLayerConfigResult; export interface LensMultiTable { type: typeof MULTITABLE; @@ -201,14 +234,16 @@ export interface LensMultiTable { }; } -export type ReferenceLineLayerConfigResult = ReferenceLineLayerArgs & { +export type ReferenceLineLayerConfigResult = Omit & { type: typeof REFERENCE_LINE_LAYER; layerType: typeof LayerTypes.REFERENCELINE; + yConfig?: YConfigResult[]; }; -export type DataLayerConfigResult = DataLayerArgs & { +export type DataLayerConfigResult = Omit & { type: typeof DATA_LAYER; layerType: typeof LayerTypes.DATA; + yConfig?: YConfigResult[]; }; export type YConfigResult = YConfig & { type: typeof Y_CONFIG }; diff --git a/src/plugins/chart_expressions/expression_xy/kibana.json b/src/plugins/chart_expressions/expression_xy/kibana.json index 7f24173b071b1..bfdec4c88bbe0 100755 --- a/src/plugins/chart_expressions/expression_xy/kibana.json +++ b/src/plugins/chart_expressions/expression_xy/kibana.json @@ -9,7 +9,7 @@ "description": "Expression XY plugin adds a `xy` renderer and function to the expression plugin. The renderer will display the `xy` chart.", "server": true, "ui": true, - "requiredPlugins": ["expressions", "charts", "data", "fieldFormats", "uiActions"], + "requiredPlugins": ["expressions", "charts", "data", "fieldFormats", "uiActions", "eventAnnotation"], "requiredBundles": ["kibanaReact"], "optionalPlugins": [] } diff --git a/src/plugins/chart_expressions/expression_xy/public/components/annotations.tsx b/src/plugins/chart_expressions/expression_xy/public/components/annotations.tsx new file mode 100644 index 0000000000000..032f4f7ce7ed4 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/public/components/annotations.tsx @@ -0,0 +1,234 @@ +/* + * 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 './expression.scss'; +import React from 'react'; +import { snakeCase } from 'lodash'; +import { + AnnotationDomainType, + AnnotationTooltipFormatter, + LineAnnotation, + Position, +} from '@elastic/charts'; +import moment from 'moment'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import type { EventAnnotationArgs } from '../../../../event_annotation/common'; +import type { FieldFormat } from '../../../../field_formats/common'; +import { defaultAnnotationColor } from '../../../../../../src/plugins/event_annotation/public'; +import type { AnnotationLayerArgs, AnnotationLayerConfigResult } from '../../common/types'; +import { hasIcon } from '../helpers'; +import { + mapVerticalToHorizontalPlacement, + LINES_MARKER_SIZE, + MarkerBody, + Marker, + AnnotationIcon, +} from '../helpers'; + +const getRoundedTimestamp = (timestamp: number, firstTimestamp?: number, minInterval?: number) => { + if (!firstTimestamp || !minInterval) { + return timestamp; + } + return timestamp - ((timestamp - firstTimestamp) % minInterval); +}; + +export interface AnnotationsProps { + groupedAnnotations: CollectiveConfig[]; + formatter?: FieldFormat; + isHorizontal: boolean; + paddingMap: Partial>; + hide?: boolean; + minInterval?: number; + isBarChart?: boolean; +} + +interface CollectiveConfig extends EventAnnotationArgs { + roundedTimestamp: number; + axisMode: 'bottom'; + customTooltipDetails?: AnnotationTooltipFormatter | undefined; +} + +const groupVisibleConfigsByInterval = ( + layers: AnnotationLayerArgs[], + minInterval?: number, + firstTimestamp?: number +) => { + return layers + .flatMap(({ annotations }) => annotations.filter((a) => !a.isHidden)) + .reduce>((acc, current) => { + const roundedTimestamp = getRoundedTimestamp( + moment(current.time).valueOf(), + firstTimestamp, + minInterval + ); + return { + ...acc, + [roundedTimestamp]: acc[roundedTimestamp] ? [...acc[roundedTimestamp], current] : [current], + }; + }, {}); +}; + +const createCustomTooltipDetails = + ( + config: EventAnnotationArgs[], + formatter?: FieldFormat + ): AnnotationTooltipFormatter | undefined => + () => { + return ( +
+ {config.map(({ icon, label, time, color }) => ( +
+ + {hasIcon(icon) && ( + + + + )} + {label} + + {formatter?.convert(time) || String(time)} +
+ ))} +
+ ); + }; + +function getCommonProperty( + configArr: EventAnnotationArgs[], + propertyName: K, + fallbackValue: T +) { + const firstStyle = configArr[0][propertyName]; + if (configArr.every((config) => firstStyle === config[propertyName])) { + return firstStyle; + } + return fallbackValue; +} + +const getCommonStyles = (configArr: EventAnnotationArgs[]) => { + return { + color: getCommonProperty( + configArr, + 'color', + defaultAnnotationColor + ), + lineWidth: getCommonProperty(configArr, 'lineWidth', 1), + lineStyle: getCommonProperty(configArr, 'lineStyle', 'solid'), + textVisibility: getCommonProperty(configArr, 'textVisibility', false), + }; +}; + +export const getAnnotationsGroupedByInterval = ( + layers: AnnotationLayerConfigResult[], + minInterval?: number, + firstTimestamp?: number, + formatter?: FieldFormat +) => { + const visibleGroupedConfigs = groupVisibleConfigsByInterval(layers, minInterval, firstTimestamp); + let collectiveConfig: CollectiveConfig; + return Object.entries(visibleGroupedConfigs).map(([roundedTimestamp, configArr]) => { + collectiveConfig = { + ...configArr[0], + roundedTimestamp: Number(roundedTimestamp), + axisMode: 'bottom', + }; + if (configArr.length > 1) { + const commonStyles = getCommonStyles(configArr); + collectiveConfig = { + ...collectiveConfig, + ...commonStyles, + icon: String(configArr.length), + customTooltipDetails: createCustomTooltipDetails(configArr, formatter), + }; + } + return collectiveConfig; + }); +}; + +export const Annotations = ({ + groupedAnnotations, + formatter, + isHorizontal, + paddingMap, + hide, + minInterval, + isBarChart, +}: AnnotationsProps) => { + return ( + <> + {groupedAnnotations.map((annotation) => { + const markerPositionVertical = Position.Top; + const markerPosition = isHorizontal + ? mapVerticalToHorizontalPlacement(markerPositionVertical) + : markerPositionVertical; + const hasReducedPadding = paddingMap[markerPositionVertical] === LINES_MARKER_SIZE; + const id = snakeCase(annotation.label); + const { roundedTimestamp, time: exactTimestamp } = annotation; + const isGrouped = Boolean(annotation.customTooltipDetails); + const header = + formatter?.convert(isGrouped ? roundedTimestamp : exactTimestamp) || + moment(isGrouped ? roundedTimestamp : exactTimestamp).toISOString(); + const strokeWidth = annotation.lineWidth || 1; + return ( + + ) : undefined + } + markerBody={ + !hide ? ( + + ) : undefined + } + markerPosition={markerPosition} + dataValues={[ + { + dataValue: moment( + isBarChart && minInterval ? roundedTimestamp + minInterval / 2 : roundedTimestamp + ).valueOf(), + header, + details: annotation.label, + }, + ]} + customTooltipDetails={annotation.customTooltipDetails} + style={{ + line: { + strokeWidth, + stroke: annotation.color || defaultAnnotationColor, + dash: + annotation.lineStyle === 'dashed' + ? [strokeWidth * 3, strokeWidth] + : annotation.lineStyle === 'dotted' + ? [strokeWidth, strokeWidth] + : undefined, + opacity: 1, + }, + }} + /> + ); + })} + + ); +}; diff --git a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.test.tsx b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.test.tsx new file mode 100644 index 0000000000000..c53ee9a5cc55b --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.test.tsx @@ -0,0 +1,379 @@ +/* + * 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 { LineAnnotation, RectAnnotation } from '@elastic/charts'; +import { shallow } from 'enzyme'; +import React from 'react'; +import { chartPluginMock } from '../../../../charts/public/mocks'; +import { FieldFormat } from '../../../../field_formats/common'; +import { LensMultiTable } from '../../common'; +import { ReferenceLineLayerArgs, YConfig } from '../../common/types'; +import { ReferenceLineAnnotations, ReferenceLineAnnotationsProps } from './reference_lines'; + +const paletteService = chartPluginMock.createPaletteRegistry(); + +const row: Record = { + xAccessorFirstId: 1, + xAccessorSecondId: 2, + yAccessorLeftFirstId: 5, + yAccessorLeftSecondId: 10, + yAccessorRightFirstId: 5, + yAccessorRightSecondId: 10, +}; + +const histogramData: LensMultiTable = { + type: 'lens_multitable', + tables: { + firstLayer: { + type: 'datatable', + rows: [row], + columns: Object.keys(row).map((id) => ({ + id, + name: `Static value: ${row[id]}`, + meta: { + type: 'number', + params: { id: 'number' }, + }, + })), + }, + }, + dateRange: { + fromDate: new Date('2020-04-01T16:14:16.246Z'), + toDate: new Date('2020-04-01T17:15:41.263Z'), + }, +}; + +function createLayers(yConfigs: ReferenceLineLayerArgs['yConfig']): ReferenceLineLayerArgs[] { + return [ + { + layerId: 'firstLayer', + accessors: (yConfigs || []).map(({ forAccessor }) => forAccessor), + yConfig: yConfigs, + }, + ]; +} + +interface YCoords { + y0: number | undefined; + y1: number | undefined; +} +interface XCoords { + x0: number | undefined; + x1: number | undefined; +} + +function getAxisFromId(layerPrefix: string): YConfig['axisMode'] { + return /left/i.test(layerPrefix) ? 'left' : /right/i.test(layerPrefix) ? 'right' : 'bottom'; +} + +const emptyCoords = { x0: undefined, x1: undefined, y0: undefined, y1: undefined }; + +describe('ReferenceLineAnnotations', () => { + describe('with fill', () => { + let formatters: Record<'left' | 'right' | 'bottom', FieldFormat | undefined>; + let defaultProps: Omit; + + beforeEach(() => { + formatters = { + left: { convert: jest.fn((x) => x) } as unknown as FieldFormat, + right: { convert: jest.fn((x) => x) } as unknown as FieldFormat, + bottom: { convert: jest.fn((x) => x) } as unknown as FieldFormat, + }; + + defaultProps = { + formatters, + isHorizontal: false, + axesMap: { left: true, right: false }, + paddingMap: {}, + }; + }); + + it.each([ + ['yAccessorLeft', 'above'], + ['yAccessorLeft', 'below'], + ['yAccessorRight', 'above'], + ['yAccessorRight', 'below'], + ] as Array<[string, YConfig['fill']]>)( + 'should render a RectAnnotation for a reference line with fill set: %s %s', + (layerPrefix, fill) => { + const axisMode = getAxisFromId(layerPrefix); + const wrapper = shallow( + + ); + + const y0 = fill === 'above' ? 5 : undefined; + const y1 = fill === 'above' ? undefined : 5; + + expect(wrapper.find(LineAnnotation).exists()).toBe(true); + expect(wrapper.find(RectAnnotation).exists()).toBe(true); + expect(wrapper.find(RectAnnotation).prop('dataValues')).toEqual( + expect.arrayContaining([ + { + coordinates: { x0: undefined, x1: undefined, y0, y1 }, + details: y0 ?? y1, + header: undefined, + }, + ]) + ); + } + ); + + it.each([ + ['xAccessor', 'above'], + ['xAccessor', 'below'], + ] as Array<[string, YConfig['fill']]>)( + 'should render a RectAnnotation for a reference line with fill set: %s %s', + (layerPrefix, fill) => { + const wrapper = shallow( + + ); + + const x0 = fill === 'above' ? 1 : undefined; + const x1 = fill === 'above' ? undefined : 1; + + expect(wrapper.find(LineAnnotation).exists()).toBe(true); + expect(wrapper.find(RectAnnotation).exists()).toBe(true); + expect(wrapper.find(RectAnnotation).prop('dataValues')).toEqual( + expect.arrayContaining([ + { + coordinates: { ...emptyCoords, x0, x1 }, + details: x0 ?? x1, + header: undefined, + }, + ]) + ); + } + ); + + it.each([ + ['yAccessorLeft', 'above', { y0: 5, y1: 10 }, { y0: 10, y1: undefined }], + ['yAccessorLeft', 'below', { y0: undefined, y1: 5 }, { y0: 5, y1: 10 }], + ['yAccessorRight', 'above', { y0: 5, y1: 10 }, { y0: 10, y1: undefined }], + ['yAccessorRight', 'below', { y0: undefined, y1: 5 }, { y0: 5, y1: 10 }], + ] as Array<[string, YConfig['fill'], YCoords, YCoords]>)( + 'should avoid overlap between two reference lines with fill in the same direction: 2 x %s %s', + (layerPrefix, fill, coordsA, coordsB) => { + const axisMode = getAxisFromId(layerPrefix); + const wrapper = shallow( + + ); + + expect(wrapper.find(RectAnnotation).first().prop('dataValues')).toEqual( + expect.arrayContaining([ + { + coordinates: { ...emptyCoords, ...coordsA }, + details: coordsA.y0 ?? coordsA.y1, + header: undefined, + }, + ]) + ); + expect(wrapper.find(RectAnnotation).last().prop('dataValues')).toEqual( + expect.arrayContaining([ + { + coordinates: { ...emptyCoords, ...coordsB }, + details: coordsB.y1 ?? coordsB.y0, + header: undefined, + }, + ]) + ); + } + ); + + it.each([ + ['xAccessor', 'above', { x0: 1, x1: 2 }, { x0: 2, x1: undefined }], + ['xAccessor', 'below', { x0: undefined, x1: 1 }, { x0: 1, x1: 2 }], + ] as Array<[string, YConfig['fill'], XCoords, XCoords]>)( + 'should avoid overlap between two reference lines with fill in the same direction: 2 x %s %s', + (layerPrefix, fill, coordsA, coordsB) => { + const wrapper = shallow( + + ); + + expect(wrapper.find(RectAnnotation).first().prop('dataValues')).toEqual( + expect.arrayContaining([ + { + coordinates: { ...emptyCoords, ...coordsA }, + details: coordsA.x0 ?? coordsA.x1, + header: undefined, + }, + ]) + ); + expect(wrapper.find(RectAnnotation).last().prop('dataValues')).toEqual( + expect.arrayContaining([ + { + coordinates: { ...emptyCoords, ...coordsB }, + details: coordsB.x1 ?? coordsB.x0, + header: undefined, + }, + ]) + ); + } + ); + + it.each(['yAccessorLeft', 'yAccessorRight', 'xAccessor'])( + 'should let areas in different directions overlap: %s', + (layerPrefix) => { + const axisMode = getAxisFromId(layerPrefix); + + const wrapper = shallow( + + ); + + expect(wrapper.find(RectAnnotation).first().prop('dataValues')).toEqual( + expect.arrayContaining([ + { + coordinates: { ...emptyCoords, ...(axisMode === 'bottom' ? { x0: 1 } : { y0: 5 }) }, + details: axisMode === 'bottom' ? 1 : 5, + header: undefined, + }, + ]) + ); + expect(wrapper.find(RectAnnotation).last().prop('dataValues')).toEqual( + expect.arrayContaining([ + { + coordinates: { ...emptyCoords, ...(axisMode === 'bottom' ? { x1: 2 } : { y1: 10 }) }, + details: axisMode === 'bottom' ? 2 : 10, + header: undefined, + }, + ]) + ); + } + ); + + it.each([ + ['above', { y0: 5, y1: 10 }, { y0: 10, y1: undefined }], + ['below', { y0: undefined, y1: 5 }, { y0: 5, y1: 10 }], + ] as Array<[YConfig['fill'], YCoords, YCoords]>)( + 'should be robust and works also for different axes when on same direction: 1x Left + 1x Right both %s', + (fill, coordsA, coordsB) => { + const wrapper = shallow( + + ); + + expect(wrapper.find(RectAnnotation).first().prop('dataValues')).toEqual( + expect.arrayContaining([ + { + coordinates: { ...emptyCoords, ...coordsA }, + details: coordsA.y0 ?? coordsA.y1, + header: undefined, + }, + ]) + ); + expect(wrapper.find(RectAnnotation).last().prop('dataValues')).toEqual( + expect.arrayContaining([ + { + coordinates: { ...emptyCoords, ...coordsB }, + details: coordsB.y1 ?? coordsB.y0, + header: undefined, + }, + ]) + ); + } + ); + }); +}); diff --git a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.tsx b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.tsx index 0ca7db39e0c99..099df5f91c5bc 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.tsx @@ -6,26 +6,190 @@ * Side Public License, v 1. */ -import './reference_lines.scss'; +import './expression_reference_lines.scss'; import React from 'react'; import { groupBy } from 'lodash'; +import { EuiIcon } from '@elastic/eui'; import { RectAnnotation, AnnotationDomainType, LineAnnotation, Position } from '@elastic/charts'; -import type { PaletteRegistry } from 'src/plugins/charts/public'; -import type { FieldFormat } from 'src/plugins/field_formats/common'; -import type { ReferenceLineLayerArgs } from '../../common/expressions'; +import { euiLightVars } from '@kbn/ui-theme'; +import type { FieldFormat } from '../../../../field_formats/common'; +import type { PaletteRegistry } from '../../../../charts/public'; +import type { IconPosition, ReferenceLineLayerArgs, YAxisMode } from '../../common/types'; import type { LensMultiTable } from '../../common/types'; -import { defaultReferenceLineColor } from './color_assignment'; -import { - MarkerBody, - Marker, - LINES_MARKER_SIZE, - mapVerticalToHorizontalPlacement, - getBaseIconPlacement, -} from './annotations_helpers'; +import { hasIcon } from '../helpers'; + +export const REFERENCE_LINE_MARKER_SIZE = 20; + +export const computeChartMargins = ( + referenceLinePaddings: Partial>, + labelVisibility: Partial>, + titleVisibility: Partial>, + axesMap: Record<'left' | 'right', unknown>, + isHorizontal: boolean +) => { + const result: Partial> = {}; + if (!labelVisibility?.x && !titleVisibility?.x && referenceLinePaddings.bottom) { + const placement = isHorizontal ? mapVerticalToHorizontalPlacement('bottom') : 'bottom'; + result[placement] = referenceLinePaddings.bottom; + } + if ( + referenceLinePaddings.left && + (isHorizontal || (!labelVisibility?.yLeft && !titleVisibility?.yLeft)) + ) { + const placement = isHorizontal ? mapVerticalToHorizontalPlacement('left') : 'left'; + result[placement] = referenceLinePaddings.left; + } + if ( + referenceLinePaddings.right && + (isHorizontal || !axesMap.right || (!labelVisibility?.yRight && !titleVisibility?.yRight)) + ) { + const placement = isHorizontal ? mapVerticalToHorizontalPlacement('right') : 'right'; + result[placement] = referenceLinePaddings.right; + } + // there's no top axis, so just check if a margin has been computed + if (referenceLinePaddings.top) { + const placement = isHorizontal ? mapVerticalToHorizontalPlacement('top') : 'top'; + result[placement] = referenceLinePaddings.top; + } + return result; +}; + +// Note: it does not take into consideration whether the reference line is in view or not +export const getReferenceLineRequiredPaddings = ( + referenceLineLayers: ReferenceLineLayerArgs[], + axesMap: Record<'left' | 'right', unknown> +) => { + // collect all paddings for the 4 axis: if any text is detected double it. + const paddings: Partial> = {}; + const icons: Partial> = {}; + referenceLineLayers.forEach((layer) => { + layer.yConfig?.forEach(({ axisMode, icon, iconPosition, textVisibility }) => { + if (axisMode && (hasIcon(icon) || textVisibility)) { + const placement = getBaseIconPlacement(iconPosition, axisMode, axesMap); + paddings[placement] = Math.max( + paddings[placement] || 0, + REFERENCE_LINE_MARKER_SIZE * (textVisibility ? 2 : 1) // double the padding size if there's text + ); + icons[placement] = (icons[placement] || 0) + (hasIcon(icon) ? 1 : 0); + } + }); + }); + // post-process the padding based on the icon presence: + // if no icon is present for the placement, just reduce the padding + (Object.keys(paddings) as Position[]).forEach((placement) => { + if (!icons[placement]) { + paddings[placement] = REFERENCE_LINE_MARKER_SIZE; + } + }); + + return paddings; +}; + +function mapVerticalToHorizontalPlacement(placement: Position) { + switch (placement) { + case Position.Top: + return Position.Right; + case Position.Bottom: + return Position.Left; + case Position.Left: + return Position.Bottom; + case Position.Right: + return Position.Top; + } +} + +// if there's just one axis, put it on the other one +// otherwise use the same axis +// this function assume the chart is vertical +function getBaseIconPlacement( + iconPosition: IconPosition | undefined, + axisMode: YAxisMode | undefined, + axesMap: Record +) { + if (iconPosition === 'auto') { + if (axisMode === 'bottom') { + return Position.Top; + } + if (axisMode === 'left') { + return axesMap.right ? Position.Left : Position.Right; + } + return axesMap.left ? Position.Right : Position.Left; + } + + if (iconPosition === 'left') { + return Position.Left; + } + if (iconPosition === 'right') { + return Position.Right; + } + if (iconPosition === 'below') { + return Position.Bottom; + } + return Position.Top; +} + +function getMarkerBody(label: string | undefined, isHorizontal: boolean) { + if (!label) { + return; + } + if (isHorizontal) { + return ( +
+ {label} +
+ ); + } + return ( +
+
+ {label} +
+
+ ); +} + +interface MarkerConfig { + axisMode?: YAxisMode; + icon?: string; + textVisibility?: boolean; +} + +function getMarkerToShow( + markerConfig: MarkerConfig, + label: string | undefined, + isHorizontal: boolean, + hasReducedPadding: boolean +) { + // show an icon if present + if (hasIcon(markerConfig.icon)) { + return ; + } + // if there's some text, check whether to show it as marker, or just show some padding for the icon + if (markerConfig.textVisibility) { + if (hasReducedPadding) { + return getMarkerBody( + label, + (!isHorizontal && markerConfig.axisMode === 'bottom') || + (isHorizontal && markerConfig.axisMode !== 'bottom') + ); + } + return ; + } +} export interface ReferenceLineAnnotationsProps { - layers: ReferenceLineLayerConfigResult[]; + layers: ReferenceLineLayerArgs[]; data: LensMultiTable; formatters: Record<'left' | 'right' | 'bottom', FieldFormat | undefined>; axesMap: Record<'left' | 'right', boolean>; @@ -75,40 +239,32 @@ export const ReferenceLineAnnotations = ({ const formatter = formatters[groupId || 'bottom']; + const defaultColor = euiLightVars.euiColorDarkShade; + // get the position for vertical chart const markerPositionVertical = getBaseIconPlacement( yConfig.iconPosition, - axesMap, - yConfig.axisMode + yConfig.axisMode, + axesMap ); // the padding map is built for vertical chart - const hasReducedPadding = paddingMap[markerPositionVertical] === LINES_MARKER_SIZE; + const hasReducedPadding = + paddingMap[markerPositionVertical] === REFERENCE_LINE_MARKER_SIZE; const props = { groupId, - marker: ( - + marker: getMarkerToShow( + yConfig, + columnToLabelMap[yConfig.forAccessor], + isHorizontal, + hasReducedPadding ), - markerBody: ( - + markerBody: getMarkerBody( + yConfig.textVisibility && !hasReducedPadding + ? columnToLabelMap[yConfig.forAccessor] + : undefined, + (!isHorizontal && yConfig.axisMode === 'bottom') || + (isHorizontal && yConfig.axisMode !== 'bottom') ), // rotate the position if required markerPosition: isHorizontal @@ -126,7 +282,7 @@ export const ReferenceLineAnnotations = ({ const sharedStyle = { strokeWidth: yConfig.lineWidth || 1, - stroke: yConfig.color || defaultReferenceLineColor, + stroke: yConfig.color || defaultColor, dash: dashStyle, }; @@ -197,7 +353,7 @@ export const ReferenceLineAnnotations = ({ })} style={{ ...sharedStyle, - fill: yConfig.color || defaultReferenceLineColor, + fill: yConfig.color || defaultColor, opacity: 0.1, }} /> diff --git a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx index e99a85b33c1f0..1640de3b2deda 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx @@ -7,9 +7,14 @@ */ import React from 'react'; -import { shallow } from 'enzyme'; +import { mount, shallow } from 'enzyme'; import { mountWithIntl } from '@kbn/test-jest-helpers'; -import { DataLayerConfigResult, LensMultiTable, XYArgs } from '../../common'; +import { + AnnotationLayerConfigResult, + DataLayerConfigResult, + LensMultiTable, + XYArgs, +} from '../../common'; import { LayerTypes } from '../../common/constants'; import { AreaSeries, @@ -19,6 +24,7 @@ import { GeometryValue, HorizontalAlignment, LayoutDirection, + LineAnnotation, LineSeries, Position, ScaleType, @@ -45,6 +51,8 @@ import { sampleLayer, } from '../../common/__mocks__'; import { XYChart, XYChartRenderProps } from './xy_chart'; +import { eventAnnotationServiceMock } from '../../../../event_annotation/public/mocks'; +import { EventAnnotationOutput } from '../../../../event_annotation/common'; const onClickValue = jest.fn(); const onSelectRange = jest.fn(); @@ -112,6 +120,7 @@ describe('XYChart component', () => { onSelectRange, syncColors: false, useLegacyTimeAxis: false, + eventAnnotationService: eventAnnotationServiceMock, }; }); @@ -124,13 +133,7 @@ describe('XYChart component', () => { data={data} args={{ ...args, - layers: [ - { - ...args.layers[0], - seriesType: 'line', - type: 'dataLayer', - } as DataLayerConfigResult, - ], + layers: [{ ...(args.layers[0] as DataLayerConfigResult), seriesType: 'line' }], }} /> ); @@ -142,8 +145,8 @@ describe('XYChart component', () => { describe('date range', () => { const timeSampleLayer: DataLayerConfigResult = { - type: 'dataLayer', layerId: 'first', + type: 'dataLayer', layerType: LayerTypes.DATA, seriesType: 'line', xAccessor: 'c', @@ -201,11 +204,10 @@ describe('XYChart component', () => { ...args, layers: [ { - ...args.layers[0], + ...(args.layers[0] as DataLayerConfigResult), seriesType: 'line', xScaleType: 'time', - type: 'dataLayer', - } as DataLayerConfigResult, + }, ], }} minInterval={undefined} @@ -243,8 +245,8 @@ describe('XYChart component', () => { describe('axis time', () => { const defaultTimeLayer: DataLayerConfigResult = { - type: 'dataLayer', layerId: 'first', + type: 'dataLayer', layerType: LayerTypes.DATA, seriesType: 'line', xAccessor: 'c', @@ -396,13 +398,12 @@ describe('XYChart component', () => { ...args, layers: [ { - ...args.layers[0], - type: 'dataLayer', + ...(args.layers[0] as DataLayerConfigResult), seriesType: 'line', xScaleType: 'time', isHistogram: true, splitAccessor: undefined, - } as DataLayerConfigResult, + }, ], }; @@ -473,14 +474,10 @@ describe('XYChart component', () => { ...args, layers: [ { - ...args.layers[0], - type: 'dataLayer', - layerType: 'data', + ...(args.layers[0] as DataLayerConfigResult), seriesType: 'bar', xScaleType: 'time', - yScaleType: 'linear', isHistogram: true, - palette: { type: 'palette', name: 'default' }, }, ], }} @@ -574,14 +571,8 @@ describe('XYChart component', () => { }, layers: [ { - ...args.layers[0], - layerType: 'data', + ...(args.layers[0] as DataLayerConfigResult), seriesType: 'area', - type: 'dataLayer', - xScaleType: 'linear', - yScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, ], }} @@ -611,14 +602,8 @@ describe('XYChart component', () => { }, layers: [ { - ...args.layers[0], + ...(args.layers[0] as DataLayerConfigResult), seriesType: 'bar', - type: 'dataLayer', - layerType: 'data', - xScaleType: 'linear', - yScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, ], }} @@ -696,14 +681,9 @@ describe('XYChart component', () => { ...args, layers: [ { - ...args.layers[0], + ...(args.layers[0] as DataLayerConfigResult), seriesType: 'line', xScaleType: 'linear', - type: 'dataLayer', - layerType: 'data', - yScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, ], }} @@ -725,14 +705,10 @@ describe('XYChart component', () => { ...args, layers: [ { - ...args.layers[0], + ...(args.layers[0] as DataLayerConfigResult), seriesType: 'line', xScaleType: 'linear', isHistogram: true, - type: 'dataLayer', - layerType: 'data', - yScaleType: 'linear', - palette: { type: 'palette', name: 'default' }, }, ], }} @@ -783,18 +759,7 @@ describe('XYChart component', () => { data={data} args={{ ...args, - layers: [ - { - ...args.layers[0], - seriesType: 'bar', - type: 'dataLayer', - layerType: 'data', - xScaleType: 'linear', - yScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, - }, - ], + layers: [{ ...(args.layers[0] as DataLayerConfigResult), seriesType: 'bar' }], }} /> ); @@ -812,18 +777,7 @@ describe('XYChart component', () => { data={data} args={{ ...args, - layers: [ - { - ...args.layers[0], - seriesType: 'area', - type: 'dataLayer', - layerType: 'data', - xScaleType: 'linear', - yScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, - }, - ], + layers: [{ ...(args.layers[0] as DataLayerConfigResult), seriesType: 'area' }], }} /> ); @@ -841,18 +795,7 @@ describe('XYChart component', () => { data={data} args={{ ...args, - layers: [ - { - ...args.layers[0], - seriesType: 'bar_horizontal', - type: 'dataLayer', - layerType: 'data', - xScaleType: 'linear', - yScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, - }, - ], + layers: [{ ...(args.layers[0] as DataLayerConfigResult), seriesType: 'bar_horizontal' }], }} /> ); @@ -1112,8 +1055,8 @@ describe('XYChart component', () => { const { args } = sampleArgs(); const numberLayer: DataLayerConfigResult = { - type: 'dataLayer', layerId: 'numberLayer', + type: 'dataLayer', layerType: LayerTypes.DATA, hide: false, xAccessor: 'xAccessorId', @@ -1281,8 +1224,8 @@ describe('XYChart component', () => { layers: [ { layerId: 'first', - type: 'dataLayer', layerType: LayerTypes.DATA, + type: 'dataLayer', seriesType: 'line', xAccessor: 'd', accessors: ['a', 'b'], @@ -1312,9 +1255,9 @@ describe('XYChart component', () => { layers: [ { layerId: 'first', - type: 'dataLayer', layerType: LayerTypes.DATA, seriesType: 'line', + type: 'dataLayer', xAccessor: 'd', accessors: ['a', 'b'], columnToLabel: '{"a": "Label A", "b": "Label B", "d": "Label D"}', @@ -1359,18 +1302,7 @@ describe('XYChart component', () => { data={data} args={{ ...args, - layers: [ - { - ...args.layers[0], - seriesType: 'bar_stacked', - type: 'dataLayer', - layerType: LayerTypes.DATA, - xScaleType: 'ordinal', - yScaleType: 'linear', - isHistogram: false, - palette: mockPaletteOutput, - }, - ], + layers: [{ ...(args.layers[0] as DataLayerConfigResult), seriesType: 'bar_stacked' }], }} /> ); @@ -1388,18 +1320,7 @@ describe('XYChart component', () => { data={data} args={{ ...args, - layers: [ - { - ...args.layers[0], - seriesType: 'area_stacked', - type: 'dataLayer', - layerType: LayerTypes.DATA, - xScaleType: 'ordinal', - yScaleType: 'linear', - isHistogram: false, - palette: mockPaletteOutput, - }, - ], + layers: [{ ...(args.layers[0] as DataLayerConfigResult), seriesType: 'area_stacked' }], }} /> ); @@ -1418,16 +1339,7 @@ describe('XYChart component', () => { args={{ ...args, layers: [ - { - ...args.layers[0], - seriesType: 'bar_horizontal_stacked', - type: 'dataLayer', - layerType: LayerTypes.DATA, - xScaleType: 'ordinal', - yScaleType: 'linear', - isHistogram: false, - palette: mockPaletteOutput, - }, + { ...(args.layers[0] as DataLayerConfigResult), seriesType: 'bar_horizontal_stacked' }, ], }} /> @@ -1450,16 +1362,10 @@ describe('XYChart component', () => { ...args, layers: [ { - ...args.layers[0], - type: 'dataLayer', + ...(args.layers[0] as DataLayerConfigResult), xAccessor: undefined, splitAccessor: 'e', seriesType: 'bar_stacked', - layerType: LayerTypes.DATA, - xScaleType: 'ordinal', - yScaleType: 'linear', - isHistogram: false, - palette: mockPaletteOutput, }, ], }} @@ -1486,12 +1392,7 @@ describe('XYChart component', () => { accessors: ['b'], seriesType: 'bar', isHistogram: true, - type: 'dataLayer', - layerType: LayerTypes.DATA, - xScaleType: 'ordinal', - yScaleType: 'linear', - palette: mockPaletteOutput, - }; + } as DataLayerConfigResult; delete firstLayer.splitAccessor; const component = shallow( @@ -1505,12 +1406,7 @@ describe('XYChart component', () => { ...args.layers[0], seriesType: 'bar', isHistogram: true, - type: 'dataLayer', - layerType: LayerTypes.DATA, - xScaleType: 'ordinal', - yScaleType: 'linear', - palette: mockPaletteOutput, - }; + } as DataLayerConfigResult; delete firstLayer.splitAccessor; const component = shallow( @@ -1525,23 +1421,13 @@ describe('XYChart component', () => { ...args.layers[0], seriesType: 'line', isHistogram: true, - type: 'dataLayer', - layerType: LayerTypes.DATA, - xScaleType: 'ordinal', - yScaleType: 'linear', - palette: mockPaletteOutput, - }; + } as DataLayerConfigResult; delete firstLayer.splitAccessor; const secondLayer: DataLayerConfigResult = { ...args.layers[0], seriesType: 'line', isHistogram: true, - type: 'dataLayer', - layerType: LayerTypes.DATA, - xScaleType: 'ordinal', - yScaleType: 'linear', - palette: mockPaletteOutput, - }; + } as DataLayerConfigResult; delete secondLayer.splitAccessor; const component = shallow( { ...args, layers: [ { - ...args.layers[0], + ...(args.layers[0] as DataLayerConfigResult), seriesType: 'bar_stacked', isHistogram: true, - type: 'dataLayer', - layerType: LayerTypes.DATA, - xScaleType: 'ordinal', - yScaleType: 'linear', - palette: mockPaletteOutput, }, ], }} @@ -1590,16 +1471,7 @@ describe('XYChart component', () => { args={{ ...args, layers: [ - { - ...args.layers[0], - seriesType: 'bar', - isHistogram: true, - type: 'dataLayer', - layerType: LayerTypes.DATA, - xScaleType: 'ordinal', - yScaleType: 'linear', - palette: mockPaletteOutput, - }, + { ...(args.layers[0] as DataLayerConfigResult), seriesType: 'bar', isHistogram: true }, ], }} /> @@ -1990,12 +1862,7 @@ describe('XYChart component', () => { data={data} args={{ ...args, - layers: [ - { - ...args.layers[0], - xScaleType: 'ordinal', - } as DataLayerConfigResult, - ], + layers: [{ ...(args.layers[0] as DataLayerConfigResult), xScaleType: 'ordinal' }], }} /> ); @@ -2012,16 +1879,10 @@ describe('XYChart component', () => { data={data} args={{ ...args, - layers: [ - { - ...args.layers[0], - yScaleType: 'sqrt', - } as DataLayerConfigResult, - ], + layers: [{ ...(args.layers[0] as DataLayerConfigResult), yScaleType: 'sqrt' }], }} /> ); - expect(component.find(LineSeries).at(0).prop('yScaleType')).toEqual(ScaleType.Sqrt); expect(component.find(LineSeries).at(1).prop('yScaleType')).toEqual(ScaleType.Sqrt); }); @@ -2041,7 +1902,10 @@ describe('XYChart component', () => { ); expect(getFormatSpy).toHaveBeenCalledWith({ @@ -2457,16 +2321,9 @@ describe('XYChart component', () => { ...args, layers: [ { - ...args.layers[0], + ...(args.layers[0] as DataLayerConfigResult), accessors: ['a'], splitAccessor: undefined, - seriesType: 'bar_stacked', - type: 'dataLayer', - layerType: LayerTypes.DATA, - xScaleType: 'ordinal', - yScaleType: 'linear', - isHistogram: false, - palette: mockPaletteOutput, }, ], legend: { ...args.legend, isVisible: true, showSingleSeries: true }, @@ -2488,16 +2345,9 @@ describe('XYChart component', () => { ...args, layers: [ { - ...args.layers[0], + ...(args.layers[0] as DataLayerConfigResult), accessors: ['a'], splitAccessor: undefined, - seriesType: 'bar_stacked', - type: 'dataLayer', - layerType: LayerTypes.DATA, - xScaleType: 'ordinal', - yScaleType: 'linear', - isHistogram: false, - palette: mockPaletteOutput, }, ], legend: { ...args.legend, isVisible: true, isInside: true }, @@ -2581,7 +2431,7 @@ describe('XYChart component', () => { test('it should apply None fitting function if not specified', () => { const { data, args } = sampleArgs(); - args.layers[0].accessors = ['a']; + (args.layers[0] as DataLayerConfigResult).accessors = ['a']; const component = shallow(); @@ -2712,4 +2562,142 @@ describe('XYChart component', () => { }, ]); }); + + describe('annotations', () => { + const sampleStyledAnnotation: EventAnnotationOutput = { + time: '2022-03-18T08:25:00.000Z', + label: 'Event 1', + icon: 'triangle', + type: 'manual_event_annotation', + color: 'red', + lineStyle: 'dashed', + lineWidth: 3, + }; + const sampleAnnotationLayers: AnnotationLayerConfigResult[] = [ + { + type: 'annotationLayer', + layerType: LayerTypes.ANNOTATIONS, + layerId: 'annotation', + annotations: [ + { + time: '2022-03-18T08:25:17.140Z', + label: 'Annotation', + type: 'manual_event_annotation', + }, + ], + }, + ]; + function sampleArgsWithAnnotation(annotationLayers = sampleAnnotationLayers) { + const { args } = sampleArgs(); + return { + data: dateHistogramData, + args: { + ...args, + layers: [dateHistogramLayer, ...annotationLayers], + } as XYArgs, + }; + } + test('should render basic annotation', () => { + const { data, args } = sampleArgsWithAnnotation(); + const component = mount(); + expect(component.find('LineAnnotation')).toMatchSnapshot(); + }); + test('should render simplified annotation when hide is true', () => { + const { data, args } = sampleArgsWithAnnotation(); + (args.layers[0] as DataLayerConfigResult).hide = true; + const component = mount(); + expect(component.find('LineAnnotation')).toMatchSnapshot(); + }); + + test('should render grouped annotations preserving the shared styles', () => { + const { data, args } = sampleArgsWithAnnotation([ + { + type: 'annotationLayer', + layerType: LayerTypes.ANNOTATIONS, + layerId: 'annotation', + annotations: [ + sampleStyledAnnotation, + { ...sampleStyledAnnotation, time: '2022-03-18T08:25:00.020Z', label: 'Event 2' }, + { + ...sampleStyledAnnotation, + time: '2022-03-18T08:25:00.001Z', + label: 'Event 3', + }, + ], + }, + ]); + const component = mount(); + const groupedAnnotation = component.find(LineAnnotation); + + expect(groupedAnnotation.length).toEqual(1); + // styles are passed because they are shared, dataValues & header is rounded to the interval + expect(groupedAnnotation).toMatchSnapshot(); + // renders numeric icon for grouped annotations + const marker = mount(
{groupedAnnotation.prop('marker')}
); + const numberIcon = marker.find('NumberIcon'); + expect(numberIcon.length).toEqual(1); + expect(numberIcon.text()).toEqual('3'); + + // checking tooltip + const renderLinks = mount(
{groupedAnnotation.prop('customTooltipDetails')!()}
); + expect(renderLinks.text()).toEqual( + ' Event 1 2022-03-18T08:25:00.000Z Event 2 2022-03-18T08:25:00.020Z Event 3 2022-03-18T08:25:00.001Z' + ); + }); + test('should render grouped annotations with default styles', () => { + const { data, args } = sampleArgsWithAnnotation([ + { + type: 'annotationLayer', + layerType: LayerTypes.ANNOTATIONS, + layerId: 'annotation', + annotations: [sampleStyledAnnotation], + }, + { + type: 'annotationLayer', + layerType: LayerTypes.ANNOTATIONS, + layerId: 'annotation', + annotations: [ + { + ...sampleStyledAnnotation, + icon: 'square', + color: 'blue', + lineStyle: 'dotted', + lineWidth: 10, + time: '2022-03-18T08:25:00.001Z', + label: 'Event 2', + }, + ], + }, + ]); + const component = mount(); + const groupedAnnotation = component.find(LineAnnotation); + + expect(groupedAnnotation.length).toEqual(1); + // styles are default because they are different for both annotations + expect(groupedAnnotation).toMatchSnapshot(); + }); + test('should not render hidden annotations', () => { + const { data, args } = sampleArgsWithAnnotation([ + { + type: 'annotationLayer', + layerType: LayerTypes.ANNOTATIONS, + layerId: 'annotation', + annotations: [ + sampleStyledAnnotation, + { ...sampleStyledAnnotation, time: '2022-03-18T08:30:00.020Z', label: 'Event 2' }, + { + ...sampleStyledAnnotation, + time: '2022-03-18T08:35:00.001Z', + label: 'Event 3', + isHidden: true, + }, + ], + }, + ]); + const component = mount(); + const annotations = component.find(LineAnnotation); + + expect(annotations.length).toEqual(2); + }); + }); }); diff --git a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx index f0471a2df1261..421ce9520f061 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx @@ -43,22 +43,9 @@ import { RenderMode } from '../../../../expressions/common'; import { FieldFormat } from '../../../../field_formats/common'; import { EmptyPlaceholder } from '../../../../../plugins/charts/public'; import type { FilterEvent, BrushEvent, FormatFactory } from '../types'; -import type { DataLayerConfigResult, SeriesType, XYChartProps } from '../../common'; -import { isHorizontalChart, getSeriesColor } from '../helpers'; -import { EventAnnotationServiceType } from '../../../../../src/plugins/event_annotation/public'; -import { KibanaThemeProvider } from '../../../../../src/plugins/kibana_react/public'; -import type { ILensInterpreterRenderHandlers, LensFilterEvent, LensBrushEvent } from '../types'; -import type { LensMultiTable, FormatFactory } from '../../common'; -import type { - DataLayerArgs, - SeriesType, - XYChartProps, - XYLayerArgs, -} from '../../common/expressions'; -import { visualizationTypes } from './types'; -import { VisualizationContainer } from '../visualization_container'; -import { isHorizontalChart, getSeriesColor } from './state_helpers'; -import { search } from '../../../../../src/plugins/data/public'; +import type { SeriesType, XYChartProps } from '../../common/types'; +import { isHorizontalChart, getSeriesColor, getAnnotationsLayers, getDataLayers } from '../helpers'; +import { EventAnnotationServiceType } from '../../../../event_annotation/public'; import { ChartsPluginSetup, ChartsPluginStart, @@ -70,8 +57,6 @@ import { MULTILAYER_TIME_AXIS_STYLE } from '../../../../../plugins/charts/common import { getFilteredLayers, getReferenceLayers, - getDataLayersArgs, - getAnnotationsLayersArgs, isDataLayer, getFitOptions, getAxesConfiguration, @@ -79,14 +64,14 @@ import { validateExtent, computeOverallDataDomain, getColorAssignments, + getLinesCausedPaddings, } from '../helpers'; import { getXDomain, XyEndzones } from './x_domain'; import { getLegendAction } from './legend_action'; -import { ReferenceLineAnnotations } from './reference_lines'; +import { ReferenceLineAnnotations, computeChartMargins } from './reference_lines'; import { visualizationDefinitions } from '../definitions'; import { XYLayerConfigResult } from '../../common/types'; -import { computeChartMargins, getLinesCausedPaddings } from './annotations_helpers'; -import { Annotations, getAnnotationsGroupedByInterval } from './annotations/expression'; +import { Annotations, getAnnotationsGroupedByInterval } from './annotations'; import './xy_chart.scss'; @@ -192,9 +177,7 @@ export function XYChart({ }); if (filteredLayers.length === 0) { - const icon: IconType = getIconForSeriesType( - getDataLayersArgs(layers)?.[0]?.seriesType || 'bar' - ); + const icon: IconType = getIconForSeriesType(getDataLayers(layers)?.[0]?.seriesType || 'bar'); return ; } @@ -282,7 +265,7 @@ export function XYChart({ }; const referenceLineLayers = getReferenceLayers(layers); - const annotationsLayers = getAnnotationsLayersArgs(layers); + const annotationsLayers = getAnnotationsLayers(layers); const firstTable = data.tables[filteredLayers[0].layerId]; const xColumnId = firstTable.columns.find((col) => col.id === filteredLayers[0].xAccessor)?.id; @@ -407,7 +390,7 @@ export function XYChart({ const valueLabelsStyling = shouldShowValueLabels && valueLabels !== 'hide' && getValueLabelsStyling(shouldRotate); - const colorAssignments = getColorAssignments(getDataLayersArgs(args.layers), data, formatFactory); + const colorAssignments = getColorAssignments(getDataLayers(args.layers), data, formatFactory); const clickHandler: ElementClickListener = ([[geometry, series]]) => { // for xyChart series is always XYChartSeriesIdentifier and geometry is always type of GeometryValue @@ -967,23 +950,6 @@ export function XYChart({ ); } -function getFilteredLayers(layers: XYLayerArgs[], data: LensMultiTable) { - return getDataLayersArgs(layers).filter((layer) => { - const { layerId, xAccessor, accessors, splitAccessor } = layer; - return !( - !accessors.length || - !data.tables[layerId] || - data.tables[layerId].rows.length === 0 || - (xAccessor && - data.tables[layerId].rows.every((row) => typeof row[xAccessor] === 'undefined')) || - // stacked percentage bars have no xAccessors but splitAccessor with undefined values in them when empty - (!xAccessor && - splitAccessor && - data.tables[layerId].rows.every((row) => typeof row[splitAccessor] === 'undefined')) - ); - }); -} - function assertNever(x: never): never { throw new Error('Unexpected series type: ' + x); } diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/annotations.tsx b/src/plugins/chart_expressions/expression_xy/public/helpers/annotations.tsx new file mode 100644 index 0000000000000..91aeae9c7c6c0 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/annotations.tsx @@ -0,0 +1,336 @@ +/* + * 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 React from 'react'; +import { EuiFlexGroup, EuiIcon, EuiIconProps, EuiText } from '@elastic/eui'; +import { Position } from '@elastic/charts'; +import classnames from 'classnames'; +import { i18n } from '@kbn/i18n'; +import moment from 'moment'; +import type { IconPosition, YAxisMode, YConfig } from '../../common/types'; +import { hasIcon } from './icon'; +import { annotationsIconSet } from './annotations_icon_set'; +import type { XYDataLayerConfig, XYAnnotationLayerConfig, XYLayerConfig } from '../../common/types'; +import type { FramePublicAPI } from '../types'; +import { getAnnotationsLayersConfig } from './visualization'; +import { defaultAnnotationColor } from '../../../../../../src/plugins/event_annotation/public'; + +import './expression_reference_lines.scss'; + +export const LINES_MARKER_SIZE = 20; + +export const computeChartMargins = ( + referenceLinePaddings: Partial>, + labelVisibility: Partial>, + titleVisibility: Partial>, + axesMap: Record<'left' | 'right', unknown>, + isHorizontal: boolean +) => { + const result: Partial> = {}; + if (!labelVisibility?.x && !titleVisibility?.x && referenceLinePaddings.bottom) { + const placement = isHorizontal ? mapVerticalToHorizontalPlacement('bottom') : 'bottom'; + result[placement] = referenceLinePaddings.bottom; + } + if ( + referenceLinePaddings.left && + (isHorizontal || (!labelVisibility?.yLeft && !titleVisibility?.yLeft)) + ) { + const placement = isHorizontal ? mapVerticalToHorizontalPlacement('left') : 'left'; + result[placement] = referenceLinePaddings.left; + } + if ( + referenceLinePaddings.right && + (isHorizontal || !axesMap.right || (!labelVisibility?.yRight && !titleVisibility?.yRight)) + ) { + const placement = isHorizontal ? mapVerticalToHorizontalPlacement('right') : 'right'; + result[placement] = referenceLinePaddings.right; + } + // there's no top axis, so just check if a margin has been computed + if (referenceLinePaddings.top) { + const placement = isHorizontal ? mapVerticalToHorizontalPlacement('top') : 'top'; + result[placement] = referenceLinePaddings.top; + } + return result; +}; + +// Note: it does not take into consideration whether the reference line is in view or not + +export const getLinesCausedPaddings = ( + visualConfigs: Array< + Pick | undefined + >, + axesMap: Record<'left' | 'right', unknown> +) => { + // collect all paddings for the 4 axis: if any text is detected double it. + const paddings: Partial> = {}; + const icons: Partial> = {}; + visualConfigs?.forEach((config) => { + if (!config) { + return; + } + const { axisMode, icon, iconPosition, textVisibility } = config; + if (axisMode && (hasIcon(icon) || textVisibility)) { + const placement = getBaseIconPlacement(iconPosition, axesMap, axisMode); + paddings[placement] = Math.max( + paddings[placement] || 0, + LINES_MARKER_SIZE * (textVisibility ? 2 : 1) // double the padding size if there's text + ); + icons[placement] = (icons[placement] || 0) + (hasIcon(icon) ? 1 : 0); + } + }); + // post-process the padding based on the icon presence: + // if no icon is present for the placement, just reduce the padding + (Object.keys(paddings) as Position[]).forEach((placement) => { + if (!icons[placement]) { + paddings[placement] = LINES_MARKER_SIZE; + } + }); + return paddings; +}; + +export function mapVerticalToHorizontalPlacement(placement: Position) { + switch (placement) { + case Position.Top: + return Position.Right; + case Position.Bottom: + return Position.Left; + case Position.Left: + return Position.Bottom; + case Position.Right: + return Position.Top; + } +} + +// if there's just one axis, put it on the other one +// otherwise use the same axis +// this function assume the chart is vertical +export function getBaseIconPlacement( + iconPosition: IconPosition | undefined, + axesMap?: Record, + axisMode?: YAxisMode +) { + if (iconPosition === 'auto') { + if (axisMode === 'bottom') { + return Position.Top; + } + if (axesMap) { + if (axisMode === 'left') { + return axesMap.right ? Position.Left : Position.Right; + } + return axesMap.left ? Position.Right : Position.Left; + } + } + + if (iconPosition === 'left') { + return Position.Left; + } + if (iconPosition === 'right') { + return Position.Right; + } + if (iconPosition === 'below') { + return Position.Bottom; + } + return Position.Top; +} + +export function MarkerBody({ + label, + isHorizontal, +}: { + label: string | undefined; + isHorizontal: boolean; +}) { + if (!label) { + return null; + } + if (isHorizontal) { + return ( +
+ {label} +
+ ); + } + return ( +
+
+ {label} +
+
+ ); +} + +const isNumericalString = (value: string) => !isNaN(Number(value)); + +function NumberIcon({ number }: { number: number }) { + return ( + + + {number < 10 ? number : `9+`} + + + ); +} + +interface MarkerConfig { + axisMode?: YAxisMode; + icon?: string; + textVisibility?: boolean; + iconPosition?: IconPosition; +} + +export const AnnotationIcon = ({ + type, + rotateClassName = '', + isHorizontal, + renderedInChart, + ...rest +}: { + type: string; + rotateClassName?: string; + isHorizontal?: boolean; + renderedInChart?: boolean; +} & EuiIconProps) => { + if (isNumericalString(type)) { + return ; + } + const iconConfig = annotationsIconSet.find((i) => i.value === type); + if (!iconConfig) { + return null; + } + return ( + + ); +}; + +export function Marker({ + config, + isHorizontal, + hasReducedPadding, + label, + rotateClassName, +}: { + config: MarkerConfig; + isHorizontal: boolean; + hasReducedPadding: boolean; + label?: string; + rotateClassName?: string; +}) { + if (hasIcon(config.icon)) { + return ( + + ); + } + + // if there's some text, check whether to show it as marker, or just show some padding for the icon + if (config.textVisibility) { + if (hasReducedPadding) { + return ; + } + return ; + } + return null; +} + +const MAX_DATE = 8640000000000000; +const MIN_DATE = -8640000000000000; + +export function getStaticDate( + dataLayers: XYDataLayerConfig[], + activeData: FramePublicAPI['activeData'] +) { + const fallbackValue = moment().toISOString(); + + const dataLayersId = dataLayers.map(({ layerId }) => layerId); + if ( + !activeData || + Object.entries(activeData) + .filter(([key]) => dataLayersId.includes(key)) + .every(([, { rows }]) => !rows || !rows.length) + ) { + return fallbackValue; + } + + const minDate = dataLayersId.reduce((acc, lId) => { + const xAccessor = dataLayers.find((dataLayer) => dataLayer.layerId === lId)?.xAccessor!; + const firstTimestamp = activeData[lId]?.rows?.[0]?.[xAccessor]; + return firstTimestamp && firstTimestamp < acc ? firstTimestamp : acc; + }, MAX_DATE); + + const maxDate = dataLayersId.reduce((acc, lId) => { + const xAccessor = dataLayers.find((dataLayer) => dataLayer.layerId === lId)?.xAccessor!; + const lastTimestamp = activeData[lId]?.rows?.[activeData?.[lId]?.rows?.length - 1]?.[xAccessor]; + return lastTimestamp && lastTimestamp > acc ? lastTimestamp : acc; + }, MIN_DATE); + const middleDate = (minDate + maxDate) / 2; + return moment(middleDate).toISOString(); +} + +export const getAnnotationsAccessorColorConfig = (layer: XYAnnotationLayerConfig) => { + return layer.annotations.map((annotation) => { + return { + columnId: annotation.id, + triggerIcon: annotation.isHidden ? ('invisible' as const) : ('color' as const), + color: annotation?.color || defaultAnnotationColor, + }; + }); +}; + +export const getUniqueLabels = (layers: XYLayerConfig[]) => { + const annotationLayers = getAnnotationsLayersConfig(layers); + const columnLabelMap = {} as Record; + const counts = {} as Record; + + const makeUnique = (label: string) => { + let uniqueLabel = label; + + while (counts[uniqueLabel] >= 0) { + const num = ++counts[uniqueLabel]; + uniqueLabel = i18n.translate('xpack.lens.uniqueLabel', { + defaultMessage: '{label} [{num}]', + values: { label, num }, + }); + } + + counts[uniqueLabel] = 0; + return uniqueLabel; + }; + + annotationLayers.forEach((layer) => { + if (!layer.annotations) { + return; + } + layer.annotations.forEach((l) => { + columnLabelMap[l.id] = makeUnique(l.label); + }); + }); + return columnLabelMap; +}; diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/annotations_icon_set.tsx b/src/plugins/chart_expressions/expression_xy/public/helpers/annotations_icon_set.tsx new file mode 100644 index 0000000000000..b6c978478d137 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/annotations_icon_set.tsx @@ -0,0 +1,99 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { TriangleIcon, CircleIcon } from '../icons'; + +export const annotationsIconSet = [ + { + value: 'asterisk', + label: i18n.translate('xpack.lens.xyChart.iconSelect.asteriskIconLabel', { + defaultMessage: 'Asterisk', + }), + }, + { + value: 'alert', + label: i18n.translate('xpack.lens.xyChart.iconSelect.alertIconLabel', { + defaultMessage: 'Alert', + }), + }, + { + value: 'bell', + label: i18n.translate('xpack.lens.xyChart.iconSelect.bellIconLabel', { + defaultMessage: 'Bell', + }), + }, + { + value: 'bolt', + label: i18n.translate('xpack.lens.xyChart.iconSelect.boltIconLabel', { + defaultMessage: 'Bolt', + }), + }, + { + value: 'bug', + label: i18n.translate('xpack.lens.xyChart.iconSelect.bugIconLabel', { + defaultMessage: 'Bug', + }), + }, + { + value: 'circle', + label: i18n.translate('xpack.lens.xyChart.iconSelect.circleIconLabel', { + defaultMessage: 'Circle', + }), + icon: CircleIcon, + canFill: true, + }, + + { + value: 'editorComment', + label: i18n.translate('xpack.lens.xyChart.iconSelect.commentIconLabel', { + defaultMessage: 'Comment', + }), + }, + { + value: 'flag', + label: i18n.translate('xpack.lens.xyChart.iconSelect.flagIconLabel', { + defaultMessage: 'Flag', + }), + }, + { + value: 'heart', + label: i18n.translate('xpack.lens.xyChart.iconSelect.heartLabel', { defaultMessage: 'Heart' }), + }, + { + value: 'mapMarker', + label: i18n.translate('xpack.lens.xyChart.iconSelect.mapMarkerLabel', { + defaultMessage: 'Map Marker', + }), + }, + { + value: 'pinFilled', + label: i18n.translate('xpack.lens.xyChart.iconSelect.mapPinLabel', { + defaultMessage: 'Map Pin', + }), + }, + { + value: 'starEmpty', + label: i18n.translate('xpack.lens.xyChart.iconSelect.starLabel', { defaultMessage: 'Star' }), + }, + { + value: 'tag', + label: i18n.translate('xpack.lens.xyChart.iconSelect.tagIconLabel', { + defaultMessage: 'Tag', + }), + }, + { + value: 'triangle', + label: i18n.translate('xpack.lens.xyChart.iconSelect.triangleIconLabel', { + defaultMessage: 'Triangle', + }), + icon: TriangleIcon, + shouldRotate: true, + canFill: true, + }, +]; diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.ts index ef0cd36e3598e..edb1994090fc2 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.ts @@ -12,6 +12,7 @@ import type { Datatable } from '../../../../expressions'; import { FormatFactory } from '../types'; import { isDataLayer } from './visualization'; import { DataLayerConfigResult, XYLayerConfigResult } from '../../common'; +import { XYLayerConfig } from '../../common/types'; const isPrimitive = (value: unknown): boolean => value != null && typeof value !== 'object'; @@ -26,7 +27,7 @@ export type ColorAssignments = Record< >; export function getColorAssignments( - layers: XYLayerConfigResult[], + layers: Array, data: { tables: Record }, formatFactory: FormatFactory ): ColorAssignments { diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/index.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/index.ts index fe2b7d89f9e2b..cb0300e47ae70 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/index.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/index.ts @@ -15,3 +15,5 @@ export * from './axes_configuration'; export * from './reference_lines'; export * from './icon'; export * from './color_assignment'; +export * from './annotations_icon_set'; +export * from './annotations'; diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/layers.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/layers.ts index 28cea4a269829..be1701e6b6e4b 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/layers.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/layers.ts @@ -6,27 +6,25 @@ * Side Public License, v 1. */ -import { DataLayerConfigResult, LensMultiTable, XYLayerConfigResult } from '../../common'; -import { isDataLayer } from './visualization'; +import { LensMultiTable } from '../../common'; +import { DataLayerConfigResult, XYLayerConfigResult } from '../../common/types'; +import { getDataLayers } from './visualization'; export function getFilteredLayers(layers: XYLayerConfigResult[], data: LensMultiTable) { - return layers.filter((layer): layer is DataLayerConfigResult => { - if (!isDataLayer(layer)) { - return false; + return getDataLayers(layers).filter( + (layer): layer is DataLayerConfigResult => { + const { layerId, xAccessor, accessors, splitAccessor } = layer; + return !( + !accessors.length || + !data.tables[layerId] || + data.tables[layerId].rows.length === 0 || + (xAccessor && + data.tables[layerId].rows.every((row) => typeof row[xAccessor] === 'undefined')) || + // stacked percentage bars have no xAccessors but splitAccessor with undefined values in them when empty + (!xAccessor && + splitAccessor && + data.tables[layerId].rows.every((row) => typeof row[splitAccessor] === 'undefined')) + ); } - - const { layerId, accessors, xAccessor, splitAccessor } = layer; - - return !( - !accessors.length || - !data.tables[layerId] || - data.tables[layerId].rows.length === 0 || - (xAccessor && - data.tables[layerId].rows.every((row) => typeof row[xAccessor] === 'undefined')) || - // stacked percentage bars have no xAccessors but splitAccessor with undefined values in them when empty - (!xAccessor && - splitAccessor && - data.tables[layerId].rows.every((row) => typeof row[splitAccessor] === 'undefined')) - ); - }); + ); } diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/reference_lines.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/reference_lines.ts index 86a87e22157c7..609aed45eda9f 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/reference_lines.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/reference_lines.ts @@ -7,7 +7,7 @@ */ import { partition } from 'lodash'; -import type { DataLayerConfigResult } from '../../common'; +import type { DataLayerConfigResult, YConfig } from '../../common'; import type { FramePublicAPI } from '../types'; import { isStackedChart } from './state'; diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/visualization.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/visualization.ts index 6d0e19dd47086..7a308860c88fd 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/visualization.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/visualization.ts @@ -9,15 +9,25 @@ import { DataLayerConfigResult, ReferenceLineLayerConfigResult, + XYAnnotationLayerConfig, XYLayerConfigResult, -} from '../../common'; + XYLayerConfig, + XYDataLayerConfig, + XYReferenceLineLayerConfig, + LensMultiTable, + AnnotationLayerConfigResult, +} from '../../common/types'; import { LayerTypes } from '../../common/constants'; -export const isDataLayer = (layer: XYLayerConfigResult): layer is DataLayerConfigResult => +export const isDataLayer = ( + layer: XYLayerConfig | XYLayerConfigResult +): layer is XYDataLayerConfig | DataLayerConfigResult => layer.layerType === LayerTypes.DATA || !layer.layerType; -export const getDataLayers = (layers: XYLayerConfigResult[]) => - (layers || []).filter((layer): layer is DataLayerConfigResult => isDataLayer(layer)); +export const getDataLayers = (layers: Array) => + (layers || []).filter((layer): layer is XYDataLayerConfig | DataLayerConfigResult => + isDataLayer(layer) + ); export const isReferenceLayer = ( layer: XYLayerConfigResult @@ -27,3 +37,31 @@ export const getReferenceLayers = (layers: XYLayerConfigResult[]) => (layers || []).filter((layer): layer is ReferenceLineLayerConfigResult => isReferenceLayer(layer) ); + +const isAnnotationLayerCommon = ( + layer: XYLayerConfig | XYLayerConfigResult +): layer is XYAnnotationLayerConfig | AnnotationLayerConfigResult => + layer.layerType === LayerTypes.ANNOTATIONS; + +export const isAnnotationsLayerConfig = (layer: XYLayerConfig): layer is XYAnnotationLayerConfig => + isAnnotationLayerCommon(layer); + +export const isAnnotationsLayer = ( + layer: XYLayerConfigResult +): layer is AnnotationLayerConfigResult => isAnnotationLayerCommon(layer); + +export const getAnnotationsLayersConfig = (layers: XYLayerConfig[]): XYAnnotationLayerConfig[] => + (layers || []).filter((layer): layer is XYAnnotationLayerConfig => + isAnnotationsLayerConfig(layer) + ); + +export const getAnnotationsLayers = ( + layers: XYLayerConfigResult[] +): AnnotationLayerConfigResult[] => + (layers || []).filter((layer): layer is AnnotationLayerConfigResult => isAnnotationsLayer(layer)); + +export interface LayerTypeToLayer { + [LayerTypes.DATA]: (layer: XYDataLayerConfig) => XYDataLayerConfig; + [LayerTypes.REFERENCELINE]: (layer: XYReferenceLineLayerConfig) => XYReferenceLineLayerConfig; + [LayerTypes.ANNOTATIONS]: (layer: XYAnnotationLayerConfig) => XYAnnotationLayerConfig; +} diff --git a/src/plugins/chart_expressions/expression_xy/public/icons/circle.tsx b/src/plugins/chart_expressions/expression_xy/public/icons/circle.tsx new file mode 100644 index 0000000000000..39bbe5cde74de --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/public/icons/circle.tsx @@ -0,0 +1,32 @@ +/* + * 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 React from 'react'; +import { EuiIconProps } from '@elastic/eui'; +import classnames from 'classnames'; + +export const CircleIcon = ({ title, titleId, ...props }: Omit) => ( + + {title ? {title} : null} + + +); diff --git a/src/plugins/chart_expressions/expression_xy/public/icons/index.ts b/src/plugins/chart_expressions/expression_xy/public/icons/index.ts index 3f78474a6d54c..4ca0b640a3d89 100644 --- a/src/plugins/chart_expressions/expression_xy/public/icons/index.ts +++ b/src/plugins/chart_expressions/expression_xy/public/icons/index.ts @@ -14,7 +14,9 @@ export { BarHorizontalIcon } from './bar_horizontal'; export { BarPercentageIcon } from './bar_percentage'; export { AreaStackedIcon } from './area_stacked'; export { BarStackedIcon } from './bar_stacked'; +export { TriangleIcon } from './triangle'; export { MixedXyIcon } from './mixed_xy'; +export { CircleIcon } from './circle'; export { AreaIcon } from './area'; export { LineIcon } from './line'; export { BarIcon } from './bar'; diff --git a/src/plugins/chart_expressions/expression_xy/public/icons/triangle.tsx b/src/plugins/chart_expressions/expression_xy/public/icons/triangle.tsx new file mode 100644 index 0000000000000..8ffb8c490d9a4 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/public/icons/triangle.tsx @@ -0,0 +1,31 @@ +/* + * 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 React from 'react'; +import { EuiIconProps } from '@elastic/eui'; +import classnames from 'classnames'; + +export const TriangleIcon = ({ title, titleId, ...props }: Omit) => ( + + {title ? {title} : null} + + +); diff --git a/src/plugins/chart_expressions/expression_xy/public/plugin.ts b/src/plugins/chart_expressions/expression_xy/public/plugin.ts index 9f175b03d6743..c43e3aae11de7 100755 --- a/src/plugins/chart_expressions/expression_xy/public/plugin.ts +++ b/src/plugins/chart_expressions/expression_xy/public/plugin.ts @@ -21,16 +21,19 @@ import { dataLayerConfigFunction, axisExtentConfigFunction, tickLabelsConfigFunction, + annotationLayerConfigFunction, labelsOrientationConfigFunction, referenceLineLayerConfigFunction, axisTitlesVisibilityConfigFunction, } from '../common'; import { GetStartDepsFn, getXyChartRenderer } from './expression_renderers'; +import { EventAnnotationPluginSetup } from '../../../event_annotation/public'; export interface XYPluginStartDependencies { data: DataPublicPluginStart; fieldFormats: FieldFormatsStart; charts: ChartsPluginStart; + eventAnnotation: EventAnnotationPluginSetup; } export function getTimeZone(uiSettings: IUiSettingsClient) { @@ -53,6 +56,7 @@ export class ExpressionXyPlugin { expressions.registerFunction(dataLayerConfigFunction); expressions.registerFunction(axisExtentConfigFunction); expressions.registerFunction(tickLabelsConfigFunction); + expressions.registerFunction(annotationLayerConfigFunction); expressions.registerFunction(labelsOrientationConfigFunction); expressions.registerFunction(referenceLineLayerConfigFunction); expressions.registerFunction(axisTitlesVisibilityConfigFunction); @@ -63,12 +67,14 @@ export class ExpressionXyPlugin { const { data, fieldFormats, + eventAnnotation, charts: { activeCursor, theme, palettes }, } = deps; const paletteService = await palettes.getPalettes(); const { theme: kibanaTheme } = coreStart; + const eventAnnotationService = await eventAnnotation.getService(); const useLegacyTimeAxis = core.uiSettings.get(LEGACY_TIME_AXIS); return { @@ -79,6 +85,7 @@ export class ExpressionXyPlugin { activeCursor, paletteService, useLegacyTimeAxis, + eventAnnotationService, timeZone: getTimeZone(core.uiSettings), }; }; diff --git a/src/plugins/chart_expressions/expression_xy/server/plugin.ts b/src/plugins/chart_expressions/expression_xy/server/plugin.ts index 38f5526504ae0..ff979fd38e1c6 100755 --- a/src/plugins/chart_expressions/expression_xy/server/plugin.ts +++ b/src/plugins/chart_expressions/expression_xy/server/plugin.ts @@ -17,6 +17,7 @@ import { dataLayerConfigFunction, axisExtentConfigFunction, tickLabelsConfigFunction, + annotationLayerConfigFunction, labelsOrientationConfigFunction, referenceLineLayerConfigFunction, axisTitlesVisibilityConfigFunction, @@ -33,6 +34,7 @@ export class ExpressionXyPlugin expressions.registerFunction(dataLayerConfigFunction); expressions.registerFunction(axisExtentConfigFunction); expressions.registerFunction(tickLabelsConfigFunction); + expressions.registerFunction(annotationLayerConfigFunction); expressions.registerFunction(labelsOrientationConfigFunction); expressions.registerFunction(referenceLineLayerConfigFunction); expressions.registerFunction(axisTitlesVisibilityConfigFunction); diff --git a/src/plugins/chart_expressions/expression_xy/tsconfig.json b/src/plugins/chart_expressions/expression_xy/tsconfig.json index ce0fa553196fb..bba4d2c779fad 100644 --- a/src/plugins/chart_expressions/expression_xy/tsconfig.json +++ b/src/plugins/chart_expressions/expression_xy/tsconfig.json @@ -20,5 +20,6 @@ { "path": "../../ui_actions/tsconfig.json" }, { "path": "../../field_formats/tsconfig.json"}, { "path": "../../kibana_utils/tsconfig.json" }, + { "path": "../../kibana_utils/tsconfig.json" }, ] } diff --git a/x-pack/plugins/lens/common/expressions/xy_chart/layer_config/index.ts b/x-pack/plugins/lens/common/expressions/xy_chart/layer_config/index.ts deleted file mode 100644 index df27229bdb81f..0000000000000 --- a/x-pack/plugins/lens/common/expressions/xy_chart/layer_config/index.ts +++ /dev/null @@ -1,17 +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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import { XYDataLayerConfig } from './data_layer_config'; -import { XYReferenceLineLayerConfig } from './reference_line_layer_config'; -import { XYAnnotationLayerConfig } from './annotation_layer_config'; -export * from './data_layer_config'; -export * from './reference_line_layer_config'; -export * from './annotation_layer_config'; - -export type XYLayerConfig = - | XYDataLayerConfig - | XYReferenceLineLayerConfig - | XYAnnotationLayerConfig; diff --git a/x-pack/plugins/lens/common/expressions/xy_chart/xy_args.ts b/x-pack/plugins/lens/common/expressions/xy_chart/xy_args.ts deleted file mode 100644 index 4520f0c99c3e9..0000000000000 --- a/x-pack/plugins/lens/common/expressions/xy_chart/xy_args.ts +++ /dev/null @@ -1,45 +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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { AxisExtentConfigResult, AxisTitlesVisibilityConfigResult } from './axis_config'; -import type { FittingFunction } from './fitting_function'; -import type { EndValue } from './end_value'; -import type { GridlinesConfigResult } from './grid_lines_config'; -import type { AnnotationLayerArgs, DataLayerArgs } from './layer_config'; -import type { LegendConfigResult } from './legend_config'; -import type { TickLabelsConfigResult } from './tick_labels_config'; -import type { LabelsOrientationConfigResult } from './labels_orientation_config'; -import type { ValueLabelConfig } from '../../types'; - -export type XYCurveType = 'LINEAR' | 'CURVE_MONOTONE_X'; -export type XYLayerArgs = DataLayerArgs | AnnotationLayerArgs; - -// Arguments to XY chart expression, with computed properties -export interface XYArgs { - title?: string; - description?: string; - xTitle: string; - yTitle: string; - yRightTitle: string; - yLeftExtent: AxisExtentConfigResult; - yRightExtent: AxisExtentConfigResult; - legend: LegendConfigResult; - valueLabels: ValueLabelConfig; - layers: XYLayerArgs[]; - fittingFunction?: FittingFunction; - endValue?: EndValue; - emphasizeFitting?: boolean; - axisTitlesVisibilitySettings?: AxisTitlesVisibilityConfigResult; - tickLabelsVisibilitySettings?: TickLabelsConfigResult; - gridlinesVisibilitySettings?: GridlinesConfigResult; - labelsOrientation?: LabelsOrientationConfigResult; - curveType?: XYCurveType; - fillOpacity?: number; - hideEndzones?: boolean; - valuesInLegend?: boolean; - ariaLabel?: string; -} diff --git a/x-pack/plugins/lens/common/expressions/xy_chart/xy_chart.ts b/x-pack/plugins/lens/common/expressions/xy_chart/xy_chart.ts deleted file mode 100644 index 6d73e8eb9ba5f..0000000000000 --- a/x-pack/plugins/lens/common/expressions/xy_chart/xy_chart.ts +++ /dev/null @@ -1,194 +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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import { i18n } from '@kbn/i18n'; -import type { ExpressionFunctionDefinition } from '../../../../../../src/plugins/expressions/common'; -import type { ExpressionValueSearchContext } from '../../../../../../src/plugins/data/common'; -import type { LensMultiTable } from '../../types'; -import type { XYArgs } from './xy_args'; -import { fittingFunctionDefinitions } from './fitting_function'; -import { endValueDefinitions } from './end_value'; -import { logDataTable } from '../expressions_utils'; - -export interface XYChartProps { - data: LensMultiTable; - args: XYArgs; -} - -export interface XYRender { - type: 'render'; - as: 'lens_xy_chart_renderer'; - value: XYChartProps; -} - -export const xyChart: ExpressionFunctionDefinition< - 'lens_xy_chart', - LensMultiTable | ExpressionValueSearchContext | null, - XYArgs, - XYRender -> = { - name: 'lens_xy_chart', - type: 'render', - inputTypes: ['lens_multitable', 'kibana_context', 'null'], - help: i18n.translate('xpack.lens.xyChart.help', { - defaultMessage: 'An X/Y chart', - }), - args: { - title: { - types: ['string'], - help: 'The chart title.', - }, - description: { - types: ['string'], - help: '', - }, - xTitle: { - types: ['string'], - help: i18n.translate('xpack.lens.xyChart.xTitle.help', { - defaultMessage: 'X axis title', - }), - }, - yTitle: { - types: ['string'], - help: i18n.translate('xpack.lens.xyChart.yLeftTitle.help', { - defaultMessage: 'Y left axis title', - }), - }, - yRightTitle: { - types: ['string'], - help: i18n.translate('xpack.lens.xyChart.yRightTitle.help', { - defaultMessage: 'Y right axis title', - }), - }, - yLeftExtent: { - types: ['lens_xy_axisExtentConfig'], - help: i18n.translate('xpack.lens.xyChart.yLeftExtent.help', { - defaultMessage: 'Y left axis extents', - }), - }, - yRightExtent: { - types: ['lens_xy_axisExtentConfig'], - help: i18n.translate('xpack.lens.xyChart.yRightExtent.help', { - defaultMessage: 'Y right axis extents', - }), - }, - legend: { - types: ['lens_xy_legendConfig'], - help: i18n.translate('xpack.lens.xyChart.legend.help', { - defaultMessage: 'Configure the chart legend.', - }), - }, - fittingFunction: { - types: ['string'], - options: [...fittingFunctionDefinitions.map(({ id }) => id)], - help: i18n.translate('xpack.lens.xyChart.fittingFunction.help', { - defaultMessage: 'Define how missing values are treated', - }), - }, - endValue: { - types: ['string'], - options: [...endValueDefinitions.map(({ id }) => id)], - help: '', - }, - emphasizeFitting: { - types: ['boolean'], - default: false, - help: '', - }, - valueLabels: { - types: ['string'], - options: ['hide', 'inside'], - help: '', - }, - tickLabelsVisibilitySettings: { - types: ['lens_xy_tickLabelsConfig'], - help: i18n.translate('xpack.lens.xyChart.tickLabelsSettings.help', { - defaultMessage: 'Show x and y axes tick labels', - }), - }, - labelsOrientation: { - types: ['lens_xy_labelsOrientationConfig'], - help: i18n.translate('xpack.lens.xyChart.labelsOrientation.help', { - defaultMessage: 'Defines the rotation of the axis labels', - }), - }, - gridlinesVisibilitySettings: { - types: ['lens_xy_gridlinesConfig'], - help: i18n.translate('xpack.lens.xyChart.gridlinesSettings.help', { - defaultMessage: 'Show x and y axes gridlines', - }), - }, - axisTitlesVisibilitySettings: { - types: ['lens_xy_axisTitlesVisibilityConfig'], - help: i18n.translate('xpack.lens.xyChart.axisTitlesSettings.help', { - defaultMessage: 'Show x and y axes titles', - }), - }, - layers: { - types: [ - 'lens_xy_data_layer', - 'lens_xy_referenceLine_layer', - 'lens_xy_annotation_layer', - // eslint-disable-next-line @typescript-eslint/no-explicit-any - ] as any, - help: 'Layers of visual series', - multi: true, - }, - curveType: { - types: ['string'], - options: ['LINEAR', 'CURVE_MONOTONE_X'], - help: i18n.translate('xpack.lens.xyChart.curveType.help', { - defaultMessage: 'Define how curve type is rendered for a line chart', - }), - }, - fillOpacity: { - types: ['number'], - help: i18n.translate('xpack.lens.xyChart.fillOpacity.help', { - defaultMessage: 'Define the area chart fill opacity', - }), - }, - hideEndzones: { - types: ['boolean'], - default: false, - help: i18n.translate('xpack.lens.xyChart.hideEndzones.help', { - defaultMessage: 'Hide endzone markers for partial data', - }), - }, - valuesInLegend: { - types: ['boolean'], - default: false, - help: i18n.translate('xpack.lens.xyChart.valuesInLegend.help', { - defaultMessage: 'Show values in legend', - }), - }, - ariaLabel: { - types: ['string'], - help: i18n.translate('xpack.lens.xyChart.ariaLabel.help', { - defaultMessage: 'Specifies the aria label of the xy chart', - }), - required: false, - }, - }, - fn(data: LensMultiTable, args: XYArgs, handlers) { - if (handlers?.inspectorAdapters?.tables) { - logDataTable(handlers.inspectorAdapters.tables, data.tables); - } - return { - type: 'render', - as: 'lens_xy_chart_renderer', - value: { - data, - args: { - ...args, - ariaLabel: - args.ariaLabel ?? - (handlers.variables?.embeddableTitle as string) ?? - handlers.getExecutionContext?.()?.description, - }, - }, - }; - }, -}; diff --git a/x-pack/plugins/lens/public/xy_visualization/annotations/config_panel/index.tsx b/x-pack/plugins/lens/public/xy_visualization/annotations/config_panel/index.tsx index 4cdb2d6c7e0b9..c143dabd2dc8e 100644 --- a/x-pack/plugins/lens/public/xy_visualization/annotations/config_panel/index.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/annotations/config_panel/index.tsx @@ -15,7 +15,7 @@ import { EventAnnotationConfig } from 'src/plugins/event_annotation/common/types import type { VisualizationDimensionEditorProps } from '../../../types'; import { State, XYState } from '../../types'; import { FormatFactory } from '../../../../common'; -import { XYAnnotationLayerConfig } from '../../../../common/expressions'; +import type { XYAnnotationLayerConfig } from '../../../../../../../src/plugins/chart_expressions/expression_xy/common'; import { ColorPicker } from '../../xy_config_panel/color_picker'; import { DimensionEditorSection, NameInput, useDebouncedValue } from '../../../shared_components'; import { isHorizontalChart } from '../../state_helpers'; diff --git a/x-pack/plugins/lens/public/xy_visualization/annotations/expression.tsx b/x-pack/plugins/lens/public/xy_visualization/annotations/expression.tsx index c36488f29d238..1fac097463857 100644 --- a/x-pack/plugins/lens/public/xy_visualization/annotations/expression.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/annotations/expression.tsx @@ -19,8 +19,8 @@ import type { EventAnnotationArgs } from 'src/plugins/event_annotation/common'; import moment from 'moment'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { defaultAnnotationColor } from '../../../../../../src/plugins/event_annotation/public'; -import type { AnnotationLayerArgs } from '../../../common/expressions'; import { hasIcon } from '../xy_config_panel/shared/icon_select'; +import { AnnotationLayerArgs } from '../../../../../../src/plugins/chart_expressions/expression_xy/common'; import { mapVerticalToHorizontalPlacement, LINES_MARKER_SIZE, diff --git a/x-pack/plugins/lens/public/xy_visualization/annotations/helpers.tsx b/x-pack/plugins/lens/public/xy_visualization/annotations/helpers.tsx index 321090c94241a..1e64741caa9cd 100644 --- a/x-pack/plugins/lens/public/xy_visualization/annotations/helpers.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/annotations/helpers.tsx @@ -12,7 +12,7 @@ import type { XYDataLayerConfig, XYAnnotationLayerConfig, XYLayerConfig, -} from '../../../common/expressions'; +} from '../../../../../../src/plugins/chart_expressions/expression_xy/common'; import type { FramePublicAPI, Visualization } from '../../types'; import { isHorizontalChart } from '../state_helpers'; import type { XYState } from '../types'; diff --git a/x-pack/plugins/lens/public/xy_visualization/annotations_helpers.tsx b/x-pack/plugins/lens/public/xy_visualization/annotations_helpers.tsx index ddbdfc91f4a3e..b00a4e9a654f2 100644 --- a/x-pack/plugins/lens/public/xy_visualization/annotations_helpers.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/annotations_helpers.tsx @@ -10,7 +10,11 @@ import React from 'react'; import { EuiFlexGroup, EuiIcon, EuiIconProps, EuiText } from '@elastic/eui'; import { Position } from '@elastic/charts'; import classnames from 'classnames'; -import type { IconPosition, YAxisMode, YConfig } from '../../common/expressions'; +import { + IconPosition, + YAxisMode, + YConfig, +} from '../../../../../src/plugins/chart_expressions/expression_xy/common'; import { hasIcon } from './xy_config_panel/shared/icon_select'; import { annotationsIconSet } from './annotations/config_panel/icon_set'; diff --git a/x-pack/plugins/lens/public/xy_visualization/axes_configuration.ts b/x-pack/plugins/lens/public/xy_visualization/axes_configuration.ts index b9b2c2ae86e42..3f3fb077baf35 100644 --- a/x-pack/plugins/lens/public/xy_visualization/axes_configuration.ts +++ b/x-pack/plugins/lens/public/xy_visualization/axes_configuration.ts @@ -6,13 +6,15 @@ */ import { FormatFactory } from '../../common'; -import { AxisExtentConfig } from '../../../../../src/plugins/chart_expressions/expression_xy/common'; +import { + AxisExtentConfig, + XYDataLayerConfig, +} from '../../../../../src/plugins/chart_expressions/expression_xy/common'; import { Datatable } from '../../../../../src/plugins/expressions/public'; import type { IFieldFormat, SerializedFieldFormat, } from '../../../../../src/plugins/field_formats/common'; -import { XYDataLayerConfig } from './types'; interface FormattedMetric { layer: string; diff --git a/x-pack/plugins/lens/public/xy_visualization/color_assignment.test.ts b/x-pack/plugins/lens/public/xy_visualization/color_assignment.test.ts index d1d03ca62376d..52e74f4ac9654 100644 --- a/x-pack/plugins/lens/public/xy_visualization/color_assignment.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/color_assignment.test.ts @@ -5,17 +5,14 @@ * 2.0. */ +import { XYDataLayerConfig } from '../../../../../src/plugins/chart_expressions/expression_xy/common'; import { getColorAssignments } from './color_assignment'; import type { FormatFactory, LensMultiTable } from '../../common'; import { layerTypes } from '../../common'; -import { XYDataLayerConfig } from './types'; describe('color_assignment', () => { const layers: XYDataLayerConfig[] = [ { - yScaleType: 'linear', - xScaleType: 'linear', - isHistogram: true, seriesType: 'bar', palette: { type: 'palette', name: 'palette1' }, layerId: '1', @@ -24,9 +21,6 @@ describe('color_assignment', () => { accessors: ['y1', 'y2'], }, { - yScaleType: 'linear', - xScaleType: 'linear', - isHistogram: true, seriesType: 'bar', palette: { type: 'palette', name: 'palette2' }, layerId: '2', diff --git a/x-pack/plugins/lens/public/xy_visualization/color_assignment.ts b/x-pack/plugins/lens/public/xy_visualization/color_assignment.ts index a60a5b7ea9d1c..71c28c3060e88 100644 --- a/x-pack/plugins/lens/public/xy_visualization/color_assignment.ts +++ b/x-pack/plugins/lens/public/xy_visualization/color_assignment.ts @@ -11,9 +11,12 @@ import type { Datatable } from 'src/plugins/expressions'; import { euiLightVars } from '@kbn/ui-theme'; import type { AccessorConfig, FramePublicAPI } from '../types'; import { getColumnToLabelMap } from './state_helpers'; -import { FormatFactory, LayerType } from '../../common'; +import { FormatFactory } from '../../common'; +import { + XYDataLayerConfig, + XYLayerConfig, +} from '../../../../../src/plugins/chart_expressions/expression_xy/common'; import { isDataLayer, isReferenceLayer, isAnnotationsLayer } from './visualization_helpers'; -import { XYDataLayerConfig, XYLayerConfig, XYReferenceLineLayerConfig } from './types'; import { getAnnotationsAccessorColorConfig } from './annotations/helpers'; import { getReferenceLineAccessorColorConfig } from './reference_line_helpers'; diff --git a/x-pack/plugins/lens/public/xy_visualization/expression.test.tsx b/x-pack/plugins/lens/public/xy_visualization/expression.test.tsx deleted file mode 100644 index 03a180cc20a08..0000000000000 --- a/x-pack/plugins/lens/public/xy_visualization/expression.test.tsx +++ /dev/null @@ -1,3167 +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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { - AreaSeries, - Axis, - BarSeries, - Position, - LineSeries, - Settings, - ScaleType, - GeometryValue, - XYChartSeriesIdentifier, - SeriesNameFn, - Fit, - HorizontalAlignment, - VerticalAlignment, - LayoutDirection, - LineAnnotation, -} from '@elastic/charts'; -import { PaletteOutput } from 'src/plugins/charts/public'; -import { calculateMinInterval, XYChart, XYChartRenderProps } from './expression'; -import type { LensMultiTable } from '../../common'; -import { layerTypes } from '../../common'; -import { AnnotationLayerArgs, xyChart } from '../../common/expressions'; -import { - dataLayerConfig, - legendConfig, - tickLabelsConfig, - gridlinesConfig, - XYArgs, - LegendConfig, - DataLayerArgs, - AxesSettingsConfig, - XYChartProps, - labelsOrientationConfig, - LabelsOrientationConfig, -} from '../../common/expressions'; -import { Datatable, DatatableRow } from '../../../../../src/plugins/expressions/public'; -import React from 'react'; -import { mount, shallow } from 'enzyme'; -import { createMockExecutionContext } from '../../../../../src/plugins/expressions/common/mocks'; -import { mountWithIntl } from '@kbn/test-jest-helpers'; -import { chartPluginMock } from '../../../../../src/plugins/charts/public/mocks'; -import { EmptyPlaceholder } from '../../../../../src/plugins/charts/public'; -import { XyEndzones } from './x_domain'; -import { eventAnnotationServiceMock } from '../../../../../src/plugins/event_annotation/public/mocks'; -import { EventAnnotationOutput } from 'src/plugins/event_annotation/common'; - -const onClickValue = jest.fn(); -const onSelectRange = jest.fn(); - -const chartSetupContract = chartPluginMock.createSetupContract(); -const chartStartContract = chartPluginMock.createStartContract(); - -const chartsThemeService = chartSetupContract.theme; -const chartsActiveCursorService = chartStartContract.activeCursor; - -const paletteService = chartPluginMock.createPaletteRegistry(); - -const mockPaletteOutput: PaletteOutput = { - type: 'palette', - name: 'mock', - params: {}, -}; - -const dateHistogramData: LensMultiTable = { - type: 'lens_multitable', - tables: { - timeLayer: { - type: 'datatable', - rows: [ - { - xAccessorId: 1585758120000, - splitAccessorId: "Men's Clothing", - yAccessorId: 1, - }, - { - xAccessorId: 1585758360000, - splitAccessorId: "Women's Accessories", - yAccessorId: 1, - }, - { - xAccessorId: 1585758360000, - splitAccessorId: "Women's Clothing", - yAccessorId: 1, - }, - { - xAccessorId: 1585759380000, - splitAccessorId: "Men's Clothing", - yAccessorId: 1, - }, - { - xAccessorId: 1585759380000, - splitAccessorId: "Men's Shoes", - yAccessorId: 1, - }, - { - xAccessorId: 1585759380000, - splitAccessorId: "Women's Clothing", - yAccessorId: 1, - }, - { - xAccessorId: 1585760700000, - splitAccessorId: "Men's Clothing", - yAccessorId: 1, - }, - { - xAccessorId: 1585760760000, - splitAccessorId: "Men's Clothing", - yAccessorId: 1, - }, - { - xAccessorId: 1585760760000, - splitAccessorId: "Men's Shoes", - yAccessorId: 1, - }, - { - xAccessorId: 1585761120000, - splitAccessorId: "Men's Shoes", - yAccessorId: 1, - }, - ], - columns: [ - { - id: 'xAccessorId', - name: 'order_date per minute', - meta: { - type: 'date', - field: 'order_date', - source: 'esaggs', - index: 'indexPatternId', - sourceParams: { - indexPatternId: 'indexPatternId', - type: 'date_histogram', - appliedTimeRange: { - from: '2020-04-01T16:14:16.246Z', - to: '2020-04-01T17:15:41.263Z', - }, - params: { - field: 'order_date', - timeRange: { from: '2020-04-01T16:14:16.246Z', to: '2020-04-01T17:15:41.263Z' }, - useNormalizedEsInterval: true, - scaleMetricValues: false, - interval: '1m', - drop_partials: false, - min_doc_count: 0, - extended_bounds: {}, - }, - }, - params: { id: 'date', params: { pattern: 'HH:mm' } }, - }, - }, - { - id: 'splitAccessorId', - name: 'Top values of category.keyword', - meta: { - type: 'string', - field: 'category.keyword', - source: 'esaggs', - index: 'indexPatternId', - sourceParams: { - indexPatternId: 'indexPatternId', - type: 'terms', - params: { - field: 'category.keyword', - orderBy: 'yAccessorId', - order: 'desc', - size: 3, - otherBucket: false, - otherBucketLabel: 'Other', - missingBucket: false, - missingBucketLabel: 'Missing', - }, - }, - params: { - id: 'terms', - params: { - id: 'string', - otherBucketLabel: 'Other', - missingBucketLabel: 'Missing', - parsedUrl: { - origin: 'http://localhost:5601', - pathname: '/jiy/app/kibana', - basePath: '/jiy', - }, - }, - }, - }, - }, - { - id: 'yAccessorId', - name: 'Count of records', - meta: { - type: 'number', - source: 'esaggs', - index: 'indexPatternId', - sourceParams: { - indexPatternId: 'indexPatternId', - params: {}, - }, - params: { id: 'number' }, - }, - }, - ], - }, - }, - dateRange: { - fromDate: new Date('2020-04-01T16:14:16.246Z'), - toDate: new Date('2020-04-01T17:15:41.263Z'), - }, -}; - -const dateHistogramLayer: DataLayerArgs = { - layerId: 'timeLayer', - layerType: layerTypes.DATA, - hide: false, - xAccessor: 'xAccessorId', - yScaleType: 'linear', - xScaleType: 'time', - isHistogram: true, - splitAccessor: 'splitAccessorId', - seriesType: 'bar_stacked', - accessors: ['yAccessorId'], - palette: mockPaletteOutput, -}; - -const createSampleDatatableWithRows = (rows: DatatableRow[]): Datatable => ({ - type: 'datatable', - columns: [ - { - id: 'a', - name: 'a', - meta: { type: 'number', params: { id: 'number', params: { pattern: '0,0.000' } } }, - }, - { - id: 'b', - name: 'b', - meta: { type: 'number', params: { id: 'number', params: { pattern: '000,0' } } }, - }, - { - id: 'c', - name: 'c', - meta: { - type: 'date', - field: 'order_date', - sourceParams: { type: 'date-histogram', params: { interval: 'auto' } }, - params: { id: 'string' }, - }, - }, - { id: 'd', name: 'ColD', meta: { type: 'string' } }, - ], - rows, -}); - -const sampleLayer: DataLayerArgs = { - layerId: 'first', - layerType: layerTypes.DATA, - seriesType: 'line', - xAccessor: 'c', - accessors: ['a', 'b'], - splitAccessor: 'd', - columnToLabel: '{"a": "Label A", "b": "Label B", "d": "Label D"}', - xScaleType: 'ordinal', - yScaleType: 'linear', - isHistogram: false, - palette: mockPaletteOutput, -}; - -const createArgsWithLayers = (layers: DataLayerArgs[] = [sampleLayer]): XYArgs => ({ - xTitle: '', - yTitle: '', - yRightTitle: '', - legend: { - type: 'lens_xy_legendConfig', - isVisible: false, - position: Position.Top, - }, - valueLabels: 'hide', - valuesInLegend: false, - axisTitlesVisibilitySettings: { - type: 'lens_xy_axisTitlesVisibilityConfig', - x: true, - yLeft: true, - yRight: true, - }, - tickLabelsVisibilitySettings: { - type: 'lens_xy_tickLabelsConfig', - x: true, - yLeft: false, - yRight: false, - }, - labelsOrientation: { - type: 'lens_xy_labelsOrientationConfig', - x: 0, - yLeft: -90, - yRight: -45, - }, - gridlinesVisibilitySettings: { - type: 'lens_xy_gridlinesConfig', - x: true, - yLeft: false, - yRight: false, - }, - yLeftExtent: { - mode: 'full', - type: 'lens_xy_axisExtentConfig', - }, - yRightExtent: { - mode: 'full', - type: 'lens_xy_axisExtentConfig', - }, - layers, -}); - -function sampleArgs() { - const data: LensMultiTable = { - type: 'lens_multitable', - tables: { - first: createSampleDatatableWithRows([ - { a: 1, b: 2, c: 'I', d: 'Foo' }, - { a: 1, b: 5, c: 'J', d: 'Bar' }, - ]), - }, - dateRange: { - fromDate: new Date('2019-01-02T05:00:00.000Z'), - toDate: new Date('2019-01-03T05:00:00.000Z'), - }, - }; - - const args: XYArgs = createArgsWithLayers(); - - return { data, args }; -} - -function sampleArgsWithReferenceLine(value: number = 150) { - const { data, args } = sampleArgs(); - - return { - data: { - ...data, - tables: { - ...data.tables, - referenceLine: { - type: 'datatable', - columns: [ - { - id: 'referenceLine-a', - meta: { params: { id: 'number' }, type: 'number' }, - name: 'Static value', - }, - ], - rows: [{ 'referenceLine-a': value }], - }, - }, - } as LensMultiTable, - args: { - ...args, - layers: [ - ...args.layers, - { - layerType: layerTypes.REFERENCELINE, - accessors: ['referenceLine-a'], - layerId: 'referenceLine', - seriesType: 'line', - xScaleType: 'linear', - yScaleType: 'linear', - palette: mockPaletteOutput, - isHistogram: false, - hide: true, - yConfig: [{ axisMode: 'left', forAccessor: 'referenceLine-a', type: 'lens_xy_yConfig' }], - }, - ], - } as XYArgs, - }; -} - -describe('xy_expression', () => { - describe('configs', () => { - test('legendConfig produces the correct arguments', () => { - const args: LegendConfig = { - isVisible: true, - position: Position.Left, - }; - - const result = legendConfig.fn(null, args, createMockExecutionContext()); - - expect(result).toEqual({ - type: 'lens_xy_legendConfig', - ...args, - }); - }); - - test('dataLayerConfig produces the correct arguments', () => { - const args: DataLayerArgs = { - layerId: 'first', - layerType: layerTypes.DATA, - seriesType: 'line', - xAccessor: 'c', - accessors: ['a', 'b'], - splitAccessor: 'd', - xScaleType: 'linear', - yScaleType: 'linear', - isHistogram: false, - palette: mockPaletteOutput, - }; - - const result = dataLayerConfig.fn(null, args, createMockExecutionContext()); - - expect(result).toEqual({ - type: 'lens_xy_data_layer', - ...args, - }); - }); - }); - - test('tickLabelsConfig produces the correct arguments', () => { - const args: AxesSettingsConfig = { - x: true, - yLeft: false, - yRight: false, - }; - - const result = tickLabelsConfig.fn(null, args, createMockExecutionContext()); - - expect(result).toEqual({ - type: 'lens_xy_tickLabelsConfig', - ...args, - }); - }); - - test('gridlinesConfig produces the correct arguments', () => { - const args: AxesSettingsConfig = { - x: true, - yLeft: false, - yRight: false, - }; - - const result = gridlinesConfig.fn(null, args, createMockExecutionContext()); - - expect(result).toEqual({ - type: 'lens_xy_gridlinesConfig', - ...args, - }); - }); - - test('labelsOrientationConfig produces the correct arguments', () => { - const args: LabelsOrientationConfig = { - x: 0, - yLeft: -90, - yRight: -45, - }; - - const result = labelsOrientationConfig.fn(null, args, createMockExecutionContext()); - - expect(result).toEqual({ - type: 'lens_xy_labelsOrientationConfig', - ...args, - }); - }); - - describe('xyChart', () => { - test('it renders with the specified data and args', () => { - const { data, args } = sampleArgs(); - const result = xyChart.fn(data, args, createMockExecutionContext()); - - expect(result).toEqual({ - type: 'render', - as: 'lens_xy_chart_renderer', - value: { data, args }, - }); - }); - }); - - describe('XYChart component', () => { - let getFormatSpy: jest.Mock; - let convertSpy: jest.Mock; - let defaultProps: Omit; - - const dataWithoutFormats: LensMultiTable = { - type: 'lens_multitable', - tables: { - first: { - type: 'datatable', - columns: [ - { id: 'a', name: 'a', meta: { type: 'number' } }, - { id: 'b', name: 'b', meta: { type: 'number' } }, - { id: 'c', name: 'c', meta: { type: 'string' } }, - { id: 'd', name: 'd', meta: { type: 'string' } }, - ], - rows: [ - { a: 1, b: 2, c: 'I', d: 'Row 1' }, - { a: 1, b: 5, c: 'J', d: 'Row 2' }, - ], - }, - }, - }; - const dataWithFormats: LensMultiTable = { - type: 'lens_multitable', - tables: { - first: { - type: 'datatable', - columns: [ - { id: 'a', name: 'a', meta: { type: 'number' } }, - { id: 'b', name: 'b', meta: { type: 'number' } }, - { id: 'c', name: 'c', meta: { type: 'string' } }, - { id: 'd', name: 'd', meta: { type: 'string', params: { id: 'custom' } } }, - ], - rows: [ - { a: 1, b: 2, c: 'I', d: 'Row 1' }, - { a: 1, b: 5, c: 'J', d: 'Row 2' }, - ], - }, - }, - }; - - const getRenderedComponent = (data: LensMultiTable, args: XYArgs) => { - return shallow(); - }; - - beforeEach(() => { - convertSpy = jest.fn((x) => x); - getFormatSpy = jest.fn(); - getFormatSpy.mockReturnValue({ convert: convertSpy }); - - defaultProps = { - formatFactory: getFormatSpy, - timeZone: 'UTC', - renderMode: 'view', - chartsThemeService, - chartsActiveCursorService, - paletteService, - minInterval: 50, - onClickValue, - onSelectRange, - syncColors: false, - useLegacyTimeAxis: false, - eventAnnotationService: eventAnnotationServiceMock, - }; - }); - - test('it renders line', () => { - const { data, args } = sampleArgs(); - - const component = shallow( - - ); - expect(component).toMatchSnapshot(); - expect(component.find(LineSeries)).toHaveLength(2); - expect(component.find(LineSeries).at(0).prop('yAccessors')).toEqual(['a']); - expect(component.find(LineSeries).at(1).prop('yAccessors')).toEqual(['b']); - }); - - describe('date range', () => { - const timeSampleLayer: DataLayerArgs = { - layerId: 'first', - layerType: layerTypes.DATA, - seriesType: 'line', - xAccessor: 'c', - accessors: ['a', 'b'], - splitAccessor: 'd', - columnToLabel: '{"a": "Label A", "b": "Label B", "d": "Label D"}', - xScaleType: 'time', - yScaleType: 'linear', - isHistogram: false, - palette: mockPaletteOutput, - }; - const multiLayerArgs = createArgsWithLayers([ - timeSampleLayer, - { - ...timeSampleLayer, - layerId: 'second', - seriesType: 'bar', - xScaleType: 'time', - }, - ]); - test('it uses the full date range', () => { - const { data, args } = sampleArgs(); - - const component = shallow( - - c.id !== 'c' - ? c - : { - ...c, - meta: { - type: 'date', - source: 'esaggs', - sourceParams: { - type: 'date_histogram', - params: {}, - appliedTimeRange: { - from: '2019-01-02T05:00:00.000Z', - to: '2019-01-03T05:00:00.000Z', - }, - }, - }, - } - ), - }, - }, - }} - args={{ - ...args, - layers: [ - { ...(args.layers[0] as DataLayerArgs), seriesType: 'line', xScaleType: 'time' }, - ], - }} - minInterval={undefined} - /> - ); - expect(component.find(Settings).prop('xDomain')).toMatchInlineSnapshot(` - Object { - "max": 1546491600000, - "min": 1546405200000, - "minInterval": undefined, - } - `); - }); - - test('it uses passed in minInterval', () => { - const data: LensMultiTable = { - type: 'lens_multitable', - tables: { - first: createSampleDatatableWithRows([{ a: 1, b: 2, c: 'I', d: 'Foo' }]), - second: createSampleDatatableWithRows([]), - }, - }; - - const component = shallow(); - - // real auto interval is 30mins = 1800000 - expect(component.find(Settings).prop('xDomain')).toMatchInlineSnapshot(` - Object { - "max": NaN, - "min": NaN, - "minInterval": 50, - } - `); - }); - - describe('axis time', () => { - const defaultTimeLayer: DataLayerArgs = { - layerId: 'first', - layerType: layerTypes.DATA, - seriesType: 'line', - xAccessor: 'c', - accessors: ['a', 'b'], - splitAccessor: 'd', - columnToLabel: '{"a": "Label A", "b": "Label B", "d": "Label D"}', - xScaleType: 'time', - yScaleType: 'linear', - isHistogram: true, - palette: mockPaletteOutput, - }; - test('it should disable the new time axis for a line time layer when isHistogram is set to false', () => { - const { data } = sampleArgs(); - - const instance = shallow( - - ); - - const axisStyle = instance.find(Axis).first().prop('timeAxisLayerCount'); - - expect(axisStyle).toBe(0); - }); - test('it should enable the new time axis for a line time layer when isHistogram is set to true', () => { - const { data } = sampleArgs(); - const timeLayerArgs = createArgsWithLayers([defaultTimeLayer]); - - const instance = shallow( - - ); - - const axisStyle = instance.find(Axis).first().prop('timeAxisLayerCount'); - - expect(axisStyle).toBe(3); - }); - test('it should disable the new time axis for a vertical bar with break down dimension', () => { - const { data } = sampleArgs(); - const timeLayer: DataLayerArgs = { - ...defaultTimeLayer, - seriesType: 'bar', - }; - const timeLayerArgs = createArgsWithLayers([timeLayer]); - - const instance = shallow( - - ); - - const axisStyle = instance.find(Axis).first().prop('timeAxisLayerCount'); - - expect(axisStyle).toBe(0); - }); - - test('it should enable the new time axis for a stacked vertical bar with break down dimension', () => { - const { data } = sampleArgs(); - const timeLayer: DataLayerArgs = { - ...defaultTimeLayer, - seriesType: 'bar_stacked', - }; - const timeLayerArgs = createArgsWithLayers([timeLayer]); - - const instance = shallow( - - ); - - const axisStyle = instance.find(Axis).first().prop('timeAxisLayerCount'); - - expect(axisStyle).toBe(3); - }); - }); - describe('endzones', () => { - const { args } = sampleArgs(); - const table = createSampleDatatableWithRows([ - { a: 1, b: 2, c: new Date('2021-04-22').valueOf(), d: 'Foo' }, - { a: 1, b: 2, c: new Date('2021-04-23').valueOf(), d: 'Foo' }, - { a: 1, b: 2, c: new Date('2021-04-24').valueOf(), d: 'Foo' }, - ]); - const data: LensMultiTable = { - type: 'lens_multitable', - tables: { - first: { - ...table, - columns: table.columns.map((c) => - c.id !== 'c' - ? c - : { - ...c, - meta: { - type: 'date', - source: 'esaggs', - sourceParams: { - type: 'date_histogram', - params: {}, - appliedTimeRange: { - from: '2021-04-22T12:00:00.000Z', - to: '2021-04-24T12:00:00.000Z', - }, - }, - }, - } - ), - }, - }, - dateRange: { - // first and last bucket are partial - fromDate: new Date('2021-04-22T12:00:00.000Z'), - toDate: new Date('2021-04-24T12:00:00.000Z'), - }, - }; - const timeArgs: XYArgs = { - ...args, - layers: [ - { - ...(args.layers[0] as DataLayerArgs), - seriesType: 'line', - xScaleType: 'time', - isHistogram: true, - splitAccessor: undefined, - }, - ], - }; - - test('it extends interval if data is exceeding it', () => { - const component = shallow( - - ); - - expect(component.find(Settings).prop('xDomain')).toEqual({ - // shortened to 24th midnight (elastic-charts automatically adds one min interval) - max: new Date('2021-04-24').valueOf(), - // extended to 22nd midnight because of first bucket - min: new Date('2021-04-22').valueOf(), - minInterval: 24 * 60 * 60 * 1000, - }); - }); - - test('it renders endzone component bridging gap between domain and extended domain', () => { - const component = shallow( - - ); - - expect(component.find(XyEndzones).dive().find('Endzones').props()).toEqual( - expect.objectContaining({ - domainStart: new Date('2021-04-22T12:00:00.000Z').valueOf(), - domainEnd: new Date('2021-04-24T12:00:00.000Z').valueOf(), - domainMin: new Date('2021-04-22').valueOf(), - domainMax: new Date('2021-04-24').valueOf(), - }) - ); - }); - - test('should pass enabled histogram mode and min interval to endzones component', () => { - const component = shallow( - - ); - - expect(component.find(XyEndzones).dive().find('Endzones').props()).toEqual( - expect.objectContaining({ - interval: 24 * 60 * 60 * 1000, - isFullBin: false, - }) - ); - }); - - test('should pass disabled histogram mode and min interval to endzones component', () => { - const component = shallow( - - ); - - expect(component.find(XyEndzones).dive().find('Endzones').props()).toEqual( - expect.objectContaining({ - interval: 24 * 60 * 60 * 1000, - isFullBin: true, - }) - ); - }); - - test('it does not render endzones if disabled via settings', () => { - const component = shallow( - - ); - - expect(component.find(XyEndzones).length).toEqual(0); - }); - }); - }); - - describe('y axis extents', () => { - test('it passes custom y axis extents to elastic-charts axis spec', () => { - const { data, args } = sampleArgs(); - - const component = shallow( - - ); - expect(component.find(Axis).find('[id="left"]').prop('domain')).toEqual({ - fit: false, - min: 123, - max: 456, - }); - }); - - test('it passes fit to bounds y axis extents to elastic-charts axis spec', () => { - const { data, args } = sampleArgs(); - - const component = shallow( - - ); - expect(component.find(Axis).find('[id="left"]').prop('domain')).toEqual({ - fit: true, - min: NaN, - max: NaN, - }); - }); - - test('it does not allow fit for area chart', () => { - const { data, args } = sampleArgs(); - - const component = shallow( - - ); - expect(component.find(Axis).find('[id="left"]').prop('domain')).toEqual({ - fit: false, - min: NaN, - max: NaN, - }); - }); - - test('it does not allow positive lower bound for bar', () => { - const { data, args } = sampleArgs(); - - const component = shallow( - - ); - expect(component.find(Axis).find('[id="left"]').prop('domain')).toEqual({ - fit: false, - min: NaN, - max: NaN, - }); - }); - - test('it does include referenceLine values when in full extent mode', () => { - const { data, args } = sampleArgsWithReferenceLine(); - - const component = shallow(); - expect(component.find(Axis).find('[id="left"]').prop('domain')).toEqual({ - fit: false, - min: 0, - max: 150, - }); - }); - - test('it should ignore referenceLine values when set to custom extents', () => { - const { data, args } = sampleArgsWithReferenceLine(); - - const component = shallow( - - ); - expect(component.find(Axis).find('[id="left"]').prop('domain')).toEqual({ - fit: false, - min: 123, - max: 456, - }); - }); - - test('it should work for negative values in referenceLines', () => { - const { data, args } = sampleArgsWithReferenceLine(-150); - - const component = shallow(); - expect(component.find(Axis).find('[id="left"]').prop('domain')).toEqual({ - fit: false, - min: -150, - max: 5, - }); - }); - }); - - test('it has xDomain undefined if the x is not a time scale or a histogram', () => { - const { data, args } = sampleArgs(); - - const component = shallow( - - ); - const xDomain = component.find(Settings).prop('xDomain'); - expect(xDomain).toEqual(undefined); - }); - - test('it uses min interval if interval is passed in and visualization is histogram', () => { - const { data, args } = sampleArgs(); - - const component = shallow( - - ); - expect(component.find(Settings).prop('xDomain')).toEqual({ - minInterval: 101, - min: NaN, - max: NaN, - }); - }); - - test('disabled legend extra by default', () => { - const { data, args } = sampleArgs(); - const component = shallow(); - expect(component.find(Settings).at(0).prop('showLegendExtra')).toEqual(false); - }); - - test('ignores legend extra for ordinal chart', () => { - const { data, args } = sampleArgs(); - const component = shallow( - - ); - expect(component.find(Settings).at(0).prop('showLegendExtra')).toEqual(false); - }); - - test('shows legend extra for histogram chart', () => { - const { args } = sampleArgs(); - const component = shallow( - - ); - expect(component.find(Settings).at(0).prop('showLegendExtra')).toEqual(true); - }); - - test('it renders bar', () => { - const { data, args } = sampleArgs(); - const component = shallow( - - ); - expect(component).toMatchSnapshot(); - expect(component.find(BarSeries)).toHaveLength(2); - expect(component.find(BarSeries).at(0).prop('yAccessors')).toEqual(['a']); - expect(component.find(BarSeries).at(1).prop('yAccessors')).toEqual(['b']); - }); - - test('it renders area', () => { - const { data, args } = sampleArgs(); - const component = shallow( - - ); - expect(component).toMatchSnapshot(); - expect(component.find(AreaSeries)).toHaveLength(2); - expect(component.find(AreaSeries).at(0).prop('yAccessors')).toEqual(['a']); - expect(component.find(AreaSeries).at(1).prop('yAccessors')).toEqual(['b']); - }); - - test('it renders horizontal bar', () => { - const { data, args } = sampleArgs(); - const component = shallow( - - ); - expect(component).toMatchSnapshot(); - expect(component.find(BarSeries)).toHaveLength(2); - expect(component.find(BarSeries).at(0).prop('yAccessors')).toEqual(['a']); - expect(component.find(BarSeries).at(1).prop('yAccessors')).toEqual(['b']); - expect(component.find(Settings).prop('rotation')).toEqual(90); - }); - - test('it renders regular bar empty placeholder for no results', () => { - const { data, args } = sampleArgs(); - - // send empty data to the chart - data.tables.first.rows = []; - - const component = shallow(); - - expect(component.find(BarSeries)).toHaveLength(0); - expect(component.find(EmptyPlaceholder).prop('icon')).toBeDefined(); - }); - - test('onBrushEnd returns correct context data for date histogram data', () => { - const { args } = sampleArgs(); - - const wrapper = mountWithIntl( - - ); - wrapper.find(Settings).first().prop('onBrushEnd')!({ x: [1585757732783, 1585758880838] }); - - expect(onSelectRange).toHaveBeenCalledWith({ - column: 0, - table: dateHistogramData.tables.timeLayer, - range: [1585757732783, 1585758880838], - }); - }); - - test('onBrushEnd returns correct context data for number histogram data', () => { - const { args } = sampleArgs(); - - const numberLayer: DataLayerArgs = { - layerId: 'numberLayer', - layerType: layerTypes.DATA, - hide: false, - xAccessor: 'xAccessorId', - yScaleType: 'linear', - xScaleType: 'linear', - isHistogram: true, - seriesType: 'bar_stacked', - accessors: ['yAccessorId'], - palette: mockPaletteOutput, - }; - - const numberHistogramData: LensMultiTable = { - type: 'lens_multitable', - tables: { - numberLayer: { - type: 'datatable', - rows: [ - { - xAccessorId: 5, - yAccessorId: 1, - }, - { - xAccessorId: 7, - yAccessorId: 1, - }, - { - xAccessorId: 8, - yAccessorId: 1, - }, - { - xAccessorId: 10, - yAccessorId: 1, - }, - ], - columns: [ - { - id: 'xAccessorId', - name: 'bytes', - meta: { type: 'number' }, - }, - { - id: 'yAccessorId', - name: 'Count of records', - meta: { type: 'number' }, - }, - ], - }, - }, - dateRange: { - fromDate: new Date('2020-04-01T16:14:16.246Z'), - toDate: new Date('2020-04-01T17:15:41.263Z'), - }, - }; - - const wrapper = mountWithIntl( - - ); - - wrapper.find(Settings).first().prop('onBrushEnd')!({ x: [5, 8] }); - - expect(onSelectRange).toHaveBeenCalledWith({ - column: 0, - table: numberHistogramData.tables.numberLayer, - range: [5, 8], - }); - }); - - test('onBrushEnd is not set on non-interactive mode', () => { - const { args, data } = sampleArgs(); - - const wrapper = mountWithIntl( - - ); - - expect(wrapper.find(Settings).first().prop('onBrushEnd')).toBeUndefined(); - }); - - test('allowBrushingLastHistogramBin is true for date histogram data', () => { - const { args } = sampleArgs(); - - const wrapper = mountWithIntl( - - ); - expect(wrapper.find(Settings).at(0).prop('allowBrushingLastHistogramBin')).toEqual(true); - }); - - test('onElementClick returns correct context data', () => { - const geometry: GeometryValue = { x: 5, y: 1, accessor: 'y1', mark: null, datum: {} }; - const series = { - key: 'spec{d}yAccessor{d}splitAccessors{b-2}', - specId: 'd', - yAccessor: 'd', - splitAccessors: {}, - seriesKeys: [2, 'd'], - }; - - const { args, data } = sampleArgs(); - - const wrapper = mountWithIntl( - - ); - - wrapper.find(Settings).first().prop('onElementClick')!([ - [geometry, series as XYChartSeriesIdentifier], - ]); - - expect(onClickValue).toHaveBeenCalledWith({ - data: [ - { - column: 1, - row: 1, - table: data.tables.first, - value: 5, - }, - { - column: 1, - row: 0, - table: data.tables.first, - value: 2, - }, - ], - }); - }); - - test('onElementClick returns correct context data for date histogram', () => { - const geometry: GeometryValue = { - x: 1585758120000, - y: 1, - accessor: 'y1', - mark: null, - datum: {}, - }; - const series = { - key: 'spec{d}yAccessor{d}splitAccessors{b-2}', - specId: 'd', - yAccessor: 'yAccessorId', - splitAccessors: {}, - seriesKeys: ['yAccessorId'], - }; - - const { args } = sampleArgs(); - - const wrapper = mountWithIntl( - - ); - - wrapper.find(Settings).first().prop('onElementClick')!([ - [geometry, series as XYChartSeriesIdentifier], - ]); - - expect(onClickValue).toHaveBeenCalledWith({ - data: [ - { - column: 0, - row: 0, - table: dateHistogramData.tables.timeLayer, - value: 1585758120000, - }, - ], - }); - }); - - test('onElementClick returns correct context data for numeric histogram', () => { - const { args } = sampleArgs(); - - const numberLayer: DataLayerArgs = { - layerId: 'numberLayer', - layerType: layerTypes.DATA, - hide: false, - xAccessor: 'xAccessorId', - yScaleType: 'linear', - xScaleType: 'linear', - isHistogram: true, - seriesType: 'bar_stacked', - accessors: ['yAccessorId'], - palette: mockPaletteOutput, - }; - - const numberHistogramData: LensMultiTable = { - type: 'lens_multitable', - tables: { - numberLayer: { - type: 'datatable', - rows: [ - { - xAccessorId: 5, - yAccessorId: 1, - }, - { - xAccessorId: 7, - yAccessorId: 1, - }, - { - xAccessorId: 8, - yAccessorId: 1, - }, - { - xAccessorId: 10, - yAccessorId: 1, - }, - ], - columns: [ - { - id: 'xAccessorId', - name: 'bytes', - meta: { type: 'number' }, - }, - { - id: 'yAccessorId', - name: 'Count of records', - meta: { type: 'number' }, - }, - ], - }, - }, - dateRange: { - fromDate: new Date('2020-04-01T16:14:16.246Z'), - toDate: new Date('2020-04-01T17:15:41.263Z'), - }, - }; - const geometry: GeometryValue = { - x: 5, - y: 1, - accessor: 'y1', - mark: null, - datum: {}, - }; - const series = { - key: 'spec{d}yAccessor{d}splitAccessors{b-2}', - specId: 'd', - yAccessor: 'yAccessorId', - splitAccessors: {}, - seriesKeys: ['yAccessorId'], - }; - - const wrapper = mountWithIntl( - - ); - - wrapper.find(Settings).first().prop('onElementClick')!([ - [geometry, series as XYChartSeriesIdentifier], - ]); - - expect(onClickValue).toHaveBeenCalledWith({ - data: [ - { - column: 0, - row: 0, - table: numberHistogramData.tables.numberLayer, - value: 5, - }, - ], - timeFieldName: undefined, - }); - }); - - test('returns correct original data for ordinal x axis with special formatter', () => { - const geometry: GeometryValue = { x: 'BAR', y: 1, accessor: 'y1', mark: null, datum: {} }; - const series = { - key: 'spec{d}yAccessor{d}splitAccessors{b-2}', - specId: 'd', - yAccessor: 'a', - splitAccessors: {}, - seriesKeys: ['a'], - }; - - const { args, data } = sampleArgs(); - - convertSpy.mockImplementation((x) => (typeof x === 'string' ? x.toUpperCase() : x)); - - const wrapper = mountWithIntl( - - ); - - wrapper.find(Settings).first().prop('onElementClick')!([ - [geometry, series as XYChartSeriesIdentifier], - ]); - - expect(onClickValue).toHaveBeenCalledWith({ - data: [ - { - column: 3, - row: 1, - table: data.tables.first, - value: 'Bar', - }, - ], - }); - }); - - test('sets up correct yScaleType equal to binary_linear for bytes formatting', () => { - const { args, data } = sampleArgs(); - data.tables.first.columns[0].meta = { - type: 'number', - params: { id: 'bytes', params: { pattern: '0,0.00b' } }, - }; - - const wrapper = mountWithIntl( - - ); - - expect(wrapper.find(LineSeries).at(0).prop('yScaleType')).toEqual('linear_binary'); - }); - - test('allowBrushingLastHistogramBin should be fakse for ordinal data', () => { - const { args, data } = sampleArgs(); - - const wrapper = mountWithIntl( - - ); - - expect(wrapper.find(Settings).at(0).prop('allowBrushingLastHistogramBin')).toEqual(false); - }); - - test('onElementClick is not triggering event on non-interactive mode', () => { - const { args, data } = sampleArgs(); - - const wrapper = mountWithIntl( - - ); - - expect(wrapper.find(Settings).first().prop('onElementClick')).toBeUndefined(); - }); - - test('legendAction is not triggering event on non-interactive mode', () => { - const { args, data } = sampleArgs(); - - const wrapper = mountWithIntl( - - ); - - expect(wrapper.find(Settings).first().prop('legendAction')).toBeUndefined(); - }); - - test('it renders stacked bar', () => { - const { data, args } = sampleArgs(); - const component = shallow( - - ); - expect(component).toMatchSnapshot(); - expect(component.find(BarSeries)).toHaveLength(2); - expect(component.find(BarSeries).at(0).prop('stackAccessors')).toHaveLength(1); - expect(component.find(BarSeries).at(1).prop('stackAccessors')).toHaveLength(1); - }); - - test('it renders stacked area', () => { - const { data, args } = sampleArgs(); - const component = shallow( - - ); - expect(component).toMatchSnapshot(); - expect(component.find(AreaSeries)).toHaveLength(2); - expect(component.find(AreaSeries).at(0).prop('stackAccessors')).toHaveLength(1); - expect(component.find(AreaSeries).at(1).prop('stackAccessors')).toHaveLength(1); - }); - - test('it renders stacked horizontal bar', () => { - const { data, args } = sampleArgs(); - const component = shallow( - - ); - expect(component).toMatchSnapshot(); - expect(component.find(BarSeries)).toHaveLength(2); - expect(component.find(BarSeries).at(0).prop('stackAccessors')).toHaveLength(1); - expect(component.find(BarSeries).at(1).prop('stackAccessors')).toHaveLength(1); - expect(component.find(Settings).prop('rotation')).toEqual(90); - }); - - test('it renders stacked bar empty placeholder for no results', () => { - const { data, args } = sampleArgs(); - - const component = shallow( - - ); - - expect(component.find(BarSeries)).toHaveLength(0); - expect(component.find(EmptyPlaceholder).prop('icon')).toBeDefined(); - }); - - test('it passes time zone to the series', () => { - const { data, args } = sampleArgs(); - const component = shallow( - - ); - expect(component.find(LineSeries).at(0).prop('timeZone')).toEqual('CEST'); - expect(component.find(LineSeries).at(1).prop('timeZone')).toEqual('CEST'); - }); - - test('it applies histogram mode to the series for single series', () => { - const { data, args } = sampleArgs(); - const firstLayer: DataLayerArgs = { - ...args.layers[0], - accessors: ['b'], - seriesType: 'bar', - isHistogram: true, - } as DataLayerArgs; - delete firstLayer.splitAccessor; - const component = shallow( - - ); - expect(component.find(BarSeries).at(0).prop('enableHistogramMode')).toEqual(true); - }); - - test('it does not apply histogram mode to more than one bar series for unstacked bar chart', () => { - const { data, args } = sampleArgs(); - const firstLayer: DataLayerArgs = { - ...args.layers[0], - seriesType: 'bar', - isHistogram: true, - } as DataLayerArgs; - delete firstLayer.splitAccessor; - const component = shallow( - - ); - expect(component.find(BarSeries).at(0).prop('enableHistogramMode')).toEqual(false); - expect(component.find(BarSeries).at(1).prop('enableHistogramMode')).toEqual(false); - }); - - test('it applies histogram mode to more than one the series for unstacked line/area chart', () => { - const { data, args } = sampleArgs(); - const firstLayer: DataLayerArgs = { - ...args.layers[0], - seriesType: 'line', - isHistogram: true, - } as DataLayerArgs; - delete firstLayer.splitAccessor; - const secondLayer: DataLayerArgs = { - ...args.layers[0], - seriesType: 'line', - isHistogram: true, - } as DataLayerArgs; - delete secondLayer.splitAccessor; - const component = shallow( - - ); - expect(component.find(LineSeries).at(0).prop('enableHistogramMode')).toEqual(true); - expect(component.find(LineSeries).at(1).prop('enableHistogramMode')).toEqual(true); - }); - - test('it applies histogram mode to the series for stacked series', () => { - const { data, args } = sampleArgs(); - const component = shallow( - - ); - expect(component.find(BarSeries).at(0).prop('enableHistogramMode')).toEqual(true); - expect(component.find(BarSeries).at(1).prop('enableHistogramMode')).toEqual(true); - }); - - test('it does not apply histogram mode for splitted series', () => { - const { data, args } = sampleArgs(); - const component = shallow( - - ); - expect(component.find(BarSeries).at(0).prop('enableHistogramMode')).toEqual(false); - expect(component.find(BarSeries).at(1).prop('enableHistogramMode')).toEqual(false); - }); - - describe('y axes', () => { - test('single axis if possible', () => { - const args = createArgsWithLayers(); - - const component = getRenderedComponent(dataWithoutFormats, args); - const axes = component.find(Axis); - expect(axes).toHaveLength(2); - }); - - test('multiple axes because of config', () => { - const args = createArgsWithLayers(); - const newArgs = { - ...args, - layers: [ - { - ...args.layers[0], - accessors: ['a', 'b'], - yConfig: [ - { - forAccessor: 'a', - axisMode: 'left', - }, - { - forAccessor: 'b', - axisMode: 'right', - }, - ], - }, - ], - } as XYArgs; - - const component = getRenderedComponent(dataWithoutFormats, newArgs); - const axes = component.find(Axis); - expect(axes).toHaveLength(3); - expect(component.find(LineSeries).at(0).prop('groupId')).toEqual( - axes.at(1).prop('groupId') - ); - expect(component.find(LineSeries).at(1).prop('groupId')).toEqual( - axes.at(2).prop('groupId') - ); - }); - - test('multiple axes because of incompatible formatters', () => { - const args = createArgsWithLayers(); - const newArgs = { - ...args, - layers: [ - { - ...args.layers[0], - accessors: ['c', 'd'], - }, - ], - } as XYArgs; - - const component = getRenderedComponent(dataWithFormats, newArgs); - const axes = component.find(Axis); - expect(axes).toHaveLength(3); - expect(component.find(LineSeries).at(0).prop('groupId')).toEqual( - axes.at(1).prop('groupId') - ); - expect(component.find(LineSeries).at(1).prop('groupId')).toEqual( - axes.at(2).prop('groupId') - ); - }); - - test('single axis despite different formatters if enforced', () => { - const args = createArgsWithLayers(); - const newArgs = { - ...args, - layers: [ - { - ...args.layers[0], - accessors: ['c', 'd'], - yConfig: [ - { - forAccessor: 'c', - axisMode: 'left', - }, - { - forAccessor: 'd', - axisMode: 'left', - }, - ], - }, - ], - } as XYArgs; - - const component = getRenderedComponent(dataWithoutFormats, newArgs); - const axes = component.find(Axis); - expect(axes).toHaveLength(2); - }); - }); - - describe('y series coloring', () => { - test('color is applied to chart for multiple series', () => { - const args = createArgsWithLayers(); - const newArgs = { - ...args, - layers: [ - { - ...args.layers[0], - splitAccessor: undefined, - accessors: ['a', 'b'], - yConfig: [ - { - forAccessor: 'a', - color: '#550000', - }, - { - forAccessor: 'b', - color: '#FFFF00', - }, - ], - }, - { - ...args.layers[0], - splitAccessor: undefined, - accessors: ['c'], - yConfig: [ - { - forAccessor: 'c', - color: '#FEECDF', - }, - ], - }, - ], - } as XYArgs; - - const component = getRenderedComponent(dataWithoutFormats, newArgs); - expect( - (component.find(LineSeries).at(0).prop('color') as Function)!({ - yAccessor: 'a', - seriesKeys: ['a'], - }) - ).toEqual('#550000'); - expect( - (component.find(LineSeries).at(1).prop('color') as Function)!({ - yAccessor: 'b', - seriesKeys: ['b'], - }) - ).toEqual('#FFFF00'); - expect( - (component.find(LineSeries).at(2).prop('color') as Function)!({ - yAccessor: 'c', - seriesKeys: ['c'], - }) - ).toEqual('#FEECDF'); - }); - test('color is not applied to chart when splitAccessor is defined or when yConfig is not configured', () => { - const args = createArgsWithLayers(); - const newArgs = { - ...args, - layers: [ - { - ...args.layers[0], - accessors: ['a'], - yConfig: [ - { - forAccessor: 'a', - color: '#550000', - }, - ], - }, - { - ...args.layers[0], - splitAccessor: undefined, - accessors: ['c'], - }, - ], - } as XYArgs; - - const component = getRenderedComponent(dataWithoutFormats, newArgs); - expect( - (component.find(LineSeries).at(0).prop('color') as Function)!({ - yAccessor: 'a', - seriesKeys: ['a'], - }) - ).toEqual('blue'); - expect( - (component.find(LineSeries).at(1).prop('color') as Function)!({ - yAccessor: 'c', - seriesKeys: ['c'], - }) - ).toEqual('blue'); - }); - }); - - describe('provides correct series naming', () => { - const nameFnArgs = { - seriesKeys: [], - key: '', - specId: 'a', - yAccessor: '', - splitAccessors: new Map(), - }; - - test('simplest xy chart without human-readable name', () => { - const args = createArgsWithLayers(); - const newArgs = { - ...args, - layers: [ - { - ...args.layers[0], - accessors: ['a'], - splitAccessor: undefined, - columnToLabel: '', - }, - ], - }; - - const component = getRenderedComponent(dataWithoutFormats, newArgs); - const nameFn = component.find(LineSeries).prop('name') as SeriesNameFn; - - // In this case, the ID is used as the name. This shouldn't happen in practice - expect(nameFn({ ...nameFnArgs, seriesKeys: ['a'] }, false)).toEqual(''); - expect(nameFn({ ...nameFnArgs, seriesKeys: ['nonsense'] }, false)).toEqual(''); - }); - - test('simplest xy chart with empty name', () => { - const args = createArgsWithLayers(); - const newArgs = { - ...args, - layers: [ - { - ...args.layers[0], - accessors: ['a'], - splitAccessor: undefined, - columnToLabel: '{"a":""}', - }, - ], - }; - - const component = getRenderedComponent(dataWithoutFormats, newArgs); - const nameFn = component.find(LineSeries).prop('name') as SeriesNameFn; - - // In this case, the ID is used as the name. This shouldn't happen in practice - expect(nameFn({ ...nameFnArgs, seriesKeys: ['a'] }, false)).toEqual(''); - expect(nameFn({ ...nameFnArgs, seriesKeys: ['nonsense'] }, false)).toEqual(''); - }); - - test('simplest xy chart with human-readable name', () => { - const args = createArgsWithLayers(); - const newArgs = { - ...args, - layers: [ - { - ...args.layers[0], - accessors: ['a'], - splitAccessor: undefined, - columnToLabel: '{"a":"Column A"}', - }, - ], - }; - - const component = getRenderedComponent(dataWithoutFormats, newArgs); - const nameFn = component.find(LineSeries).prop('name') as SeriesNameFn; - - expect(nameFn({ ...nameFnArgs, seriesKeys: ['a'] }, false)).toEqual('Column A'); - }); - - test('multiple y accessors', () => { - const args = createArgsWithLayers(); - const newArgs = { - ...args, - layers: [ - { - ...args.layers[0], - accessors: ['a', 'b'], - splitAccessor: undefined, - columnToLabel: '{"a": "Label A"}', - }, - ], - }; - - const component = getRenderedComponent(dataWithoutFormats, newArgs); - const nameFn1 = component.find(LineSeries).at(0).prop('name') as SeriesNameFn; - const nameFn2 = component.find(LineSeries).at(1).prop('name') as SeriesNameFn; - - // This accessor has a human-readable name - expect(nameFn1({ ...nameFnArgs, seriesKeys: ['a'] }, false)).toEqual('Label A'); - // This accessor does not - expect(nameFn2({ ...nameFnArgs, seriesKeys: ['b'] }, false)).toEqual(''); - expect(nameFn1({ ...nameFnArgs, seriesKeys: ['nonsense'] }, false)).toEqual(''); - }); - - test('split series without formatting and single y accessor', () => { - const args = createArgsWithLayers(); - const newArgs = { - ...args, - layers: [ - { - ...args.layers[0], - accessors: ['a'], - splitAccessor: 'd', - columnToLabel: '{"a": "Label A"}', - }, - ], - }; - - const component = getRenderedComponent(dataWithoutFormats, newArgs); - const nameFn = component.find(LineSeries).prop('name') as SeriesNameFn; - - expect(nameFn({ ...nameFnArgs, seriesKeys: ['split1', 'a'] }, false)).toEqual('split1'); - }); - - test('split series with formatting and single y accessor', () => { - const args = createArgsWithLayers(); - const newArgs = { - ...args, - layers: [ - { - ...args.layers[0], - accessors: ['a'], - splitAccessor: 'd', - columnToLabel: '{"a": "Label A"}', - }, - ], - }; - - const component = getRenderedComponent(dataWithFormats, newArgs); - const nameFn = component.find(LineSeries).prop('name') as SeriesNameFn; - - convertSpy.mockReturnValueOnce('formatted'); - expect(nameFn({ ...nameFnArgs, seriesKeys: ['split1', 'a'] }, false)).toEqual('formatted'); - expect(getFormatSpy).toHaveBeenCalledWith({ id: 'custom' }); - }); - - test('split series without formatting with multiple y accessors', () => { - const args = createArgsWithLayers(); - const newArgs = { - ...args, - layers: [ - { - ...args.layers[0], - accessors: ['a', 'b'], - splitAccessor: 'd', - columnToLabel: '{"a": "Label A","b": "Label B"}', - }, - ], - }; - - const component = getRenderedComponent(dataWithoutFormats, newArgs); - const nameFn1 = component.find(LineSeries).at(0).prop('name') as SeriesNameFn; - const nameFn2 = component.find(LineSeries).at(0).prop('name') as SeriesNameFn; - - expect(nameFn1({ ...nameFnArgs, seriesKeys: ['split1', 'a'] }, false)).toEqual( - 'split1 - Label A' - ); - expect(nameFn2({ ...nameFnArgs, seriesKeys: ['split1', 'b'] }, false)).toEqual( - 'split1 - Label B' - ); - }); - - test('split series with formatting with multiple y accessors', () => { - const args = createArgsWithLayers(); - const newArgs = { - ...args, - layers: [ - { - ...args.layers[0], - accessors: ['a', 'b'], - splitAccessor: 'd', - columnToLabel: '{"a": "Label A","b": "Label B"}', - }, - ], - }; - - const component = getRenderedComponent(dataWithFormats, newArgs); - const nameFn1 = component.find(LineSeries).at(0).prop('name') as SeriesNameFn; - const nameFn2 = component.find(LineSeries).at(1).prop('name') as SeriesNameFn; - - convertSpy.mockReturnValueOnce('formatted1').mockReturnValueOnce('formatted2'); - expect(nameFn1({ ...nameFnArgs, seriesKeys: ['split1', 'a'] }, false)).toEqual( - 'formatted1 - Label A' - ); - expect(nameFn2({ ...nameFnArgs, seriesKeys: ['split1', 'b'] }, false)).toEqual( - 'formatted2 - Label B' - ); - }); - }); - - test('it set the scale of the x axis according to the args prop', () => { - const { data, args } = sampleArgs(); - - const component = shallow( - - ); - expect(component.find(LineSeries).at(0).prop('xScaleType')).toEqual(ScaleType.Ordinal); - expect(component.find(LineSeries).at(1).prop('xScaleType')).toEqual(ScaleType.Ordinal); - }); - - test('it set the scale of the y axis according to the args prop', () => { - const { data, args } = sampleArgs(); - - const component = shallow( - - ); - expect(component.find(LineSeries).at(0).prop('yScaleType')).toEqual(ScaleType.Sqrt); - expect(component.find(LineSeries).at(1).prop('yScaleType')).toEqual(ScaleType.Sqrt); - }); - - test('it gets the formatter for the x axis', () => { - const { data, args } = sampleArgs(); - - shallow(); - - expect(getFormatSpy).toHaveBeenCalledWith({ id: 'string' }); - }); - - test('it gets the formatter for the y axis if there is only one accessor', () => { - const { data, args } = sampleArgs(); - - shallow( - - ); - expect(getFormatSpy).toHaveBeenCalledWith({ - id: 'number', - params: { pattern: '0,0.000' }, - }); - }); - - test('it should pass the formatter function to the axis', () => { - const { data, args } = sampleArgs(); - - const instance = shallow(); - - const tickFormatter = instance.find(Axis).first().prop('tickFormat'); - - if (!tickFormatter) { - throw new Error('tickFormatter prop not found'); - } - - tickFormatter('I'); - - expect(convertSpy).toHaveBeenCalledWith('I'); - }); - - test('it should set the tickLabel visibility on the x axis if the tick labels is hidden', () => { - const { data, args } = sampleArgs(); - - args.tickLabelsVisibilitySettings = { - x: false, - yLeft: true, - yRight: true, - type: 'lens_xy_tickLabelsConfig', - }; - - const instance = shallow(); - - const axisStyle = instance.find(Axis).first().prop('style'); - - expect(axisStyle).toMatchObject({ - tickLabel: { - visible: false, - }, - }); - }); - - test('it should set the tickLabel visibility on the y axis if the tick labels is hidden', () => { - const { data, args } = sampleArgs(); - - args.tickLabelsVisibilitySettings = { - x: true, - yLeft: false, - yRight: false, - type: 'lens_xy_tickLabelsConfig', - }; - - const instance = shallow(); - - const axisStyle = instance.find(Axis).at(1).prop('style'); - - expect(axisStyle).toMatchObject({ - tickLabel: { - visible: false, - }, - }); - }); - - test('it should set the tickLabel visibility on the x axis if the tick labels is shown', () => { - const { data, args } = sampleArgs(); - - args.tickLabelsVisibilitySettings = { - x: true, - yLeft: true, - yRight: true, - type: 'lens_xy_tickLabelsConfig', - }; - - const instance = shallow(); - - const axisStyle = instance.find(Axis).first().prop('style'); - - expect(axisStyle).toMatchObject({ - tickLabel: { - visible: true, - }, - }); - }); - - test('it should set the tickLabel orientation on the x axis', () => { - const { data, args } = sampleArgs(); - - args.labelsOrientation = { - x: -45, - yLeft: 0, - yRight: -90, - type: 'lens_xy_labelsOrientationConfig', - }; - - const instance = shallow(); - - const axisStyle = instance.find(Axis).first().prop('style'); - - expect(axisStyle).toMatchObject({ - tickLabel: { - rotation: -45, - }, - }); - }); - - test('it should set the tickLabel visibility on the y axis if the tick labels is shown', () => { - const { data, args } = sampleArgs(); - - args.tickLabelsVisibilitySettings = { - x: false, - yLeft: true, - yRight: true, - type: 'lens_xy_tickLabelsConfig', - }; - - const instance = shallow(); - - const axisStyle = instance.find(Axis).at(1).prop('style'); - - expect(axisStyle).toMatchObject({ - tickLabel: { - visible: true, - }, - }); - }); - - test('it should set the tickLabel orientation on the y axis', () => { - const { data, args } = sampleArgs(); - - args.labelsOrientation = { - x: -45, - yLeft: -90, - yRight: -90, - type: 'lens_xy_labelsOrientationConfig', - }; - - const instance = shallow(); - - const axisStyle = instance.find(Axis).at(1).prop('style'); - - expect(axisStyle).toMatchObject({ - tickLabel: { - rotation: -90, - }, - }); - }); - - test('it should remove invalid rows', () => { - const data: LensMultiTable = { - type: 'lens_multitable', - tables: { - first: { - type: 'datatable', - columns: [ - { id: 'a', name: 'a', meta: { type: 'number' } }, - { id: 'b', name: 'b', meta: { type: 'number' } }, - { id: 'c', name: 'c', meta: { type: 'string' } }, - ], - rows: [ - { a: undefined, b: 2, c: 'I', d: 'Row 1' }, - { a: 1, b: 5, c: 'J', d: 'Row 2' }, - ], - }, - second: { - type: 'datatable', - columns: [ - { id: 'a', name: 'a', meta: { type: 'number' } }, - { id: 'b', name: 'b', meta: { type: 'number' } }, - { id: 'c', name: 'c', meta: { type: 'string' } }, - ], - rows: [ - { a: undefined, b: undefined, c: undefined }, - { a: undefined, b: undefined, c: undefined }, - ], - }, - }, - }; - - const args: XYArgs = { - xTitle: '', - yTitle: '', - yRightTitle: '', - legend: { type: 'lens_xy_legendConfig', isVisible: false, position: Position.Top }, - valueLabels: 'hide', - tickLabelsVisibilitySettings: { - type: 'lens_xy_tickLabelsConfig', - x: true, - yLeft: true, - yRight: true, - }, - gridlinesVisibilitySettings: { - type: 'lens_xy_gridlinesConfig', - x: true, - yLeft: false, - yRight: false, - }, - labelsOrientation: { - type: 'lens_xy_labelsOrientationConfig', - x: 0, - yLeft: 0, - yRight: 0, - }, - yLeftExtent: { - mode: 'full', - type: 'lens_xy_axisExtentConfig', - }, - yRightExtent: { - mode: 'full', - type: 'lens_xy_axisExtentConfig', - }, - layers: [ - { - layerId: 'first', - layerType: layerTypes.DATA, - seriesType: 'line', - xAccessor: 'a', - accessors: ['c'], - splitAccessor: 'b', - columnToLabel: '', - xScaleType: 'ordinal', - yScaleType: 'linear', - isHistogram: false, - palette: mockPaletteOutput, - }, - { - layerId: 'second', - layerType: layerTypes.DATA, - seriesType: 'line', - xAccessor: 'a', - accessors: ['c'], - splitAccessor: 'b', - columnToLabel: '', - xScaleType: 'ordinal', - yScaleType: 'linear', - isHistogram: false, - palette: mockPaletteOutput, - }, - ], - }; - - const component = shallow(); - - const series = component.find(LineSeries); - - // Only one series should be rendered, even though 2 are configured - // This one series should only have one row, even though 2 are sent - expect(series.prop('data')).toEqual([{ a: 1, b: 5, c: 'J', d: 'Row 2' }]); - }); - - test('it should not remove rows with falsy but non-undefined values', () => { - const data: LensMultiTable = { - type: 'lens_multitable', - tables: { - first: { - type: 'datatable', - columns: [ - { id: 'a', name: 'a', meta: { type: 'number' } }, - { id: 'b', name: 'b', meta: { type: 'number' } }, - { id: 'c', name: 'c', meta: { type: 'number' } }, - ], - rows: [ - { a: 0, b: 2, c: 5 }, - { a: 1, b: 0, c: 7 }, - ], - }, - }, - }; - - const args: XYArgs = { - xTitle: '', - yTitle: '', - yRightTitle: '', - legend: { type: 'lens_xy_legendConfig', isVisible: false, position: Position.Top }, - valueLabels: 'hide', - tickLabelsVisibilitySettings: { - type: 'lens_xy_tickLabelsConfig', - x: true, - yLeft: false, - yRight: false, - }, - gridlinesVisibilitySettings: { - type: 'lens_xy_gridlinesConfig', - x: true, - yLeft: false, - yRight: false, - }, - labelsOrientation: { - type: 'lens_xy_labelsOrientationConfig', - x: 0, - yLeft: 0, - yRight: 0, - }, - yLeftExtent: { - mode: 'full', - type: 'lens_xy_axisExtentConfig', - }, - yRightExtent: { - mode: 'full', - type: 'lens_xy_axisExtentConfig', - }, - layers: [ - { - layerId: 'first', - layerType: layerTypes.DATA, - seriesType: 'line', - xAccessor: 'a', - accessors: ['c'], - splitAccessor: 'b', - columnToLabel: '', - xScaleType: 'ordinal', - yScaleType: 'linear', - isHistogram: false, - palette: mockPaletteOutput, - }, - ], - }; - - const component = shallow(); - - const series = component.find(LineSeries); - - expect(series.prop('data')).toEqual([ - { a: 0, b: 2, c: 5 }, - { a: 1, b: 0, c: 7 }, - ]); - }); - - test('it should show legend for split series, even with one row', () => { - const data: LensMultiTable = { - type: 'lens_multitable', - tables: { - first: { - type: 'datatable', - columns: [ - { id: 'a', name: 'a', meta: { type: 'number' } }, - { id: 'b', name: 'b', meta: { type: 'number' } }, - { id: 'c', name: 'c', meta: { type: 'string' } }, - ], - rows: [{ a: 1, b: 5, c: 'J' }], - }, - }, - }; - - const args: XYArgs = { - xTitle: '', - yTitle: '', - yRightTitle: '', - legend: { type: 'lens_xy_legendConfig', isVisible: true, position: Position.Top }, - valueLabels: 'hide', - tickLabelsVisibilitySettings: { - type: 'lens_xy_tickLabelsConfig', - x: true, - yLeft: false, - yRight: false, - }, - gridlinesVisibilitySettings: { - type: 'lens_xy_gridlinesConfig', - x: true, - yLeft: false, - yRight: false, - }, - labelsOrientation: { - type: 'lens_xy_labelsOrientationConfig', - x: 0, - yLeft: 0, - yRight: 0, - }, - yLeftExtent: { - mode: 'full', - type: 'lens_xy_axisExtentConfig', - }, - yRightExtent: { - mode: 'full', - type: 'lens_xy_axisExtentConfig', - }, - layers: [ - { - layerId: 'first', - layerType: layerTypes.DATA, - seriesType: 'line', - xAccessor: 'a', - accessors: ['c'], - splitAccessor: 'b', - columnToLabel: '', - xScaleType: 'ordinal', - yScaleType: 'linear', - isHistogram: false, - palette: mockPaletteOutput, - }, - ], - }; - - const component = shallow(); - - expect(component.find(Settings).prop('showLegend')).toEqual(true); - }); - - test('it should always show legend if showSingleSeries is set', () => { - const { data, args } = sampleArgs(); - - const component = shallow( - - ); - - expect(component.find(Settings).prop('showLegend')).toEqual(true); - }); - - test('it should populate the correct legendPosition if isInside is set', () => { - const { data, args } = sampleArgs(); - - const component = shallow( - - ); - - expect(component.find(Settings).prop('legendPosition')).toEqual({ - vAlign: VerticalAlignment.Top, - hAlign: HorizontalAlignment.Right, - direction: LayoutDirection.Vertical, - floating: true, - floatingColumns: 1, - }); - }); - - test('it not show legend if isVisible is set to false', () => { - const { data, args } = sampleArgs(); - - const component = shallow( - - ); - - expect(component.find(Settings).prop('showLegend')).toEqual(false); - }); - - test('it should show legend on right side', () => { - const { data, args } = sampleArgs(); - - const component = shallow( - - ); - - expect(component.find(Settings).prop('legendPosition')).toEqual('top'); - }); - - test('it should apply the fitting function to all non-bar series', () => { - const data: LensMultiTable = { - type: 'lens_multitable', - tables: { - first: createSampleDatatableWithRows([ - { a: 1, b: 2, c: 'I', d: 'Foo' }, - { a: 1, b: 5, c: 'J', d: 'Bar' }, - ]), - }, - }; - - const args: XYArgs = createArgsWithLayers([ - { ...sampleLayer, accessors: ['a'] }, - { ...sampleLayer, seriesType: 'bar', accessors: ['a'] }, - { ...sampleLayer, seriesType: 'area', accessors: ['a'] }, - { ...sampleLayer, seriesType: 'area_stacked', accessors: ['a'] }, - ]); - - const component = shallow( - - ); - - expect(component.find(LineSeries).prop('fit')).toEqual({ type: Fit.Carry }); - expect(component.find(BarSeries).prop('fit')).toEqual(undefined); - expect(component.find(AreaSeries).at(0).prop('fit')).toEqual({ type: Fit.Carry }); - expect(component.find(AreaSeries).at(0).prop('stackAccessors')).toEqual([]); - expect(component.find(AreaSeries).at(1).prop('fit')).toEqual({ type: Fit.Carry }); - expect(component.find(AreaSeries).at(1).prop('stackAccessors')).toEqual(['c']); - }); - - test('it should apply None fitting function if not specified', () => { - const { data, args } = sampleArgs(); - - (args.layers[0] as DataLayerArgs).accessors = ['a']; - - const component = shallow( - - ); - - expect(component.find(LineSeries).prop('fit')).toEqual({ type: Fit.None }); - }); - - test('it should apply the xTitle if is specified', () => { - const { data, args } = sampleArgs(); - - args.xTitle = 'My custom x-axis title'; - - const component = shallow( - - ); - - expect(component.find(Axis).at(0).prop('title')).toEqual('My custom x-axis title'); - }); - - test('it should hide the X axis title if the corresponding switch is off', () => { - const { data, args } = sampleArgs(); - - args.axisTitlesVisibilitySettings = { - x: false, - yLeft: true, - yRight: true, - type: 'lens_xy_axisTitlesVisibilityConfig', - }; - - const component = shallow( - - ); - - const axisStyle = component.find(Axis).first().prop('style'); - - expect(axisStyle).toMatchObject({ - axisTitle: { - visible: false, - }, - }); - }); - - test('it should show the X axis gridlines if the setting is on', () => { - const { data, args } = sampleArgs(); - - args.gridlinesVisibilitySettings = { - x: true, - yLeft: false, - yRight: false, - type: 'lens_xy_gridlinesConfig', - }; - - const component = shallow( - - ); - - expect(component.find(Axis).at(0).prop('gridLine')).toMatchObject({ - visible: true, - }); - }); - - test('it should format the boolean values correctly', () => { - const data: LensMultiTable = { - type: 'lens_multitable', - tables: { - first: { - type: 'datatable', - columns: [ - { - id: 'a', - name: 'a', - meta: { type: 'number', params: { id: 'number', params: { pattern: '0,0.000' } } }, - }, - { - id: 'b', - name: 'b', - meta: { type: 'number', params: { id: 'number', params: { pattern: '000,0' } } }, - }, - { - id: 'c', - name: 'c', - meta: { - type: 'boolean', - params: { id: 'boolean' }, - }, - }, - ], - rows: [ - { a: 5, b: 2, c: 0 }, - { a: 19, b: 5, c: 1 }, - ], - }, - }, - dateRange: { - fromDate: new Date('2019-01-02T05:00:00.000Z'), - toDate: new Date('2019-01-03T05:00:00.000Z'), - }, - }; - const timeSampleLayer: DataLayerArgs = { - layerId: 'first', - layerType: layerTypes.DATA, - seriesType: 'line', - xAccessor: 'c', - accessors: ['a', 'b'], - xScaleType: 'ordinal', - yScaleType: 'linear', - isHistogram: false, - palette: mockPaletteOutput, - }; - const args = createArgsWithLayers([timeSampleLayer]); - - const getCustomFormatSpy = jest.fn(); - getCustomFormatSpy.mockReturnValue({ convert: jest.fn((x) => Boolean(x)) }); - - const component = shallow( - - ); - - expect(component.find(LineSeries).at(1).prop('data')).toEqual([ - { - a: 5, - b: 2, - c: false, - }, - { - a: 19, - b: 5, - c: true, - }, - ]); - }); - - describe('annotations', () => { - const sampleStyledAnnotation: EventAnnotationOutput = { - time: '2022-03-18T08:25:00.000Z', - label: 'Event 1', - icon: 'triangle', - type: 'manual_event_annotation', - color: 'red', - lineStyle: 'dashed', - lineWidth: 3, - }; - const sampleAnnotationLayers: AnnotationLayerArgs[] = [ - { - layerType: layerTypes.ANNOTATIONS, - layerId: 'annotation', - annotations: [ - { - time: '2022-03-18T08:25:17.140Z', - label: 'Annotation', - type: 'manual_event_annotation', - }, - ], - }, - ]; - function sampleArgsWithAnnotation(annotationLayers = sampleAnnotationLayers) { - const { args } = sampleArgs(); - return { - data: dateHistogramData, - args: { - ...args, - layers: [dateHistogramLayer, ...annotationLayers], - } as XYArgs, - }; - } - test('should render basic annotation', () => { - const { data, args } = sampleArgsWithAnnotation(); - const component = mount(); - expect(component.find('LineAnnotation')).toMatchSnapshot(); - }); - test('should render simplified annotation when hide is true', () => { - const { data, args } = sampleArgsWithAnnotation(); - args.layers[0].hide = true; - const component = mount(); - expect(component.find('LineAnnotation')).toMatchSnapshot(); - }); - - test('should render grouped annotations preserving the shared styles', () => { - const { data, args } = sampleArgsWithAnnotation([ - { - layerType: layerTypes.ANNOTATIONS, - layerId: 'annotation', - annotations: [ - sampleStyledAnnotation, - { ...sampleStyledAnnotation, time: '2022-03-18T08:25:00.020Z', label: 'Event 2' }, - { - ...sampleStyledAnnotation, - time: '2022-03-18T08:25:00.001Z', - label: 'Event 3', - }, - ], - }, - ]); - const component = mount(); - const groupedAnnotation = component.find(LineAnnotation); - - expect(groupedAnnotation.length).toEqual(1); - // styles are passed because they are shared, dataValues & header is rounded to the interval - expect(groupedAnnotation).toMatchSnapshot(); - // renders numeric icon for grouped annotations - const marker = mount(
{groupedAnnotation.prop('marker')}
); - const numberIcon = marker.find('NumberIcon'); - expect(numberIcon.length).toEqual(1); - expect(numberIcon.text()).toEqual('3'); - - // checking tooltip - const renderLinks = mount(
{groupedAnnotation.prop('customTooltipDetails')!()}
); - expect(renderLinks.text()).toEqual( - ' Event 1 2022-03-18T08:25:00.000Z Event 2 2022-03-18T08:25:00.020Z Event 3 2022-03-18T08:25:00.001Z' - ); - }); - test('should render grouped annotations with default styles', () => { - const { data, args } = sampleArgsWithAnnotation([ - { - layerType: layerTypes.ANNOTATIONS, - layerId: 'annotation', - annotations: [sampleStyledAnnotation], - }, - { - layerType: layerTypes.ANNOTATIONS, - layerId: 'annotation', - annotations: [ - { - ...sampleStyledAnnotation, - icon: 'square', - color: 'blue', - lineStyle: 'dotted', - lineWidth: 10, - time: '2022-03-18T08:25:00.001Z', - label: 'Event 2', - }, - ], - }, - ]); - const component = mount(); - const groupedAnnotation = component.find(LineAnnotation); - - expect(groupedAnnotation.length).toEqual(1); - // styles are default because they are different for both annotations - expect(groupedAnnotation).toMatchSnapshot(); - }); - test('should not render hidden annotations', () => { - const { data, args } = sampleArgsWithAnnotation([ - { - layerType: layerTypes.ANNOTATIONS, - layerId: 'annotation', - annotations: [ - sampleStyledAnnotation, - { ...sampleStyledAnnotation, time: '2022-03-18T08:30:00.020Z', label: 'Event 2' }, - { - ...sampleStyledAnnotation, - time: '2022-03-18T08:35:00.001Z', - label: 'Event 3', - isHidden: true, - }, - ], - }, - ]); - const component = mount(); - const annotations = component.find(LineAnnotation); - - expect(annotations.length).toEqual(2); - }); - }); - }); - - describe('calculateMinInterval', () => { - let xyProps: XYChartProps; - - beforeEach(() => { - xyProps = sampleArgs(); - (xyProps.args.layers[0] as DataLayerArgs).xScaleType = 'time'; - }); - it('should use first valid layer and determine interval', async () => { - xyProps.data.tables.first.columns[2].meta.source = 'esaggs'; - xyProps.data.tables.first.columns[2].meta.sourceParams = { - type: 'date_histogram', - params: { - used_interval: '5m', - }, - }; - const result = await calculateMinInterval(xyProps); - expect(result).toEqual(5 * 60 * 1000); - }); - - it('should return interval of number histogram if available on first x axis columns', async () => { - (xyProps.args.layers[0] as DataLayerArgs).xScaleType = 'linear'; - xyProps.data.tables.first.columns[2].meta = { - source: 'esaggs', - type: 'number', - field: 'someField', - sourceParams: { - type: 'histogram', - params: { - interval: 'auto', - used_interval: 5, - }, - }, - }; - const result = await calculateMinInterval(xyProps); - expect(result).toEqual(5); - }); - - it('should return undefined if data table is empty', async () => { - xyProps.data.tables.first.rows = []; - xyProps.data.tables.first.columns[2].meta.source = 'esaggs'; - xyProps.data.tables.first.columns[2].meta.sourceParams = { - type: 'date_histogram', - params: { - used_interval: '5m', - }, - }; - const result = await calculateMinInterval(xyProps); - expect(result).toEqual(undefined); - }); - - it('should return undefined if interval can not be checked', async () => { - const result = await calculateMinInterval(xyProps); - expect(result).toEqual(undefined); - }); - - it('should return undefined if date column is not found', async () => { - xyProps.data.tables.first.columns.splice(2, 1); - const result = await calculateMinInterval(xyProps); - expect(result).toEqual(undefined); - }); - - it('should return undefined if x axis is not a date', async () => { - (xyProps.args.layers[0] as DataLayerArgs).xScaleType = 'ordinal'; - xyProps.data.tables.first.columns.splice(2, 1); - const result = await calculateMinInterval(xyProps); - expect(result).toEqual(undefined); - }); - }); -}); diff --git a/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx b/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx index af679d1354792..9a36f5805dbc3 100644 --- a/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx @@ -11,12 +11,14 @@ import { layerTypes } from '../../common'; import type { YAxisMode, YConfig, + XYDataLayerConfig, + XYReferenceLineLayerConfig, } from '../../../../../src/plugins/chart_expressions/expression_xy/common'; import { Datatable } from '../../../../../src/plugins/expressions/public'; import type { DatasourcePublicAPI, FramePublicAPI, Visualization } from '../types'; import { groupAxesByType } from './axes_configuration'; import { isHorizontalChart, isPercentageSeries, isStackedChart } from './state_helpers'; -import type { XYState, XYDataLayerConfig, XYReferenceLineLayerConfig } from './types'; +import type { XYState } from './types'; import { checkScaleOperation, getAxisName, diff --git a/x-pack/plugins/lens/public/xy_visualization/state_helpers.ts b/x-pack/plugins/lens/public/xy_visualization/state_helpers.ts index d3c8efae33836..3add251efb2ca 100644 --- a/x-pack/plugins/lens/public/xy_visualization/state_helpers.ts +++ b/x-pack/plugins/lens/public/xy_visualization/state_helpers.ts @@ -11,13 +11,11 @@ import type { SeriesType, YConfig, ValidLayer, -} from '../../../../../src/plugins/chart_expressions/expression_xy/common'; -import { - visualizationTypes, XYLayerConfig, XYDataLayerConfig, XYReferenceLineLayerConfig, -} from './types'; +} from '../../../../../src/plugins/chart_expressions/expression_xy/common'; +import { visualizationTypes } from './types'; import { getDataLayers, isAnnotationsLayer, isDataLayer } from './visualization_helpers'; export function isHorizontalSeries(seriesType: SeriesType) { diff --git a/x-pack/plugins/lens/public/xy_visualization/to_expression.test.ts b/x-pack/plugins/lens/public/xy_visualization/to_expression.test.ts index 2e3db8f2f6f93..c04781198c78f 100644 --- a/x-pack/plugins/lens/public/xy_visualization/to_expression.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/to_expression.test.ts @@ -13,9 +13,9 @@ import { OperationDescriptor } from '../types'; import { createMockDatasource, createMockFramePublicAPI } from '../mocks'; import { layerTypes } from '../../common'; import { fieldFormatsServiceMock } from '../../../../../src/plugins/field_formats/public/mocks'; +import { eventAnnotationServiceMock } from '../../../../../src/plugins/event_annotation/public/mocks'; import { defaultReferenceLineColor } from './color_assignment'; import { themeServiceMock } from '../../../../../src/core/public/mocks'; -import { eventAnnotationServiceMock } from 'src/plugins/event_annotation/public/mocks'; describe('#toExpression', () => { const xyVisualization = getXyVisualization({ diff --git a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts index 06ebed10a4338..527b7b8a96201 100644 --- a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts +++ b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts @@ -10,18 +10,15 @@ import { ScaleType } from '@elastic/charts'; import { PaletteRegistry } from 'src/plugins/charts/public'; import { EventAnnotationServiceType } from 'src/plugins/event_annotation/public'; -import { - State, - XYDataLayerConfig, - XYReferenceLineLayerConfig, - XYAnnotationLayerConfig, -} from './types'; +import { State } from './types'; import { OperationMetadata, DatasourcePublicAPI } from '../types'; import { getColumnToLabelMap } from './state_helpers'; import type { - ReferenceLineLayerConfigResult, ValidLayer, YConfig, + XYDataLayerConfig, + XYReferenceLineLayerConfig, + XYAnnotationLayerConfig, } from '../../../../../src/plugins/chart_expressions/expression_xy/common'; import { hasIcon } from './xy_config_panel/shared/icon_select'; import { defaultReferenceLineColor } from './color_assignment'; @@ -34,6 +31,7 @@ import { } from './visualization_helpers'; import { defaultAnnotationLabel } from './annotations/config_panel'; import { getUniqueLabels } from './annotations/helpers'; +import { layerTypes } from '../../common'; export const getSortedAccessors = ( datasource: DatasourcePublicAPI, @@ -383,7 +381,7 @@ export const buildExpression = ( }; const referenceLineLayerToExpression = ( - layer: ReferenceLineLayerConfigResult, + layer: XYReferenceLineLayerConfig, datasourceLayer: DatasourcePublicAPI ): Ast => { return { diff --git a/x-pack/plugins/lens/public/xy_visualization/types.ts b/x-pack/plugins/lens/public/xy_visualization/types.ts index 817df9223c826..34f544ecb6bd5 100644 --- a/x-pack/plugins/lens/public/xy_visualization/types.ts +++ b/x-pack/plugins/lens/public/xy_visualization/types.ts @@ -26,36 +26,11 @@ import type { AxesSettingsConfig, FittingFunction, LabelsOrientationConfig, - DataLayerArgs, - LayerType, - ReferenceLineLayerArgs, - YConfig, - XScaleType, - YScaleType, EndValue, + XYLayerConfig, } from '../../../../../src/plugins/chart_expressions/expression_xy/common'; -import { PaletteOutput } from '../../../../../src/plugins/charts/common'; import type { ValueLabelConfig } from '../../common/types'; -export interface XYDataLayerConfig - extends Omit { - layerType: LayerType; - yConfig?: YConfig[]; - palette?: PaletteOutput; - yScaleType?: YScaleType; - xScaleType?: XScaleType; - isHistogram?: boolean; -} - -export interface XYReferenceLineLayerConfig - extends Omit { - layerType: LayerType; - yConfig?: YConfig[]; - palette?: PaletteOutput; -} - -export type XYLayerConfig = XYDataLayerConfig | XYReferenceLineLayerConfig; - // Persisted parts of the state export interface XYState { preferredSeriesType: SeriesType; @@ -81,6 +56,7 @@ export interface XYState { } export type State = XYState; + const groupLabelForBar = i18n.translate('xpack.lens.xyVisualization.barGroupLabel', { defaultMessage: 'Bar', }); diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts b/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts index fb0885325970e..2d1cbdccdfb01 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts @@ -8,17 +8,13 @@ import { getXyVisualization } from './visualization'; import { Position } from '@elastic/charts'; import { Operation, VisualizeEditorContext, Suggestion, OperationDescriptor } from '../types'; +import type { State, XYState, XYSuggestion } from './types'; import type { - State, - XYState, - XYSuggestion, + DataLayerConfigResult, + SeriesType, XYLayerConfig, XYDataLayerConfig, XYReferenceLineLayerConfig, -} from './types'; -import type { - DataLayerConfigResult, - SeriesType, } from '../../../../../src/plugins/chart_expressions/expression_xy/common'; import { layerTypes } from '../../common'; import { createMockDatasource, createMockFramePublicAPI } from '../mocks'; diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx index 9464b40bc43b5..e6003ca0e3c10 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx @@ -26,8 +26,10 @@ import { SeriesType, YAxisMode, YConfig, + XYLayerConfig, + XYDataLayerConfig, } from '../../../../../src/plugins/chart_expressions/expression_xy/common'; -import { State, visualizationTypes, XYSuggestion, XYLayerConfig } from './types'; +import { State, visualizationTypes, XYSuggestion } from './types'; import { layerTypes } from '../../common'; import { isHorizontalChart } from './state_helpers'; import { toExpression, toPreviewExpression, getSortedAccessors } from './to_expression'; @@ -65,7 +67,7 @@ import { validateLayersForDimension, } from './visualization_helpers'; import { groupAxesByType } from './axes_configuration'; -import { XYState, XYDataLayerConfig } from './types'; +import { XYState } from './types'; import { ReferenceLinePanel } from './xy_config_panel/reference_line_panel'; import { DimensionTrigger } from '../shared_components/dimension_trigger'; import { AnnotationsPanel, defaultAnnotationLabel } from './annotations/config_panel'; diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization_helpers.tsx b/x-pack/plugins/lens/public/xy_visualization/visualization_helpers.tsx index 004f9b8106b4a..68c8c80f42b35 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization_helpers.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/visualization_helpers.tsx @@ -8,20 +8,15 @@ import { i18n } from '@kbn/i18n'; import { uniq } from 'lodash'; import { DatasourcePublicAPI, OperationMetadata, VisualizationType } from '../types'; +import { State, visualizationTypes, XYState } from './types'; +import { isHorizontalChart } from './state_helpers'; import { - State, - visualizationTypes, - XYState, - AnnotationLayerArgs, - DataLayerArgs, + SeriesType, XYAnnotationLayerConfig, - XYLayerArgs, XYLayerConfig, XYDataLayerConfig, XYReferenceLineLayerConfig, -} from './types'; -import { isHorizontalChart } from './state_helpers'; -import { SeriesType } from '../../../../../src/plugins/chart_expressions/expression_xy/common'; +} from '../../../../../src/plugins/chart_expressions/expression_xy/common'; import { layerTypes } from '..'; import { LensIconChartBarHorizontal } from '../assets/chart_bar_horizontal'; import { LensIconChartMixedXy } from '../assets/chart_mixed_xy'; @@ -136,12 +131,9 @@ export function checkScaleOperation( export const isDataLayer = (layer: XYLayerConfig): layer is XYDataLayerConfig => layer.layerType === layerTypes.DATA || !layer.layerType; -export const getDataLayers = (layers: Array>) => +export const getDataLayers = (layers: XYLayerConfig[]) => (layers || []).filter((layer): layer is XYDataLayerConfig => isDataLayer(layer)); -export const getDataLayersArgs = (layers: XYLayerArgs[]) => - (layers || []).filter((layer): layer is DataLayerArgs => isDataLayer(layer)); - export const getFirstDataLayer = (layers: XYLayerConfig[]) => (layers || []).find((layer): layer is XYDataLayerConfig => isDataLayer(layer)); @@ -159,9 +151,6 @@ export const isAnnotationsLayer = ( export const getAnnotationsLayers = (layers: Array>) => (layers || []).filter((layer): layer is XYAnnotationLayerConfig => isAnnotationsLayer(layer)); -export const getAnnotationsLayersArgs = (layers: XYLayerArgs[]) => - (layers || []).filter((layer): layer is AnnotationLayerArgs => isAnnotationsLayer(layer)); - export interface LayerTypeToLayer { [layerTypes.DATA]: (layer: XYDataLayerConfig) => XYDataLayerConfig; [layerTypes.REFERENCELINE]: (layer: XYReferenceLineLayerConfig) => XYReferenceLineLayerConfig; diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/axis_settings_popover.test.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/axis_settings_popover.test.tsx index 32203a7cc975c..87ccf41e91143 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/axis_settings_popover.test.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/axis_settings_popover.test.tsx @@ -23,10 +23,6 @@ describe('Axes Settings', () => { splitAccessor: 'baz', xAccessor: 'foo', accessors: ['bar'], - xScaleType: 'linear', - yScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, ], updateTitleState: jest.fn(), diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/axis_settings_popover.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/axis_settings_popover.tsx index 320918405cca9..667c2caa267aa 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/axis_settings_popover.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/axis_settings_popover.tsx @@ -20,6 +20,7 @@ import { isEqual } from 'lodash'; import { AxesSettingsConfig, AxisExtentConfig, + XYLayerConfig, } from '../../../../../../src/plugins/chart_expressions/expression_xy/common'; import { ToolbarPopover, @@ -34,7 +35,6 @@ import { EuiIconAxisRight } from '../../assets/axis_right'; import { EuiIconAxisTop } from '../../assets/axis_top'; import { ToolbarButtonProps } from '../../../../../../src/plugins/kibana_react/public'; import { validateExtent } from '../axes_configuration'; -import { XYLayerConfig } from '../types'; import './axis_settings_popover.scss'; diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/color_picker.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/color_picker.tsx index 3f801fb92876d..aecb3d5d3ad1b 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/color_picker.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/color_picker.tsx @@ -10,8 +10,9 @@ import { i18n } from '@kbn/i18n'; import { EuiFormRow, EuiColorPicker, EuiColorPickerProps, EuiToolTip, EuiIcon } from '@elastic/eui'; import type { PaletteRegistry } from 'src/plugins/charts/public'; import { defaultAnnotationColor } from '../../../../../../src/plugins/event_annotation/public'; +import { XYDataLayerConfig } from '../../../../../../src/plugins/chart_expressions/expression_xy/common'; import type { VisualizationDimensionEditorProps } from '../../types'; -import { State, XYDataLayerConfig } from '../types'; +import { State } from '../types'; import { FormatFactory } from '../../../common'; import { getSeriesColor } from '../state_helpers'; import { diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/dimension_editor.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/dimension_editor.tsx index 3d68c5b0af530..ab8f9a423a67f 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/dimension_editor.tsx @@ -10,17 +10,19 @@ import { i18n } from '@kbn/i18n'; import { EuiButtonGroup, EuiFormRow, htmlIdGenerator } from '@elastic/eui'; import type { PaletteRegistry } from 'src/plugins/charts/public'; import type { VisualizationDimensionEditorProps } from '../../types'; -import { State, XYState, XYDataLayerConfig } from '../types'; +import { State, XYState } from '../types'; import { FormatFactory } from '../../../common'; import { YAxisMode, YConfig, + XYDataLayerConfig, } from '../../../../../../src/plugins/chart_expressions/expression_xy/common'; import { isHorizontalChart } from '../state_helpers'; import { ColorPicker } from './color_picker'; import { PalettePicker, useDebouncedValue } from '../../shared_components'; -import { isReferenceLayer } from '../visualization_helpers'; +import { isAnnotationsLayer, isReferenceLayer } from '../visualization_helpers'; import { ReferenceLinePanel } from './reference_line_panel'; +import { AnnotationsPanel } from '../annotations/config_panel'; type UnwrapArray = T extends Array ? P : T; @@ -48,7 +50,7 @@ export function DimensionEditor( ) { const { state, setState, layerId, accessor } = props; const index = state.layers.findIndex((l) => l.layerId === layerId); - const layer = state.layers[index]; + const layer = state.layers[index] as XYDataLayerConfig; const { inputValue: localState, handleInputChange: setLocalState } = useDebouncedValue({ value: props.state, @@ -80,6 +82,10 @@ export function DimensionEditor( [accessor, index, localState, layer, setLocalState] ); + if (isAnnotationsLayer(layer)) { + return ; + } + if (isReferenceLayer(layer)) { return ; } diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_panel.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_panel.tsx index ffca2c0531b7c..5674bb2637666 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_panel.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_panel.tsx @@ -10,11 +10,12 @@ import { i18n } from '@kbn/i18n'; import { EuiButtonGroup, EuiFormRow } from '@elastic/eui'; import type { PaletteRegistry } from 'src/plugins/charts/public'; import type { VisualizationDimensionEditorProps } from '../../types'; -import { State, XYState, XYReferenceLineLayerConfig } from '../types'; +import { State, XYState } from '../types'; import { FormatFactory } from '../../../common'; import { FillStyle, YConfig, + XYReferenceLineLayerConfig, } from '../../../../../../src/plugins/chart_expressions/expression_xy/common'; import { ColorPicker } from './color_picker'; diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/visual_options_popover.test.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/visual_options_popover.test.tsx index 1f8ec4bea4f7a..d88a08e68422d 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/visual_options_popover.test.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/visual_options_popover.test.tsx @@ -10,7 +10,8 @@ import { shallowWithIntl as shallow } from '@kbn/test-jest-helpers'; import { Position } from '@elastic/charts'; import type { FramePublicAPI } from '../../../types'; import { createMockDatasource, createMockFramePublicAPI } from '../../../mocks'; -import { State, XYLayerConfig } from '../../types'; +import { XYLayerConfig } from '../../../../../../../src/plugins/chart_expressions/expression_xy/common'; +import { State } from '../../types'; import { VisualOptionsPopover } from '.'; import { ToolbarPopover, ValueLabelsSettings } from '../../../shared_components'; import { MissingValuesOptions } from './missing_values_option'; @@ -33,10 +34,6 @@ describe('Visual options popover', () => { splitAccessor: 'baz', xAccessor: 'foo', accessors: ['bar'], - xScaleType: 'linear', - yScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, ], }; @@ -242,10 +239,6 @@ describe('Visual options popover', () => { splitAccessor: 'baz', xAccessor: 'foo', accessors: ['bar'], - xScaleType: 'linear', - yScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, ], fittingFunction: 'Carry', diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/xy_config_panel.test.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/xy_config_panel.test.tsx index 7015dd780c827..c7e5b6dc8b4ff 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/xy_config_panel.test.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/xy_config_panel.test.tsx @@ -12,13 +12,13 @@ import { XyToolbar } from '.'; import { DimensionEditor } from './dimension_editor'; import { AxisSettingsPopover } from './axis_settings_popover'; import { FramePublicAPI } from '../../types'; -import { State, XYDataLayerConfig, XYState } from '../types'; +import { State, XYState } from '../types'; import { Position } from '@elastic/charts'; import { createMockFramePublicAPI, createMockDatasource } from '../../mocks'; import { chartPluginMock } from 'src/plugins/charts/public/mocks'; import { EuiColorPicker } from '@elastic/eui'; import { layerTypes } from '../../../common'; -import { DataLayerConfigResult } from '../../../../../../src/plugins/chart_expressions/expression_xy/common'; +import { XYDataLayerConfig } from '../../../../../../src/plugins/chart_expressions/expression_xy/common'; describe('XY Config panels', () => { let frame: FramePublicAPI; @@ -36,10 +36,6 @@ describe('XY Config panels', () => { splitAccessor: 'baz', xAccessor: 'foo', accessors: ['bar'], - xScaleType: 'linear', - yScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, ], }; @@ -227,7 +223,7 @@ describe('XY Config panels', () => { groupId="left" state={{ ...state, - layers: [{ ...state.layers[0], seriesType: 'bar_horizontal' } as DataLayerConfigResult], + layers: [{ ...state.layers[0], seriesType: 'bar_horizontal' } as XYDataLayerConfig], }} formatFactory={jest.fn()} paletteService={chartPluginMock.createPaletteRegistry()} diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.test.ts b/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.test.ts index f4488edb06244..bd0f4ddf14986 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.test.ts @@ -12,11 +12,14 @@ import { generateId } from '../id_generator'; import { getXyVisualization } from './xy_visualization'; import { chartPluginMock } from '../../../../../src/plugins/charts/public/mocks'; import { eventAnnotationServiceMock } from '../../../../../src/plugins/event_annotation/public/mocks'; +import { + XYAnnotationLayerConfig, + XYDataLayerConfig, +} from '../../../../../src/plugins/chart_expressions/expression_xy/common'; import { PaletteOutput } from 'src/plugins/charts/public'; import { layerTypes } from '../../common'; import { fieldFormatsServiceMock } from '../../../../../src/plugins/field_formats/public/mocks'; import { themeServiceMock } from '../../../../../src/core/public/mocks'; -import { XYAnnotationLayerConfig, XYDataLayerConfig } from './types'; jest.mock('../id_generator'); @@ -201,10 +204,6 @@ describe('xy_suggestions', () => { seriesType: 'bar', accessors: ['bytes'], splitAccessor: undefined, - yScaleType: 'linear', - xScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, { layerId: 'second', @@ -212,10 +211,6 @@ describe('xy_suggestions', () => { seriesType: 'bar', accessors: ['bytes'], splitAccessor: undefined, - yScaleType: 'linear', - xScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, ], }, @@ -316,10 +311,6 @@ describe('xy_suggestions', () => { xAccessor: 'date', accessors: ['bytes'], splitAccessor: undefined, - yScaleType: 'linear', - xScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, ], }, @@ -362,10 +353,6 @@ describe('xy_suggestions', () => { xAccessor: 'date', accessors: ['bytes'], splitAccessor: undefined, - yScaleType: 'linear', - xScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, { layerId: 'second', @@ -374,10 +361,6 @@ describe('xy_suggestions', () => { xAccessor: undefined, accessors: [], splitAccessor: undefined, - yScaleType: 'linear', - xScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, ], }, @@ -661,10 +644,6 @@ describe('xy_suggestions', () => { seriesType: 'bar', splitAccessor: 'product', xAccessor: 'date', - yScaleType: 'linear', - xScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, ], }, @@ -720,10 +699,6 @@ describe('xy_suggestions', () => { seriesType: 'line', splitAccessor: undefined, xAccessor: '', - yScaleType: 'linear', - xScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, ], }; @@ -763,10 +738,6 @@ describe('xy_suggestions', () => { seriesType: 'bar', splitAccessor: undefined, xAccessor: 'date', - yScaleType: 'linear', - xScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, ], }; @@ -810,10 +781,6 @@ describe('xy_suggestions', () => { seriesType: 'bar', splitAccessor: 'product', xAccessor: 'date', - yScaleType: 'linear', - xScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, ], }; @@ -858,10 +825,6 @@ describe('xy_suggestions', () => { seriesType: 'bar', splitAccessor: 'dummyCol', xAccessor: 'product', - yScaleType: 'linear', - xScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, ], }; @@ -900,10 +863,6 @@ describe('xy_suggestions', () => { seriesType: 'bar', splitAccessor: 'date', xAccessor: 'product', - yScaleType: 'linear', - xScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, ], }; @@ -945,10 +904,6 @@ describe('xy_suggestions', () => { seriesType: 'bar', splitAccessor: 'dummyCol', xAccessor: 'product', - yScaleType: 'linear', - xScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, ], }; @@ -994,10 +949,6 @@ describe('xy_suggestions', () => { seriesType: 'bar', splitAccessor: 'category', xAccessor: 'product', - yScaleType: 'linear', - xScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, ], }; @@ -1044,10 +995,6 @@ describe('xy_suggestions', () => { seriesType: 'bar', splitAccessor: 'dummyCol', xAccessor: 'product', - yScaleType: 'linear', - xScaleType: 'linear', - isHistogram: false, - palette: { type: 'palette', name: 'default' }, }, ], }; diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.ts b/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.ts index 610e21c1fe138..eead82862e630 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.ts +++ b/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.ts @@ -16,8 +16,12 @@ import { TableSuggestion, TableChangeType, } from '../types'; -import { State, XYState, visualizationTypes, XYLayerConfig, XYDataLayerConfig } from './types'; -import type { SeriesType } from '../../../../../src/plugins/chart_expressions/expression_xy/common'; +import { State, XYState, visualizationTypes } from './types'; +import type { + SeriesType, + XYLayerConfig, + XYDataLayerConfig, +} from '../../../../../src/plugins/chart_expressions/expression_xy/common'; import { layerTypes } from '../../common'; import { getIconForSeries } from './state_helpers'; import { getDataLayers, isDataLayer } from './visualization_helpers'; From 9cc22c875bb16fc2f2a9270dc84c46b1772bd3e6 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Fri, 25 Mar 2022 10:58:55 +0200 Subject: [PATCH 060/133] Fixed annotatations. --- .../common/expression_functions/xy_vis.ts | 3 +- .../public/components/annotations.scss | 37 +++++ .../public/components/annotations.tsx | 144 ++++++++++++++++-- .../components/reference_lines.test.tsx | 13 -- .../public/components/reference_lines.tsx | 2 +- .../public/helpers/annotations.tsx | 129 +--------------- .../public/xy_visualization/to_expression.ts | 3 +- 7 files changed, 175 insertions(+), 156 deletions(-) create mode 100644 src/plugins/chart_expressions/expression_xy/public/components/annotations.scss diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts index 9f79dc375f364..aca605584d0ea 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts @@ -29,6 +29,7 @@ import { LABELS_ORIENTATION_CONFIG, AXIS_TITLES_VISIBILITY_CONFIG, EndValues, + ANNOTATION_LAYER, } from '../constants'; export const logDataTable = ( @@ -146,7 +147,7 @@ export const xyVisFunction: ExpressionFunctionDefinition< }), }, layers: { - types: [DATA_LAYER, REFERENCE_LINE_LAYER], + types: [DATA_LAYER, REFERENCE_LINE_LAYER, ANNOTATION_LAYER], help: i18n.translate('expressionXY.xyVis.layers.help', { defaultMessage: 'Layers of visual series', }), diff --git a/src/plugins/chart_expressions/expression_xy/public/components/annotations.scss b/src/plugins/chart_expressions/expression_xy/public/components/annotations.scss new file mode 100644 index 0000000000000..fc2b1204bb1d0 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/public/components/annotations.scss @@ -0,0 +1,37 @@ +.lnsXyDecorationRotatedWrapper { + display: inline-block; + overflow: hidden; + line-height: 1.5; + + .lnsXyDecorationRotatedWrapper__label { + display: inline-block; + white-space: nowrap; + transform: translate(0, 100%) rotate(-90deg); + transform-origin: 0 0; + + &::after { + content: ''; + float: left; + margin-top: 100%; + } + } +} + +.lnsXyAnnotationNumberIcon { + border-radius: $euiSize; + min-width: $euiSize; + height: $euiSize; + background-color: currentColor; +} + +.lnsXyAnnotationNumberIcon__text { + font-weight: 500; + font-size: 9px; + letter-spacing: -.5px; + line-height: 11px; +} + +.lnsXyAnnotationIcon_rotate90 { + transform: rotate(45deg); + transform-origin: center; +} diff --git a/src/plugins/chart_expressions/expression_xy/public/components/annotations.tsx b/src/plugins/chart_expressions/expression_xy/public/components/annotations.tsx index 032f4f7ce7ed4..f30c0e2ab305d 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/annotations.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/annotations.tsx @@ -6,7 +6,9 @@ * Side Public License, v 1. */ -import './expression.scss'; +import './annotations.scss'; +import './reference_lines.scss'; + import React from 'react'; import { snakeCase } from 'lodash'; import { @@ -16,19 +18,19 @@ import { Position, } from '@elastic/charts'; import moment from 'moment'; -import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiIconProps, EuiText } from '@elastic/eui'; +import classnames from 'classnames'; import type { EventAnnotationArgs } from '../../../../event_annotation/common'; import type { FieldFormat } from '../../../../field_formats/common'; import { defaultAnnotationColor } from '../../../../../../src/plugins/event_annotation/public'; -import type { AnnotationLayerArgs, AnnotationLayerConfigResult } from '../../common/types'; -import { hasIcon } from '../helpers'; -import { - mapVerticalToHorizontalPlacement, - LINES_MARKER_SIZE, - MarkerBody, - Marker, - AnnotationIcon, -} from '../helpers'; +import type { + AnnotationLayerArgs, + AnnotationLayerConfigResult, + IconPosition, + YAxisMode, +} from '../../common/types'; +import { annotationsIconSet, hasIcon, isNumericalString } from '../helpers'; +import { mapVerticalToHorizontalPlacement, LINES_MARKER_SIZE } from '../helpers'; const getRoundedTimestamp = (timestamp: number, firstTimestamp?: number, minInterval?: number) => { if (!firstTimestamp || !minInterval) { @@ -232,3 +234,123 @@ export const Annotations = ({ ); }; + +export function MarkerBody({ + label, + isHorizontal, +}: { + label: string | undefined; + isHorizontal: boolean; +}) { + if (!label) { + return null; + } + if (isHorizontal) { + return ( +
+ {label} +
+ ); + } + return ( +
+
+ {label} +
+
+ ); +} + +function NumberIcon({ number }: { number: number }) { + return ( + + + {number < 10 ? number : `9+`} + + + ); +} + +export const AnnotationIcon = ({ + type, + rotateClassName = '', + isHorizontal, + renderedInChart, + ...rest +}: { + type: string; + rotateClassName?: string; + isHorizontal?: boolean; + renderedInChart?: boolean; +} & EuiIconProps) => { + if (isNumericalString(type)) { + return ; + } + const iconConfig = annotationsIconSet.find((i) => i.value === type); + if (!iconConfig) { + return null; + } + return ( + + ); +}; + +interface MarkerConfig { + axisMode?: YAxisMode; + icon?: string; + textVisibility?: boolean; + iconPosition?: IconPosition; +} + +export function Marker({ + config, + isHorizontal, + hasReducedPadding, + label, + rotateClassName, +}: { + config: MarkerConfig; + isHorizontal: boolean; + hasReducedPadding: boolean; + label?: string; + rotateClassName?: string; +}) { + if (hasIcon(config.icon)) { + return ( + + ); + } + + // if there's some text, check whether to show it as marker, or just show some padding for the icon + if (config.textVisibility) { + if (hasReducedPadding) { + return ; + } + return ; + } + return null; +} diff --git a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.test.tsx b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.test.tsx index c53ee9a5cc55b..5afece23ca6bc 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.test.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.test.tsx @@ -9,14 +9,11 @@ import { LineAnnotation, RectAnnotation } from '@elastic/charts'; import { shallow } from 'enzyme'; import React from 'react'; -import { chartPluginMock } from '../../../../charts/public/mocks'; import { FieldFormat } from '../../../../field_formats/common'; import { LensMultiTable } from '../../common'; import { ReferenceLineLayerArgs, YConfig } from '../../common/types'; import { ReferenceLineAnnotations, ReferenceLineAnnotationsProps } from './reference_lines'; -const paletteService = chartPluginMock.createPaletteRegistry(); - const row: Record = { xAccessorFirstId: 1, xAccessorSecondId: 2, @@ -112,7 +109,6 @@ describe('ReferenceLineAnnotations', () => { axisMode, lineStyle: 'solid', fill, - type: 'yConfig', }, ])} /> @@ -151,7 +147,6 @@ describe('ReferenceLineAnnotations', () => { axisMode: 'bottom', lineStyle: 'solid', fill, - type: 'yConfig', }, ])} /> @@ -193,14 +188,12 @@ describe('ReferenceLineAnnotations', () => { axisMode, lineStyle: 'solid', fill, - type: 'yConfig', }, { forAccessor: `${layerPrefix}SecondId`, axisMode, lineStyle: 'solid', fill, - type: 'yConfig', }, ])} /> @@ -243,14 +236,12 @@ describe('ReferenceLineAnnotations', () => { axisMode: 'bottom', lineStyle: 'solid', fill, - type: 'yConfig', }, { forAccessor: `${layerPrefix}SecondId`, axisMode: 'bottom', lineStyle: 'solid', fill, - type: 'yConfig', }, ])} /> @@ -292,14 +283,12 @@ describe('ReferenceLineAnnotations', () => { axisMode, lineStyle: 'solid', fill: 'above', - type: 'yConfig', }, { forAccessor: `${layerPrefix}SecondId`, axisMode, lineStyle: 'solid', fill: 'below', - type: 'yConfig', }, ])} /> @@ -342,14 +331,12 @@ describe('ReferenceLineAnnotations', () => { axisMode: 'left', lineStyle: 'solid', fill, - type: 'yConfig', }, { forAccessor: `yAccessorRightSecondId`, axisMode: 'right', lineStyle: 'solid', fill, - type: 'yConfig', }, ])} /> diff --git a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.tsx b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.tsx index 099df5f91c5bc..400064ed6212e 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.tsx @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import './expression_reference_lines.scss'; +import './reference_lines.scss'; import React from 'react'; import { groupBy } from 'lodash'; diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/annotations.tsx b/src/plugins/chart_expressions/expression_xy/public/helpers/annotations.tsx index 91aeae9c7c6c0..42c3dba44f119 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/annotations.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/annotations.tsx @@ -5,23 +5,16 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ - -import React from 'react'; -import { EuiFlexGroup, EuiIcon, EuiIconProps, EuiText } from '@elastic/eui'; import { Position } from '@elastic/charts'; -import classnames from 'classnames'; import { i18n } from '@kbn/i18n'; import moment from 'moment'; import type { IconPosition, YAxisMode, YConfig } from '../../common/types'; import { hasIcon } from './icon'; -import { annotationsIconSet } from './annotations_icon_set'; import type { XYDataLayerConfig, XYAnnotationLayerConfig, XYLayerConfig } from '../../common/types'; import type { FramePublicAPI } from '../types'; import { getAnnotationsLayersConfig } from './visualization'; import { defaultAnnotationColor } from '../../../../../../src/plugins/event_annotation/public'; -import './expression_reference_lines.scss'; - export const LINES_MARKER_SIZE = 20; export const computeChartMargins = ( @@ -138,127 +131,7 @@ export function getBaseIconPlacement( return Position.Top; } -export function MarkerBody({ - label, - isHorizontal, -}: { - label: string | undefined; - isHorizontal: boolean; -}) { - if (!label) { - return null; - } - if (isHorizontal) { - return ( -
- {label} -
- ); - } - return ( -
-
- {label} -
-
- ); -} - -const isNumericalString = (value: string) => !isNaN(Number(value)); - -function NumberIcon({ number }: { number: number }) { - return ( - - - {number < 10 ? number : `9+`} - - - ); -} - -interface MarkerConfig { - axisMode?: YAxisMode; - icon?: string; - textVisibility?: boolean; - iconPosition?: IconPosition; -} - -export const AnnotationIcon = ({ - type, - rotateClassName = '', - isHorizontal, - renderedInChart, - ...rest -}: { - type: string; - rotateClassName?: string; - isHorizontal?: boolean; - renderedInChart?: boolean; -} & EuiIconProps) => { - if (isNumericalString(type)) { - return ; - } - const iconConfig = annotationsIconSet.find((i) => i.value === type); - if (!iconConfig) { - return null; - } - return ( - - ); -}; - -export function Marker({ - config, - isHorizontal, - hasReducedPadding, - label, - rotateClassName, -}: { - config: MarkerConfig; - isHorizontal: boolean; - hasReducedPadding: boolean; - label?: string; - rotateClassName?: string; -}) { - if (hasIcon(config.icon)) { - return ( - - ); - } - - // if there's some text, check whether to show it as marker, or just show some padding for the icon - if (config.textVisibility) { - if (hasReducedPadding) { - return ; - } - return ; - } - return null; -} +export const isNumericalString = (value: string) => !isNaN(Number(value)); const MAX_DATE = 8640000000000000; const MIN_DATE = -8640000000000000; diff --git a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts index 527b7b8a96201..a36633bb95d0a 100644 --- a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts +++ b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts @@ -414,11 +414,10 @@ const annotationLayerToExpression = ( chain: [ { type: 'function', - function: 'lens_xy_annotation_layer', + function: 'annotationLayer', arguments: { hide: [Boolean(layer.hide)], layerId: [layer.layerId], - layerType: [layerTypes.ANNOTATIONS], annotations: layer.annotations ? layer.annotations.map( (ann): Ast => From a99a2235c71003e81c257ab42c082783b3a8dd5d Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Fri, 25 Mar 2022 11:57:52 +0200 Subject: [PATCH 061/133] Fixed types. --- .../expression_xy/common/types/expression_functions.ts | 6 ++++-- .../public/components/reference_lines.test.tsx | 10 ++++++++++ .../public/components/reference_lines.tsx | 1 - .../expression_xy/public/helpers/reference_lines.ts | 2 +- .../expression_xy/public/helpers/state.ts | 5 +++-- .../expression_xy/public/helpers/visualization.ts | 1 - .../chart_expressions/expression_xy/tsconfig.json | 2 +- x-pack/plugins/lens/public/index.ts | 3 ++- .../lens/public/xy_visualization/visualization.tsx | 2 +- 9 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts b/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts index 8646ffcf74a9c..04d6867782d78 100644 --- a/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts +++ b/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts @@ -97,13 +97,14 @@ export interface ValidLayer extends DataLayerConfigResult { xAccessor: NonNullable; } -export type DataLayerArgs = Omit & { +export type DataLayerArgs = Omit & { columnToLabel?: string; // Actually a JSON key-value pair yScaleType: YScaleType; xScaleType: XScaleType; isHistogram: boolean; // palette will always be set on the expression palette: PaletteOutput; + yConfig?: YConfigResult[]; }; export interface LegendConfig { @@ -209,8 +210,9 @@ export interface XYReferenceLineLayerConfig { layerType: typeof LayerTypes.REFERENCELINE; } -export type ReferenceLineLayerArgs = Omit & { +export type ReferenceLineLayerArgs = Omit & { columnToLabel?: string; + yConfig?: YConfigResult[]; }; export type XYLayerArgs = DataLayerArgs | ReferenceLineLayerArgs | AnnotationLayerArgs; diff --git a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.test.tsx b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.test.tsx index 5afece23ca6bc..a0d6351dad7f9 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.test.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.test.tsx @@ -109,6 +109,7 @@ describe('ReferenceLineAnnotations', () => { axisMode, lineStyle: 'solid', fill, + type: 'yConfig', }, ])} /> @@ -146,6 +147,7 @@ describe('ReferenceLineAnnotations', () => { forAccessor: `${layerPrefix}FirstId`, axisMode: 'bottom', lineStyle: 'solid', + type: 'yConfig', fill, }, ])} @@ -187,12 +189,14 @@ describe('ReferenceLineAnnotations', () => { forAccessor: `${layerPrefix}FirstId`, axisMode, lineStyle: 'solid', + type: 'yConfig', fill, }, { forAccessor: `${layerPrefix}SecondId`, axisMode, lineStyle: 'solid', + type: 'yConfig', fill, }, ])} @@ -235,12 +239,14 @@ describe('ReferenceLineAnnotations', () => { forAccessor: `${layerPrefix}FirstId`, axisMode: 'bottom', lineStyle: 'solid', + type: 'yConfig', fill, }, { forAccessor: `${layerPrefix}SecondId`, axisMode: 'bottom', lineStyle: 'solid', + type: 'yConfig', fill, }, ])} @@ -283,12 +289,14 @@ describe('ReferenceLineAnnotations', () => { axisMode, lineStyle: 'solid', fill: 'above', + type: 'yConfig', }, { forAccessor: `${layerPrefix}SecondId`, axisMode, lineStyle: 'solid', fill: 'below', + type: 'yConfig', }, ])} /> @@ -331,12 +339,14 @@ describe('ReferenceLineAnnotations', () => { axisMode: 'left', lineStyle: 'solid', fill, + type: 'yConfig', }, { forAccessor: `yAccessorRightSecondId`, axisMode: 'right', lineStyle: 'solid', fill, + type: 'yConfig', }, ])} /> diff --git a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.tsx b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.tsx index 400064ed6212e..b151e495844b9 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.tsx @@ -14,7 +14,6 @@ import { EuiIcon } from '@elastic/eui'; import { RectAnnotation, AnnotationDomainType, LineAnnotation, Position } from '@elastic/charts'; import { euiLightVars } from '@kbn/ui-theme'; import type { FieldFormat } from '../../../../field_formats/common'; -import type { PaletteRegistry } from '../../../../charts/public'; import type { IconPosition, ReferenceLineLayerArgs, YAxisMode } from '../../common/types'; import type { LensMultiTable } from '../../common/types'; import { hasIcon } from '../helpers'; diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/reference_lines.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/reference_lines.ts index 609aed45eda9f..86a87e22157c7 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/reference_lines.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/reference_lines.ts @@ -7,7 +7,7 @@ */ import { partition } from 'lodash'; -import type { DataLayerConfigResult, YConfig } from '../../common'; +import type { DataLayerConfigResult } from '../../common'; import type { FramePublicAPI } from '../types'; import { isStackedChart } from './state'; diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/state.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/state.ts index eda33dde1ba47..a5cd66f178b63 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/state.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/state.ts @@ -7,7 +7,7 @@ */ import type { SeriesType, XYLayerConfigResult, YConfig } from '../../common'; -import { getDataLayers, isDataLayer } from './visualization'; +import { getDataLayers, isAnnotationsLayer, isDataLayer } from './visualization'; export function isHorizontalSeries(seriesType: SeriesType) { return ( @@ -26,9 +26,10 @@ export function isHorizontalChart(layers: XYLayerConfigResult[]) { } export const getSeriesColor = (layer: XYLayerConfigResult, accessor: string) => { - if (isDataLayer(layer) && layer.splitAccessor) { + if ((isDataLayer(layer) && layer.splitAccessor) || isAnnotationsLayer(layer)) { return null; } + return ( layer?.yConfig?.find((yConfig: YConfig) => yConfig.forAccessor === accessor)?.color || null ); diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/visualization.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/visualization.ts index 7a308860c88fd..c1907900249b4 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/visualization.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/visualization.ts @@ -14,7 +14,6 @@ import { XYLayerConfig, XYDataLayerConfig, XYReferenceLineLayerConfig, - LensMultiTable, AnnotationLayerConfigResult, } from '../../common/types'; import { LayerTypes } from '../../common/constants'; diff --git a/src/plugins/chart_expressions/expression_xy/tsconfig.json b/src/plugins/chart_expressions/expression_xy/tsconfig.json index bba4d2c779fad..f20b13efbb415 100644 --- a/src/plugins/chart_expressions/expression_xy/tsconfig.json +++ b/src/plugins/chart_expressions/expression_xy/tsconfig.json @@ -20,6 +20,6 @@ { "path": "../../ui_actions/tsconfig.json" }, { "path": "../../field_formats/tsconfig.json"}, { "path": "../../kibana_utils/tsconfig.json" }, - { "path": "../../kibana_utils/tsconfig.json" }, + { "path": "../../event_annotation/tsconfig.json" }, ] } diff --git a/x-pack/plugins/lens/public/index.ts b/x-pack/plugins/lens/public/index.ts index 7c26bc1d0a544..345bf6a797f52 100644 --- a/x-pack/plugins/lens/public/index.ts +++ b/x-pack/plugins/lens/public/index.ts @@ -11,7 +11,7 @@ export type { EmbeddableComponentProps, TypedLensByValueInput, } from './embeddable/embeddable_component'; -export type { XYState, XYLayerConfig } from './xy_visualization/types'; +export type { XYState } from './xy_visualization/types'; export type { DatasourcePublicAPI, DataType, @@ -80,6 +80,7 @@ export type { IconPosition, YConfigResult, DataLayerArgs, + XYLayerConfig, LensMultiTable, ValueLabelMode, AxisExtentMode, diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx index e6003ca0e3c10..cfaf35d08dfc5 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx @@ -209,7 +209,7 @@ export const getXyVisualization = ({ }); if (isReferenceLayer(layer)) { - return getReferenceConfiguration({ state, frame, layer, sortedAccessors, mappedAccessors }); + return getReferenceConfiguration({ state, frame, layer, sortedAccessors }); } const dataLayer: XYDataLayerConfig = layer; From b5534560a70c66e83098cf4b5f4d690cb17b4485 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Fri, 25 Mar 2022 13:26:32 +0200 Subject: [PATCH 062/133] Updated snapshots --- .../__snapshots__/xy_chart.test.tsx.snap | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/public/components/__snapshots__/xy_chart.test.tsx.snap b/src/plugins/chart_expressions/expression_xy/public/components/__snapshots__/xy_chart.test.tsx.snap index 27c269efe2c84..828a62c85cce3 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/__snapshots__/xy_chart.test.tsx.snap +++ b/src/plugins/chart_expressions/expression_xy/public/components/__snapshots__/xy_chart.test.tsx.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`xy_expression XYChart component annotations should render basic annotation 1`] = ` +exports[`XYChart component annotations should render basic annotation 1`] = ` `; -exports[`xy_expression XYChart component annotations should render grouped annotations preserving the shared styles 1`] = ` +exports[`XYChart component annotations should render grouped annotations preserving the shared styles 1`] = ` `; -exports[`xy_expression XYChart component annotations should render grouped annotations with default styles 1`] = ` +exports[`XYChart component annotations should render grouped annotations with default styles 1`] = ` `; -exports[`xy_expression XYChart component annotations should render simplified annotation when hide is true 1`] = ` +exports[`XYChart component annotations should render simplified annotation when hide is true 1`] = ` `; -exports[`xy_expression XYChart component it renders area 1`] = ` +exports[`XYChart component it renders area 1`] = ` @@ -375,7 +375,7 @@ exports[`xy_expression XYChart component it renders area 1`] = ` stackAccessors={Array []} timeZone="UTC" xAccessor="c" - xScaleType="linear" + xScaleType="ordinal" yAccessors={ Array [ "a", @@ -436,7 +436,7 @@ exports[`xy_expression XYChart component it renders area 1`] = ` stackAccessors={Array []} timeZone="UTC" xAccessor="c" - xScaleType="linear" + xScaleType="ordinal" yAccessors={ Array [ "b", @@ -615,7 +615,7 @@ exports[`XYChart component it renders bar 1`] = ` stackAccessors={Array []} timeZone="UTC" xAccessor="c" - xScaleType="linear" + xScaleType="ordinal" yAccessors={ Array [ "a", @@ -682,7 +682,7 @@ exports[`XYChart component it renders bar 1`] = ` stackAccessors={Array []} timeZone="UTC" xAccessor="c" - xScaleType="linear" + xScaleType="ordinal" yAccessors={ Array [ "b", @@ -861,7 +861,7 @@ exports[`XYChart component it renders horizontal bar 1`] = ` stackAccessors={Array []} timeZone="UTC" xAccessor="c" - xScaleType="linear" + xScaleType="ordinal" yAccessors={ Array [ "a", @@ -928,7 +928,7 @@ exports[`XYChart component it renders horizontal bar 1`] = ` stackAccessors={Array []} timeZone="UTC" xAccessor="c" - xScaleType="linear" + xScaleType="ordinal" yAccessors={ Array [ "b", From dec0d184e694958a1a5178ee029d5e778bd23e4a Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Fri, 25 Mar 2022 15:20:48 +0200 Subject: [PATCH 063/133] Fixed tests. --- .../public/components/annotations.tsx | 1 + .../public/components/xy_chart.test.tsx | 28 +++++++++---------- .../public/components/xy_chart.tsx | 5 ++-- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/public/components/annotations.tsx b/src/plugins/chart_expressions/expression_xy/public/components/annotations.tsx index f30c0e2ab305d..d00174d9fd59e 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/annotations.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/annotations.tsx @@ -62,6 +62,7 @@ const groupVisibleConfigsByInterval = ( ) => { return layers .flatMap(({ annotations }) => annotations.filter((a) => !a.isHidden)) + .sort((a, b) => moment(a.time).valueOf() - moment(b.time).valueOf()) .reduce>((acc, current) => { const roundedTimestamp = getRoundedTimestamp( moment(current.time).valueOf(), diff --git a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx index e18d54981d78a..1a8a16c165e9e 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx @@ -145,8 +145,8 @@ describe('XYChart component', () => { describe('date range', () => { const timeSampleLayer: DataLayerConfigResult = { - layerId: 'first', type: 'dataLayer', + layerId: 'first', layerType: LayerTypes.DATA, seriesType: 'line', xAccessor: 'c', @@ -245,8 +245,8 @@ describe('XYChart component', () => { describe('axis time', () => { const defaultTimeLayer: DataLayerConfigResult = { - layerId: 'first', type: 'dataLayer', + layerId: 'first', layerType: LayerTypes.DATA, seriesType: 'line', xAccessor: 'c', @@ -844,8 +844,8 @@ describe('XYChart component', () => { const { args } = sampleArgs(); const numberLayer: DataLayerConfigResult = { - layerId: 'numberLayer', type: 'dataLayer', + layerId: 'numberLayer', layerType: LayerTypes.DATA, hide: false, xAccessor: 'xAccessorId', @@ -966,8 +966,8 @@ describe('XYChart component', () => { ...args, layers: [ { - layerId: 'first', type: 'dataLayer', + layerId: 'first', layerType: LayerTypes.DATA, isHistogram: true, seriesType: 'bar_stacked', @@ -1055,8 +1055,8 @@ describe('XYChart component', () => { const { args } = sampleArgs(); const numberLayer: DataLayerConfigResult = { - layerId: 'numberLayer', type: 'dataLayer', + layerId: 'numberLayer', layerType: LayerTypes.DATA, hide: false, xAccessor: 'xAccessorId', @@ -1175,8 +1175,8 @@ describe('XYChart component', () => { ...args, layers: [ { - layerId: 'first', type: 'dataLayer', + layerId: 'first', layerType: LayerTypes.DATA, seriesType: 'line', xAccessor: 'd', @@ -1223,9 +1223,9 @@ describe('XYChart component', () => { ...args, layers: [ { + type: 'dataLayer', layerId: 'first', layerType: LayerTypes.DATA, - type: 'dataLayer', seriesType: 'line', xAccessor: 'd', accessors: ['a', 'b'], @@ -1254,10 +1254,10 @@ describe('XYChart component', () => { ...args, layers: [ { + type: 'dataLayer', layerId: 'first', layerType: LayerTypes.DATA, seriesType: 'line', - type: 'dataLayer', xAccessor: 'd', accessors: ['a', 'b'], columnToLabel: '{"a": "Label A", "b": "Label B", "d": "Label D"}', @@ -2121,8 +2121,8 @@ describe('XYChart component', () => { }, layers: [ { - layerId: 'first', type: 'dataLayer', + layerId: 'first', layerType: LayerTypes.DATA, seriesType: 'line', xAccessor: 'a', @@ -2135,8 +2135,8 @@ describe('XYChart component', () => { palette: mockPaletteOutput, }, { - layerId: 'second', type: 'dataLayer', + layerId: 'second', layerType: LayerTypes.DATA, seriesType: 'line', xAccessor: 'a', @@ -2213,8 +2213,8 @@ describe('XYChart component', () => { }, layers: [ { - layerId: 'first', type: 'dataLayer', + layerId: 'first', layerType: LayerTypes.DATA, seriesType: 'line', xAccessor: 'a', @@ -2289,8 +2289,8 @@ describe('XYChart component', () => { }, layers: [ { - layerId: 'first', type: 'dataLayer', + layerId: 'first', layerType: LayerTypes.DATA, seriesType: 'line', xAccessor: 'a', @@ -2524,8 +2524,8 @@ describe('XYChart component', () => { }, }; const timeSampleLayer: DataLayerConfigResult = { - layerId: 'first', type: 'dataLayer', + layerId: 'first', layerType: LayerTypes.DATA, seriesType: 'line', xAccessor: 'c', @@ -2604,7 +2604,7 @@ describe('XYChart component', () => { }); test('should render simplified annotation when hide is true', () => { const { data, args } = sampleArgsWithAnnotation(); - (args.layers[0] as DataLayerConfigResult).hide = true; + (args.layers[0] as AnnotationLayerConfigResult).hide = true; const component = mount(); expect(component.find('LineAnnotation')).toMatchSnapshot(); }); diff --git a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx index 450f631947ac7..000edf8d28a9d 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx @@ -183,8 +183,9 @@ export function XYChart({ // use formatting hint of first x axis column to format ticks const xAxisColumn = data.tables[filteredLayers[0].layerId].columns.find( - ({ id }) => isDataLayer(filteredLayers[0]) && id === filteredLayers[0].xAccessor + ({ id }) => id === filteredLayers[0].xAccessor ); + const xAxisFormatter = formatFactory(xAxisColumn && xAxisColumn.meta?.params); const layersAlreadyFormatted: Record = {}; @@ -197,7 +198,7 @@ export function XYChart({ const chartHasMoreThanOneSeries = filteredLayers.length > 1 || filteredLayers.some((layer) => layer.accessors.length > 1) || - filteredLayers.some((layer) => isDataLayer(layer) && layer.splitAccessor); + filteredLayers.some((layer) => layer.splitAccessor); const shouldRotate = isHorizontalChart(filteredLayers); const yAxesConfiguration = getAxesConfiguration( From 628ade6964d379fc985fe747cc29b476ec601495 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Fri, 25 Mar 2022 15:42:35 +0200 Subject: [PATCH 064/133] Fixed dependencies. --- src/plugins/chart_expressions/expression_xy/kibana.json | 2 +- src/plugins/chart_expressions/expression_xy/tsconfig.json | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plugins/chart_expressions/expression_xy/kibana.json b/src/plugins/chart_expressions/expression_xy/kibana.json index bfdec4c88bbe0..e9680d9b85163 100755 --- a/src/plugins/chart_expressions/expression_xy/kibana.json +++ b/src/plugins/chart_expressions/expression_xy/kibana.json @@ -9,7 +9,7 @@ "description": "Expression XY plugin adds a `xy` renderer and function to the expression plugin. The renderer will display the `xy` chart.", "server": true, "ui": true, - "requiredPlugins": ["expressions", "charts", "data", "fieldFormats", "uiActions", "eventAnnotation"], + "requiredPlugins": ["expressions", "charts", "data", "fieldFormats", "uiActions", "eventAnnotation", "visualizations"], "requiredBundles": ["kibanaReact"], "optionalPlugins": [] } diff --git a/src/plugins/chart_expressions/expression_xy/tsconfig.json b/src/plugins/chart_expressions/expression_xy/tsconfig.json index f20b13efbb415..5733914869317 100644 --- a/src/plugins/chart_expressions/expression_xy/tsconfig.json +++ b/src/plugins/chart_expressions/expression_xy/tsconfig.json @@ -21,5 +21,6 @@ { "path": "../../field_formats/tsconfig.json"}, { "path": "../../kibana_utils/tsconfig.json" }, { "path": "../../event_annotation/tsconfig.json" }, + { "path": "../../visualizations/tsconfig.json" }, ] } From cc63956b66e5cf022911fbfe3d1280c2b5f869a0 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Fri, 25 Mar 2022 16:03:36 +0200 Subject: [PATCH 065/133] Fixed i18n. --- .../common/expression_functions/xy_vis.ts | 8 ++--- .../public/helpers/annotations.tsx | 2 +- .../public/helpers/annotations_icon_set.tsx | 30 ++++++++++--------- 3 files changed, 21 insertions(+), 19 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts index 4cd72be4af3dc..0f83aeecc7a20 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts @@ -32,19 +32,19 @@ import { const strings = { getMetricHelp: () => - i18n.translate('xpack.lens.xy.logDatatable.metric', { + i18n.translate('expressionXY.xyVis.logDatatable.metric', { defaultMessage: 'Vertical axis', }), getXAxisHelp: () => - i18n.translate('xpack.lens.xy.logDatatable.x', { + i18n.translate('expressionXY.xyVis.logDatatable.x', { defaultMessage: 'Horizontal axis', }), getBreakdownHelp: () => - i18n.translate('xpack.lens.xy.logDatatable.breakDown', { + i18n.translate('expressionXY.xyVis.logDatatable.breakDown', { defaultMessage: 'Break down by', }), getReferenceLineHelp: () => - i18n.translate('xpack.lens.xy.logDatatable.breakDown', { + i18n.translate('expressionXY.xyVis.logDatatable.breakDown', { defaultMessage: 'Break down by', }), }; diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/annotations.tsx b/src/plugins/chart_expressions/expression_xy/public/helpers/annotations.tsx index 42c3dba44f119..90203799d2e1b 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/annotations.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/annotations.tsx @@ -187,7 +187,7 @@ export const getUniqueLabels = (layers: XYLayerConfig[]) => { while (counts[uniqueLabel] >= 0) { const num = ++counts[uniqueLabel]; - uniqueLabel = i18n.translate('xpack.lens.uniqueLabel', { + uniqueLabel = i18n.translate('expressionXY.uniqueLabel', { defaultMessage: '{label} [{num}]', values: { label, num }, }); diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/annotations_icon_set.tsx b/src/plugins/chart_expressions/expression_xy/public/helpers/annotations_icon_set.tsx index b6c978478d137..99b4648e4d556 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/annotations_icon_set.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/annotations_icon_set.tsx @@ -12,37 +12,37 @@ import { TriangleIcon, CircleIcon } from '../icons'; export const annotationsIconSet = [ { value: 'asterisk', - label: i18n.translate('xpack.lens.xyChart.iconSelect.asteriskIconLabel', { + label: i18n.translate('expressionXY.xyChart.iconSelect.asteriskIconLabel', { defaultMessage: 'Asterisk', }), }, { value: 'alert', - label: i18n.translate('xpack.lens.xyChart.iconSelect.alertIconLabel', { + label: i18n.translate('expressionXY.xyChart.iconSelect.alertIconLabel', { defaultMessage: 'Alert', }), }, { value: 'bell', - label: i18n.translate('xpack.lens.xyChart.iconSelect.bellIconLabel', { + label: i18n.translate('expressionXY.xyChart.iconSelect.bellIconLabel', { defaultMessage: 'Bell', }), }, { value: 'bolt', - label: i18n.translate('xpack.lens.xyChart.iconSelect.boltIconLabel', { + label: i18n.translate('expressionXY.xyChart.iconSelect.boltIconLabel', { defaultMessage: 'Bolt', }), }, { value: 'bug', - label: i18n.translate('xpack.lens.xyChart.iconSelect.bugIconLabel', { + label: i18n.translate('expressionXY.xyChart.iconSelect.bugIconLabel', { defaultMessage: 'Bug', }), }, { value: 'circle', - label: i18n.translate('xpack.lens.xyChart.iconSelect.circleIconLabel', { + label: i18n.translate('expressionXY.xyChart.iconSelect.circleIconLabel', { defaultMessage: 'Circle', }), icon: CircleIcon, @@ -51,45 +51,47 @@ export const annotationsIconSet = [ { value: 'editorComment', - label: i18n.translate('xpack.lens.xyChart.iconSelect.commentIconLabel', { + label: i18n.translate('expressionXY.xyChart.iconSelect.commentIconLabel', { defaultMessage: 'Comment', }), }, { value: 'flag', - label: i18n.translate('xpack.lens.xyChart.iconSelect.flagIconLabel', { + label: i18n.translate('expressionXY.xyChart.iconSelect.flagIconLabel', { defaultMessage: 'Flag', }), }, { value: 'heart', - label: i18n.translate('xpack.lens.xyChart.iconSelect.heartLabel', { defaultMessage: 'Heart' }), + label: i18n.translate('expressionXY.xyChart.iconSelect.heartLabel', { + defaultMessage: 'Heart', + }), }, { value: 'mapMarker', - label: i18n.translate('xpack.lens.xyChart.iconSelect.mapMarkerLabel', { + label: i18n.translate('expressionXY.xyChart.iconSelect.mapMarkerLabel', { defaultMessage: 'Map Marker', }), }, { value: 'pinFilled', - label: i18n.translate('xpack.lens.xyChart.iconSelect.mapPinLabel', { + label: i18n.translate('expressionXY.xyChart.iconSelect.mapPinLabel', { defaultMessage: 'Map Pin', }), }, { value: 'starEmpty', - label: i18n.translate('xpack.lens.xyChart.iconSelect.starLabel', { defaultMessage: 'Star' }), + label: i18n.translate('expressionXY.xyChart.iconSelect.starLabel', { defaultMessage: 'Star' }), }, { value: 'tag', - label: i18n.translate('xpack.lens.xyChart.iconSelect.tagIconLabel', { + label: i18n.translate('expressionXY.xyChart.iconSelect.tagIconLabel', { defaultMessage: 'Tag', }), }, { value: 'triangle', - label: i18n.translate('xpack.lens.xyChart.iconSelect.triangleIconLabel', { + label: i18n.translate('expressionXY.xyChart.iconSelect.triangleIconLabel', { defaultMessage: 'Triangle', }), icon: TriangleIcon, From 0e4092dfbbc653d75b6c3a0147e948acd1348ac6 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Fri, 25 Mar 2022 17:02:12 +0200 Subject: [PATCH 066/133] Moved XY state types to lens. --- .../expression_xy/common/index.ts | 4 - .../common/types/expression_functions.ts | 41 +++------- .../public/helpers/annotations.tsx | 81 ------------------- .../public/helpers/color_assignment.ts | 3 +- .../public/helpers/visualization.ts | 33 ++------ x-pack/plugins/lens/public/index.ts | 10 ++- .../annotations/config_panel/index.tsx | 3 +- .../xy_visualization/annotations/helpers.tsx | 7 +- .../xy_visualization/axes_configuration.ts | 6 +- .../xy_visualization/color_assignment.test.ts | 2 +- .../xy_visualization/color_assignment.ts | 5 +- .../reference_line_helpers.tsx | 4 +- .../public/xy_visualization/state_helpers.ts | 6 +- .../public/xy_visualization/to_expression.ts | 10 ++- .../lens/public/xy_visualization/types.ts | 35 +++++++- .../xy_visualization/visualization.test.ts | 10 ++- .../public/xy_visualization/visualization.tsx | 4 +- .../visualization_helpers.tsx | 10 ++- .../xy_config_panel/axis_settings_popover.tsx | 2 +- .../xy_config_panel/color_picker.tsx | 3 +- .../xy_config_panel/dimension_editor.tsx | 3 +- .../xy_config_panel/reference_line_panel.tsx | 3 +- .../visual_options_popover.test.tsx | 3 +- .../xy_config_panel/xy_config_panel.test.tsx | 3 +- .../xy_visualization/xy_suggestions.test.ts | 12 +-- .../public/xy_visualization/xy_suggestions.ts | 8 +- 26 files changed, 102 insertions(+), 209 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/common/index.ts b/src/plugins/chart_expressions/expression_xy/common/index.ts index bfb2b8f0eddb2..68f9f946baeb0 100755 --- a/src/plugins/chart_expressions/expression_xy/common/index.ts +++ b/src/plugins/chart_expressions/expression_xy/common/index.ts @@ -44,13 +44,11 @@ export type { IconPosition, YConfigResult, DataLayerArgs, - XYLayerConfig, LensMultiTable, ValueLabelMode, AxisExtentMode, FittingFunction, AxisExtentConfig, - XYDataLayerConfig, LegendConfigResult, AxesSettingsConfig, AnnotationLayerArgs, @@ -60,9 +58,7 @@ export type { TickLabelsConfigResult, AxisExtentConfigResult, ReferenceLineLayerArgs, - XYAnnotationLayerConfig, LabelsOrientationConfig, - XYReferenceLineLayerConfig, AnnotationLayerConfigResult, LabelsOrientationConfigResult, ReferenceLineLayerConfigResult, diff --git a/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts b/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts index 04d6867782d78..94b39344605be 100644 --- a/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts +++ b/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts @@ -10,7 +10,7 @@ import { HorizontalAlignment, Position, VerticalAlignment } from '@elastic/chart import { $Values } from '@kbn/utility-types'; import { Datatable } from '../../../../expressions'; import { PaletteOutput } from '../../../../charts/common'; -import { EventAnnotationConfig, EventAnnotationOutput } from '../../../../event_annotation/common'; +import { EventAnnotationOutput } from '../../../../event_annotation/common'; import { AxisExtentModes, FillStyles, @@ -81,23 +81,17 @@ export interface YConfig { textVisibility?: boolean; } -export interface XYDataLayerConfig { +export interface ValidLayer extends DataLayerConfigResult { + xAccessor: NonNullable; +} + +export interface DataLayerArgs { layerId: string; accessors: string[]; - layerType: typeof LayerTypes.DATA; seriesType: SeriesType; xAccessor?: string; hide?: boolean; - yConfig?: YConfig[]; splitAccessor?: string; - palette?: PaletteOutput; -} - -export interface ValidLayer extends DataLayerConfigResult { - xAccessor: NonNullable; -} - -export type DataLayerArgs = Omit & { columnToLabel?: string; // Actually a JSON key-value pair yScaleType: YScaleType; xScaleType: XScaleType; @@ -105,7 +99,7 @@ export type DataLayerArgs = Omit & { // palette will always be set on the expression palette: PaletteOutput; yConfig?: YConfigResult[]; -}; +} export interface LegendConfig { /** @@ -185,13 +179,6 @@ export interface XYArgs { ariaLabel?: string; } -export interface XYAnnotationLayerConfig { - layerId: string; - layerType: typeof LayerTypes.ANNOTATIONS; - annotations: EventAnnotationConfig[]; - hide?: boolean; -} - export interface AnnotationLayerArgs { annotations: EventAnnotationOutput[]; layerId: string; @@ -203,25 +190,15 @@ export type AnnotationLayerConfigResult = AnnotationLayerArgs & { layerType: typeof LayerTypes.ANNOTATIONS; }; -export interface XYReferenceLineLayerConfig { +export interface ReferenceLineLayerArgs { layerId: string; accessors: string[]; - yConfig?: YConfig[]; - layerType: typeof LayerTypes.REFERENCELINE; -} - -export type ReferenceLineLayerArgs = Omit & { columnToLabel?: string; yConfig?: YConfigResult[]; -}; +} export type XYLayerArgs = DataLayerArgs | ReferenceLineLayerArgs | AnnotationLayerArgs; -export type XYLayerConfig = - | XYDataLayerConfig - | XYReferenceLineLayerConfig - | XYAnnotationLayerConfig; - export type XYLayerConfigResult = | DataLayerConfigResult | ReferenceLineLayerConfigResult diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/annotations.tsx b/src/plugins/chart_expressions/expression_xy/public/helpers/annotations.tsx index 90203799d2e1b..8da38af10f5d9 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/annotations.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/annotations.tsx @@ -6,14 +6,8 @@ * Side Public License, v 1. */ import { Position } from '@elastic/charts'; -import { i18n } from '@kbn/i18n'; -import moment from 'moment'; import type { IconPosition, YAxisMode, YConfig } from '../../common/types'; import { hasIcon } from './icon'; -import type { XYDataLayerConfig, XYAnnotationLayerConfig, XYLayerConfig } from '../../common/types'; -import type { FramePublicAPI } from '../types'; -import { getAnnotationsLayersConfig } from './visualization'; -import { defaultAnnotationColor } from '../../../../../../src/plugins/event_annotation/public'; export const LINES_MARKER_SIZE = 20; @@ -132,78 +126,3 @@ export function getBaseIconPlacement( } export const isNumericalString = (value: string) => !isNaN(Number(value)); - -const MAX_DATE = 8640000000000000; -const MIN_DATE = -8640000000000000; - -export function getStaticDate( - dataLayers: XYDataLayerConfig[], - activeData: FramePublicAPI['activeData'] -) { - const fallbackValue = moment().toISOString(); - - const dataLayersId = dataLayers.map(({ layerId }) => layerId); - if ( - !activeData || - Object.entries(activeData) - .filter(([key]) => dataLayersId.includes(key)) - .every(([, { rows }]) => !rows || !rows.length) - ) { - return fallbackValue; - } - - const minDate = dataLayersId.reduce((acc, lId) => { - const xAccessor = dataLayers.find((dataLayer) => dataLayer.layerId === lId)?.xAccessor!; - const firstTimestamp = activeData[lId]?.rows?.[0]?.[xAccessor]; - return firstTimestamp && firstTimestamp < acc ? firstTimestamp : acc; - }, MAX_DATE); - - const maxDate = dataLayersId.reduce((acc, lId) => { - const xAccessor = dataLayers.find((dataLayer) => dataLayer.layerId === lId)?.xAccessor!; - const lastTimestamp = activeData[lId]?.rows?.[activeData?.[lId]?.rows?.length - 1]?.[xAccessor]; - return lastTimestamp && lastTimestamp > acc ? lastTimestamp : acc; - }, MIN_DATE); - const middleDate = (minDate + maxDate) / 2; - return moment(middleDate).toISOString(); -} - -export const getAnnotationsAccessorColorConfig = (layer: XYAnnotationLayerConfig) => { - return layer.annotations.map((annotation) => { - return { - columnId: annotation.id, - triggerIcon: annotation.isHidden ? ('invisible' as const) : ('color' as const), - color: annotation?.color || defaultAnnotationColor, - }; - }); -}; - -export const getUniqueLabels = (layers: XYLayerConfig[]) => { - const annotationLayers = getAnnotationsLayersConfig(layers); - const columnLabelMap = {} as Record; - const counts = {} as Record; - - const makeUnique = (label: string) => { - let uniqueLabel = label; - - while (counts[uniqueLabel] >= 0) { - const num = ++counts[uniqueLabel]; - uniqueLabel = i18n.translate('expressionXY.uniqueLabel', { - defaultMessage: '{label} [{num}]', - values: { label, num }, - }); - } - - counts[uniqueLabel] = 0; - return uniqueLabel; - }; - - annotationLayers.forEach((layer) => { - if (!layer.annotations) { - return; - } - layer.annotations.forEach((l) => { - columnLabelMap[l.id] = makeUnique(l.label); - }); - }); - return columnLabelMap; -}; diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.ts index edb1994090fc2..ef0cd36e3598e 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.ts @@ -12,7 +12,6 @@ import type { Datatable } from '../../../../expressions'; import { FormatFactory } from '../types'; import { isDataLayer } from './visualization'; import { DataLayerConfigResult, XYLayerConfigResult } from '../../common'; -import { XYLayerConfig } from '../../common/types'; const isPrimitive = (value: unknown): boolean => value != null && typeof value !== 'object'; @@ -27,7 +26,7 @@ export type ColorAssignments = Record< >; export function getColorAssignments( - layers: Array, + layers: XYLayerConfigResult[], data: { tables: Record }, formatFactory: FormatFactory ): ColorAssignments { diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/visualization.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/visualization.ts index c1907900249b4..af2e80948ffdf 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/visualization.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/visualization.ts @@ -9,24 +9,16 @@ import { DataLayerConfigResult, ReferenceLineLayerConfigResult, - XYAnnotationLayerConfig, XYLayerConfigResult, - XYLayerConfig, - XYDataLayerConfig, - XYReferenceLineLayerConfig, AnnotationLayerConfigResult, } from '../../common/types'; import { LayerTypes } from '../../common/constants'; -export const isDataLayer = ( - layer: XYLayerConfig | XYLayerConfigResult -): layer is XYDataLayerConfig | DataLayerConfigResult => +export const isDataLayer = (layer: XYLayerConfigResult): layer is DataLayerConfigResult => layer.layerType === LayerTypes.DATA || !layer.layerType; -export const getDataLayers = (layers: Array) => - (layers || []).filter((layer): layer is XYDataLayerConfig | DataLayerConfigResult => - isDataLayer(layer) - ); +export const getDataLayers = (layers: XYLayerConfigResult[]) => + (layers || []).filter((layer): layer is DataLayerConfigResult => isDataLayer(layer)); export const isReferenceLayer = ( layer: XYLayerConfigResult @@ -38,29 +30,14 @@ export const getReferenceLayers = (layers: XYLayerConfigResult[]) => ); const isAnnotationLayerCommon = ( - layer: XYLayerConfig | XYLayerConfigResult -): layer is XYAnnotationLayerConfig | AnnotationLayerConfigResult => - layer.layerType === LayerTypes.ANNOTATIONS; - -export const isAnnotationsLayerConfig = (layer: XYLayerConfig): layer is XYAnnotationLayerConfig => - isAnnotationLayerCommon(layer); + layer: XYLayerConfigResult +): layer is AnnotationLayerConfigResult => layer.layerType === LayerTypes.ANNOTATIONS; export const isAnnotationsLayer = ( layer: XYLayerConfigResult ): layer is AnnotationLayerConfigResult => isAnnotationLayerCommon(layer); -export const getAnnotationsLayersConfig = (layers: XYLayerConfig[]): XYAnnotationLayerConfig[] => - (layers || []).filter((layer): layer is XYAnnotationLayerConfig => - isAnnotationsLayerConfig(layer) - ); - export const getAnnotationsLayers = ( layers: XYLayerConfigResult[] ): AnnotationLayerConfigResult[] => (layers || []).filter((layer): layer is AnnotationLayerConfigResult => isAnnotationsLayer(layer)); - -export interface LayerTypeToLayer { - [LayerTypes.DATA]: (layer: XYDataLayerConfig) => XYDataLayerConfig; - [LayerTypes.REFERENCELINE]: (layer: XYReferenceLineLayerConfig) => XYReferenceLineLayerConfig; - [LayerTypes.ANNOTATIONS]: (layer: XYAnnotationLayerConfig) => XYAnnotationLayerConfig; -} diff --git a/x-pack/plugins/lens/public/index.ts b/x-pack/plugins/lens/public/index.ts index 345bf6a797f52..5b1501410df26 100644 --- a/x-pack/plugins/lens/public/index.ts +++ b/x-pack/plugins/lens/public/index.ts @@ -11,7 +11,13 @@ export type { EmbeddableComponentProps, TypedLensByValueInput, } from './embeddable/embeddable_component'; -export type { XYState } from './xy_visualization/types'; +export type { + XYState, + XYReferenceLineLayerConfig, + XYLayerConfig, + XYDataLayerConfig, + XYAnnotationLayerConfig, +} from './xy_visualization/types'; export type { DatasourcePublicAPI, DataType, @@ -80,7 +86,6 @@ export type { IconPosition, YConfigResult, DataLayerArgs, - XYLayerConfig, LensMultiTable, ValueLabelMode, AxisExtentMode, @@ -94,7 +99,6 @@ export type { AxisExtentConfigResult, ReferenceLineLayerArgs, LabelsOrientationConfig, - XYReferenceLineLayerConfig, LabelsOrientationConfigResult, ReferenceLineLayerConfigResult, AxisTitlesVisibilityConfigResult, diff --git a/x-pack/plugins/lens/public/xy_visualization/annotations/config_panel/index.tsx b/x-pack/plugins/lens/public/xy_visualization/annotations/config_panel/index.tsx index c143dabd2dc8e..c27165accb81d 100644 --- a/x-pack/plugins/lens/public/xy_visualization/annotations/config_panel/index.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/annotations/config_panel/index.tsx @@ -13,9 +13,8 @@ import type { PaletteRegistry } from 'src/plugins/charts/public'; import moment from 'moment'; import { EventAnnotationConfig } from 'src/plugins/event_annotation/common/types'; import type { VisualizationDimensionEditorProps } from '../../../types'; -import { State, XYState } from '../../types'; +import { State, XYState, XYAnnotationLayerConfig } from '../../types'; import { FormatFactory } from '../../../../common'; -import type { XYAnnotationLayerConfig } from '../../../../../../../src/plugins/chart_expressions/expression_xy/common'; import { ColorPicker } from '../../xy_config_panel/color_picker'; import { DimensionEditorSection, NameInput, useDebouncedValue } from '../../../shared_components'; import { isHorizontalChart } from '../../state_helpers'; diff --git a/x-pack/plugins/lens/public/xy_visualization/annotations/helpers.tsx b/x-pack/plugins/lens/public/xy_visualization/annotations/helpers.tsx index 53c8ebc469639..8f18450ba5a21 100644 --- a/x-pack/plugins/lens/public/xy_visualization/annotations/helpers.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/annotations/helpers.tsx @@ -8,14 +8,9 @@ import { i18n } from '@kbn/i18n'; import moment from 'moment'; import { layerTypes } from '../../../common'; -import type { - XYDataLayerConfig, - XYAnnotationLayerConfig, - XYLayerConfig, -} from '../../../../../../src/plugins/chart_expressions/expression_xy/common'; import type { FramePublicAPI, Visualization } from '../../types'; import { isHorizontalChart } from '../state_helpers'; -import type { XYState } from '../types'; +import type { XYState, XYDataLayerConfig, XYAnnotationLayerConfig, XYLayerConfig } from '../types'; import { checkScaleOperation, getAnnotationsLayers, diff --git a/x-pack/plugins/lens/public/xy_visualization/axes_configuration.ts b/x-pack/plugins/lens/public/xy_visualization/axes_configuration.ts index 3f3fb077baf35..b9b2c2ae86e42 100644 --- a/x-pack/plugins/lens/public/xy_visualization/axes_configuration.ts +++ b/x-pack/plugins/lens/public/xy_visualization/axes_configuration.ts @@ -6,15 +6,13 @@ */ import { FormatFactory } from '../../common'; -import { - AxisExtentConfig, - XYDataLayerConfig, -} from '../../../../../src/plugins/chart_expressions/expression_xy/common'; +import { AxisExtentConfig } from '../../../../../src/plugins/chart_expressions/expression_xy/common'; import { Datatable } from '../../../../../src/plugins/expressions/public'; import type { IFieldFormat, SerializedFieldFormat, } from '../../../../../src/plugins/field_formats/common'; +import { XYDataLayerConfig } from './types'; interface FormattedMetric { layer: string; diff --git a/x-pack/plugins/lens/public/xy_visualization/color_assignment.test.ts b/x-pack/plugins/lens/public/xy_visualization/color_assignment.test.ts index 52e74f4ac9654..a329c12b083a5 100644 --- a/x-pack/plugins/lens/public/xy_visualization/color_assignment.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/color_assignment.test.ts @@ -5,10 +5,10 @@ * 2.0. */ -import { XYDataLayerConfig } from '../../../../../src/plugins/chart_expressions/expression_xy/common'; import { getColorAssignments } from './color_assignment'; import type { FormatFactory, LensMultiTable } from '../../common'; import { layerTypes } from '../../common'; +import { XYDataLayerConfig } from './types'; describe('color_assignment', () => { const layers: XYDataLayerConfig[] = [ diff --git a/x-pack/plugins/lens/public/xy_visualization/color_assignment.ts b/x-pack/plugins/lens/public/xy_visualization/color_assignment.ts index 71c28c3060e88..ed1b7f0244c89 100644 --- a/x-pack/plugins/lens/public/xy_visualization/color_assignment.ts +++ b/x-pack/plugins/lens/public/xy_visualization/color_assignment.ts @@ -12,13 +12,10 @@ import { euiLightVars } from '@kbn/ui-theme'; import type { AccessorConfig, FramePublicAPI } from '../types'; import { getColumnToLabelMap } from './state_helpers'; import { FormatFactory } from '../../common'; -import { - XYDataLayerConfig, - XYLayerConfig, -} from '../../../../../src/plugins/chart_expressions/expression_xy/common'; import { isDataLayer, isReferenceLayer, isAnnotationsLayer } from './visualization_helpers'; import { getAnnotationsAccessorColorConfig } from './annotations/helpers'; import { getReferenceLineAccessorColorConfig } from './reference_line_helpers'; +import { XYDataLayerConfig, XYLayerConfig } from './types'; const isPrimitive = (value: unknown): boolean => value != null && typeof value !== 'object'; diff --git a/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx b/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx index 9a36f5805dbc3..af679d1354792 100644 --- a/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx @@ -11,14 +11,12 @@ import { layerTypes } from '../../common'; import type { YAxisMode, YConfig, - XYDataLayerConfig, - XYReferenceLineLayerConfig, } from '../../../../../src/plugins/chart_expressions/expression_xy/common'; import { Datatable } from '../../../../../src/plugins/expressions/public'; import type { DatasourcePublicAPI, FramePublicAPI, Visualization } from '../types'; import { groupAxesByType } from './axes_configuration'; import { isHorizontalChart, isPercentageSeries, isStackedChart } from './state_helpers'; -import type { XYState } from './types'; +import type { XYState, XYDataLayerConfig, XYReferenceLineLayerConfig } from './types'; import { checkScaleOperation, getAxisName, diff --git a/x-pack/plugins/lens/public/xy_visualization/state_helpers.ts b/x-pack/plugins/lens/public/xy_visualization/state_helpers.ts index 3add251efb2ca..d3c8efae33836 100644 --- a/x-pack/plugins/lens/public/xy_visualization/state_helpers.ts +++ b/x-pack/plugins/lens/public/xy_visualization/state_helpers.ts @@ -11,11 +11,13 @@ import type { SeriesType, YConfig, ValidLayer, +} from '../../../../../src/plugins/chart_expressions/expression_xy/common'; +import { + visualizationTypes, XYLayerConfig, XYDataLayerConfig, XYReferenceLineLayerConfig, -} from '../../../../../src/plugins/chart_expressions/expression_xy/common'; -import { visualizationTypes } from './types'; +} from './types'; import { getDataLayers, isAnnotationsLayer, isDataLayer } from './visualization_helpers'; export function isHorizontalSeries(seriesType: SeriesType) { diff --git a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts index a36633bb95d0a..ec9093a999c84 100644 --- a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts +++ b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts @@ -10,15 +10,17 @@ import { ScaleType } from '@elastic/charts'; import { PaletteRegistry } from 'src/plugins/charts/public'; import { EventAnnotationServiceType } from 'src/plugins/event_annotation/public'; -import { State } from './types'; +import { + State, + XYDataLayerConfig, + XYReferenceLineLayerConfig, + XYAnnotationLayerConfig, +} from './types'; import { OperationMetadata, DatasourcePublicAPI } from '../types'; import { getColumnToLabelMap } from './state_helpers'; import type { ValidLayer, YConfig, - XYDataLayerConfig, - XYReferenceLineLayerConfig, - XYAnnotationLayerConfig, } from '../../../../../src/plugins/chart_expressions/expression_xy/common'; import { hasIcon } from './xy_config_panel/shared/icon_select'; import { defaultReferenceLineColor } from './color_assignment'; diff --git a/x-pack/plugins/lens/public/xy_visualization/types.ts b/x-pack/plugins/lens/public/xy_visualization/types.ts index 34f544ecb6bd5..9fcb1951d5ac2 100644 --- a/x-pack/plugins/lens/public/xy_visualization/types.ts +++ b/x-pack/plugins/lens/public/xy_visualization/types.ts @@ -18,6 +18,7 @@ import { LensIconChartBarHorizontalPercentage } from '../assets/chart_bar_horizo import { LensIconChartLine } from '../assets/chart_line'; import type { VisualizationType, Suggestion } from '../types'; +import { PaletteOutput } from '../../../../../src/plugins/charts/common'; import type { SeriesType, LegendConfig, @@ -27,10 +28,42 @@ import type { FittingFunction, LabelsOrientationConfig, EndValue, - XYLayerConfig, + YConfig, } from '../../../../../src/plugins/chart_expressions/expression_xy/common'; +import { EventAnnotationConfig } from '../../../../../src/plugins/event_annotation/common'; import type { ValueLabelConfig } from '../../common/types'; +export interface XYDataLayerConfig { + layerId: string; + accessors: string[]; + layerType: 'data'; + seriesType: SeriesType; + xAccessor?: string; + hide?: boolean; + yConfig?: YConfig[]; + splitAccessor?: string; + palette?: PaletteOutput; +} + +export interface XYReferenceLineLayerConfig { + layerId: string; + accessors: string[]; + yConfig?: YConfig[]; + layerType: 'referenceLine'; +} + +export interface XYAnnotationLayerConfig { + layerId: string; + layerType: 'annotations'; + annotations: EventAnnotationConfig[]; + hide?: boolean; +} + +export type XYLayerConfig = + | XYDataLayerConfig + | XYReferenceLineLayerConfig + | XYAnnotationLayerConfig; + // Persisted parts of the state export interface XYState { preferredSeriesType: SeriesType; diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts b/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts index 4bd5ebf0cb0a8..520f8fdcb38cc 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts @@ -8,13 +8,17 @@ import { getXyVisualization } from './visualization'; import { Position } from '@elastic/charts'; import { Operation, VisualizeEditorContext, Suggestion, OperationDescriptor } from '../types'; -import type { State, XYState, XYSuggestion } from './types'; import type { - DataLayerConfigResult, - SeriesType, + State, + XYState, + XYSuggestion, XYLayerConfig, XYDataLayerConfig, XYReferenceLineLayerConfig, +} from './types'; +import type { + DataLayerConfigResult, + SeriesType, } from '../../../../../src/plugins/chart_expressions/expression_xy/common'; import { layerTypes } from '../../common'; import { createMockDatasource, createMockFramePublicAPI } from '../mocks'; diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx index cfaf35d08dfc5..1a6af0dc36475 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx @@ -26,10 +26,8 @@ import { SeriesType, YAxisMode, YConfig, - XYLayerConfig, - XYDataLayerConfig, } from '../../../../../src/plugins/chart_expressions/expression_xy/common'; -import { State, visualizationTypes, XYSuggestion } from './types'; +import { State, visualizationTypes, XYSuggestion, XYLayerConfig, XYDataLayerConfig } from './types'; import { layerTypes } from '../../common'; import { isHorizontalChart } from './state_helpers'; import { toExpression, toPreviewExpression, getSortedAccessors } from './to_expression'; diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization_helpers.tsx b/x-pack/plugins/lens/public/xy_visualization/visualization_helpers.tsx index 68c8c80f42b35..d680494ef8315 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization_helpers.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/visualization_helpers.tsx @@ -8,15 +8,17 @@ import { i18n } from '@kbn/i18n'; import { uniq } from 'lodash'; import { DatasourcePublicAPI, OperationMetadata, VisualizationType } from '../types'; -import { State, visualizationTypes, XYState } from './types'; -import { isHorizontalChart } from './state_helpers'; import { - SeriesType, + State, + visualizationTypes, + XYState, XYAnnotationLayerConfig, XYLayerConfig, XYDataLayerConfig, XYReferenceLineLayerConfig, -} from '../../../../../src/plugins/chart_expressions/expression_xy/common'; +} from './types'; +import { isHorizontalChart } from './state_helpers'; +import { SeriesType } from '../../../../../src/plugins/chart_expressions/expression_xy/common'; import { layerTypes } from '..'; import { LensIconChartBarHorizontal } from '../assets/chart_bar_horizontal'; import { LensIconChartMixedXy } from '../assets/chart_mixed_xy'; diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/axis_settings_popover.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/axis_settings_popover.tsx index 667c2caa267aa..340a9211fcdee 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/axis_settings_popover.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/axis_settings_popover.tsx @@ -20,8 +20,8 @@ import { isEqual } from 'lodash'; import { AxesSettingsConfig, AxisExtentConfig, - XYLayerConfig, } from '../../../../../../src/plugins/chart_expressions/expression_xy/common'; +import { XYLayerConfig } from '../types'; import { ToolbarPopover, useDebouncedValue, diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/color_picker.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/color_picker.tsx index aecb3d5d3ad1b..3f801fb92876d 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/color_picker.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/color_picker.tsx @@ -10,9 +10,8 @@ import { i18n } from '@kbn/i18n'; import { EuiFormRow, EuiColorPicker, EuiColorPickerProps, EuiToolTip, EuiIcon } from '@elastic/eui'; import type { PaletteRegistry } from 'src/plugins/charts/public'; import { defaultAnnotationColor } from '../../../../../../src/plugins/event_annotation/public'; -import { XYDataLayerConfig } from '../../../../../../src/plugins/chart_expressions/expression_xy/common'; import type { VisualizationDimensionEditorProps } from '../../types'; -import { State } from '../types'; +import { State, XYDataLayerConfig } from '../types'; import { FormatFactory } from '../../../common'; import { getSeriesColor } from '../state_helpers'; import { diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/dimension_editor.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/dimension_editor.tsx index ab8f9a423a67f..b3e13ece50434 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/dimension_editor.tsx @@ -10,12 +10,11 @@ import { i18n } from '@kbn/i18n'; import { EuiButtonGroup, EuiFormRow, htmlIdGenerator } from '@elastic/eui'; import type { PaletteRegistry } from 'src/plugins/charts/public'; import type { VisualizationDimensionEditorProps } from '../../types'; -import { State, XYState } from '../types'; +import { State, XYState, XYDataLayerConfig } from '../types'; import { FormatFactory } from '../../../common'; import { YAxisMode, YConfig, - XYDataLayerConfig, } from '../../../../../../src/plugins/chart_expressions/expression_xy/common'; import { isHorizontalChart } from '../state_helpers'; import { ColorPicker } from './color_picker'; diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_panel.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_panel.tsx index 5674bb2637666..ffca2c0531b7c 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_panel.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_panel.tsx @@ -10,12 +10,11 @@ import { i18n } from '@kbn/i18n'; import { EuiButtonGroup, EuiFormRow } from '@elastic/eui'; import type { PaletteRegistry } from 'src/plugins/charts/public'; import type { VisualizationDimensionEditorProps } from '../../types'; -import { State, XYState } from '../types'; +import { State, XYState, XYReferenceLineLayerConfig } from '../types'; import { FormatFactory } from '../../../common'; import { FillStyle, YConfig, - XYReferenceLineLayerConfig, } from '../../../../../../src/plugins/chart_expressions/expression_xy/common'; import { ColorPicker } from './color_picker'; diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/visual_options_popover.test.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/visual_options_popover.test.tsx index d88a08e68422d..5b91ee70c6945 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/visual_options_popover.test.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/visual_options_popover.test.tsx @@ -10,8 +10,7 @@ import { shallowWithIntl as shallow } from '@kbn/test-jest-helpers'; import { Position } from '@elastic/charts'; import type { FramePublicAPI } from '../../../types'; import { createMockDatasource, createMockFramePublicAPI } from '../../../mocks'; -import { XYLayerConfig } from '../../../../../../../src/plugins/chart_expressions/expression_xy/common'; -import { State } from '../../types'; +import { State, XYLayerConfig } from '../../types'; import { VisualOptionsPopover } from '.'; import { ToolbarPopover, ValueLabelsSettings } from '../../../shared_components'; import { MissingValuesOptions } from './missing_values_option'; diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/xy_config_panel.test.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/xy_config_panel.test.tsx index c7e5b6dc8b4ff..953828db48a72 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/xy_config_panel.test.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/xy_config_panel.test.tsx @@ -12,13 +12,12 @@ import { XyToolbar } from '.'; import { DimensionEditor } from './dimension_editor'; import { AxisSettingsPopover } from './axis_settings_popover'; import { FramePublicAPI } from '../../types'; -import { State, XYState } from '../types'; +import { State, XYState, XYDataLayerConfig } from '../types'; import { Position } from '@elastic/charts'; import { createMockFramePublicAPI, createMockDatasource } from '../../mocks'; import { chartPluginMock } from 'src/plugins/charts/public/mocks'; import { EuiColorPicker } from '@elastic/eui'; import { layerTypes } from '../../../common'; -import { XYDataLayerConfig } from '../../../../../../src/plugins/chart_expressions/expression_xy/common'; describe('XY Config panels', () => { let frame: FramePublicAPI; diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.test.ts b/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.test.ts index bd0f4ddf14986..941e50d6e5285 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.test.ts @@ -7,15 +7,17 @@ import { getSuggestions } from './xy_suggestions'; import type { TableSuggestionColumn, VisualizationSuggestion, TableSuggestion } from '../types'; -import { State, XYState, visualizationTypes } from './types'; +import { + State, + XYState, + visualizationTypes, + XYAnnotationLayerConfig, + XYDataLayerConfig, +} from './types'; import { generateId } from '../id_generator'; import { getXyVisualization } from './xy_visualization'; import { chartPluginMock } from '../../../../../src/plugins/charts/public/mocks'; import { eventAnnotationServiceMock } from '../../../../../src/plugins/event_annotation/public/mocks'; -import { - XYAnnotationLayerConfig, - XYDataLayerConfig, -} from '../../../../../src/plugins/chart_expressions/expression_xy/common'; import { PaletteOutput } from 'src/plugins/charts/public'; import { layerTypes } from '../../common'; import { fieldFormatsServiceMock } from '../../../../../src/plugins/field_formats/public/mocks'; diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.ts b/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.ts index eead82862e630..610e21c1fe138 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.ts +++ b/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.ts @@ -16,12 +16,8 @@ import { TableSuggestion, TableChangeType, } from '../types'; -import { State, XYState, visualizationTypes } from './types'; -import type { - SeriesType, - XYLayerConfig, - XYDataLayerConfig, -} from '../../../../../src/plugins/chart_expressions/expression_xy/common'; +import { State, XYState, visualizationTypes, XYLayerConfig, XYDataLayerConfig } from './types'; +import type { SeriesType } from '../../../../../src/plugins/chart_expressions/expression_xy/common'; import { layerTypes } from '../../common'; import { getIconForSeries } from './state_helpers'; import { getDataLayers, isDataLayer } from './visualization_helpers'; From ef30205607676a159666641648a2da9a22113ead Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Fri, 25 Mar 2022 17:07:49 +0200 Subject: [PATCH 067/133] Fixed more types. --- .../reference_line_helpers.test.ts | 54 +++++++++---------- .../xy_visualization/visualization.test.ts | 7 +-- .../xy_config_panel/layer_header.tsx | 9 ++-- 3 files changed, 32 insertions(+), 38 deletions(-) diff --git a/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.test.ts b/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.test.ts index 4448d14576f5a..368b213428ed7 100644 --- a/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.test.ts @@ -5,9 +5,9 @@ * 2.0. */ -import { DataLayerConfigResult } from '../../../../../src/plugins/chart_expressions/expression_xy/common'; import { FramePublicAPI } from '../types'; import { computeOverallDataDomain, getStaticValue } from './reference_line_helpers'; +import { XYDataLayerConfig } from './types'; function getActiveData(json: Array<{ id: string; rows: Array> }>) { return json.reduce((memo, { id, rows }) => { @@ -51,7 +51,7 @@ describe('reference_line helpers', () => { // accessor id has no hit in data expect( getStaticValue( - [{ layerId: 'id-a', seriesType: 'area' } as DataLayerConfigResult], // missing xAccessor for groupId == x + [{ layerId: 'id-a', seriesType: 'area' } as XYDataLayerConfig], // missing xAccessor for groupId == x 'x', { activeData: getActiveData([ @@ -69,7 +69,7 @@ describe('reference_line helpers', () => { seriesType: 'area', layerType: 'data', accessors: ['d'], - } as DataLayerConfigResult, + } as XYDataLayerConfig, ], // missing hit of accessor "d" in data 'yLeft', { @@ -88,7 +88,7 @@ describe('reference_line helpers', () => { seriesType: 'area', layerType: 'data', accessors: ['a'], - } as DataLayerConfigResult, + } as XYDataLayerConfig, ], // missing yConfig fallbacks to left axis, but the requested group is yRight 'yRight', { @@ -107,7 +107,7 @@ describe('reference_line helpers', () => { seriesType: 'area', layerType: 'data', accessors: ['a'], - } as DataLayerConfigResult, + } as XYDataLayerConfig, ], // same as above with x groupId 'x', { @@ -130,7 +130,7 @@ describe('reference_line helpers', () => { layerType: 'data', accessors: ['a'], yConfig: [{ forAccessor: 'a', axisMode: 'right' }], - } as DataLayerConfigResult, + } as XYDataLayerConfig, ], 'yRight', { @@ -155,7 +155,7 @@ describe('reference_line helpers', () => { seriesType: 'area', layerType: 'data', accessors: ['a'], - } as DataLayerConfigResult, + } as XYDataLayerConfig, ], 'yLeft', { @@ -178,7 +178,7 @@ describe('reference_line helpers', () => { layerType: 'data', accessors: ['a'], yConfig: [{ forAccessor: 'a', axisMode: 'right' }], - } as DataLayerConfigResult, + } as XYDataLayerConfig, ], 'yRight', { @@ -205,7 +205,7 @@ describe('reference_line helpers', () => { seriesType: 'area', layerType: 'data', accessors: ['a', 'b'], - } as DataLayerConfigResult, + } as XYDataLayerConfig, ], 'yLeft', { activeData: tables }, @@ -220,7 +220,7 @@ describe('reference_line helpers', () => { seriesType: 'area', layerType: 'data', accessors: ['a', 'b'], - } as DataLayerConfigResult, + } as XYDataLayerConfig, ], 'yRight', { activeData: tables }, @@ -243,7 +243,7 @@ describe('reference_line helpers', () => { seriesType: 'area', layerType: 'data', accessors: ['a', 'b'], - } as DataLayerConfigResult, + } as XYDataLayerConfig, ], 'yLeft', { activeData: tables }, @@ -258,7 +258,7 @@ describe('reference_line helpers', () => { seriesType: 'area', layerType: 'data', accessors: ['a', 'b'], - } as DataLayerConfigResult, + } as XYDataLayerConfig, ], 'yRight', { activeData: tables }, @@ -282,7 +282,7 @@ describe('reference_line helpers', () => { xScaleType: 'linear', isHistogram: false, palette: { type: 'palette', name: 'palette1' }, - } as DataLayerConfigResult, + } as XYDataLayerConfig, ], 'x', // this is influenced by the callback { @@ -310,7 +310,7 @@ describe('reference_line helpers', () => { xScaleType: 'linear', isHistogram: false, palette: { type: 'palette', name: 'palette1' }, - } as DataLayerConfigResult, + } as XYDataLayerConfig, ], 'x', { @@ -334,7 +334,7 @@ describe('reference_line helpers', () => { for (const seriesType of ['bar_stacked', 'bar_horizontal_stacked', 'area_stacked']) expect( computeOverallDataDomain( - [{ layerId: 'id-a', seriesType, accessors: ['a', 'b', 'c'] } as DataLayerConfigResult], + [{ layerId: 'id-a', seriesType, accessors: ['a', 'b', 'c'] } as XYDataLayerConfig], ['a', 'b', 'c'], getActiveData([ { @@ -360,7 +360,7 @@ describe('reference_line helpers', () => { ]) expect( computeOverallDataDomain( - [{ layerId: 'id-a', seriesType, accessors: ['a', 'b', 'c'] } as DataLayerConfigResult], + [{ layerId: 'id-a', seriesType, accessors: ['a', 'b', 'c'] } as XYDataLayerConfig], ['a', 'b', 'c'], getActiveData([ { @@ -385,7 +385,7 @@ describe('reference_line helpers', () => { [ { layerId: 'id-a', seriesType, accessors: ['a', 'b', 'c'] }, { layerId: 'id-b', seriesType, accessors: ['d', 'e', 'f'] }, - ] as DataLayerConfigResult[], + ] as XYDataLayerConfig[], ['a', 'b', 'c', 'd', 'e', 'f'], getActiveData([ { id: 'id-a', rows: [{ a: 25, b: 100, c: 100 }] }, @@ -399,7 +399,7 @@ describe('reference_line helpers', () => { [ { layerId: 'id-a', seriesType, accessors: ['a', 'b', 'c'] }, { layerId: 'id-b', seriesType, accessors: ['d', 'e', 'f'] }, - ] as DataLayerConfigResult[], + ] as XYDataLayerConfig[], ['a', 'b', 'c', 'd', 'e', 'f'], getActiveData([ { @@ -435,7 +435,7 @@ describe('reference_line helpers', () => { [ { layerId: 'id-a', seriesType, accessors: ['a', 'b', 'c'] }, { layerId: 'id-b', seriesType, accessors: ['d', 'e', 'f'] }, - ] as DataLayerConfigResult[], + ] as XYDataLayerConfig[], ['a', 'b', 'c', 'd', 'e', 'f'], getActiveData([ { id: 'id-a', rows: Array(3).fill({ a: 100, b: 100, c: 100 }) }, @@ -453,7 +453,7 @@ describe('reference_line helpers', () => { [ { layerId: 'id-a', seriesType: nonStackedSeries, accessors: ['a', 'b', 'c'] }, { layerId: 'id-b', seriesType: stackedSeries, accessors: ['d', 'e', 'f'] }, - ] as DataLayerConfigResult[], + ] as XYDataLayerConfig[], ['a', 'b', 'c', 'd', 'e', 'f'], getActiveData([ { id: 'id-a', rows: [{ a: 100, b: 100, c: 100 }] }, @@ -475,7 +475,7 @@ describe('reference_line helpers', () => { [ { layerId: 'id-a', seriesType, xAccessor: 'c', accessors: ['a', 'b'] }, { layerId: 'id-b', seriesType, xAccessor: 'f', accessors: ['d', 'e'] }, - ] as DataLayerConfigResult[], + ] as XYDataLayerConfig[], ['a', 'b', 'd', 'e'], getActiveData([ { @@ -502,7 +502,7 @@ describe('reference_line helpers', () => { [ { layerId: 'id-a', seriesType, accessors: ['c'] }, { layerId: 'id-b', seriesType, accessors: ['f'] }, - ] as DataLayerConfigResult[], + ] as XYDataLayerConfig[], ['c', 'f'], getActiveData([ { @@ -530,7 +530,7 @@ describe('reference_line helpers', () => { [ { layerId: 'id-a', seriesType, xAccessor: 'c', accessors: ['a', 'b'] }, { layerId: 'id-b', seriesType, xAccessor: 'f', accessors: ['d', 'e'] }, - ] as DataLayerConfigResult[], + ] as XYDataLayerConfig[], ['a', 'b', 'd', 'e'], getActiveData([ { @@ -559,7 +559,7 @@ describe('reference_line helpers', () => { layerId: 'id-a', seriesType: 'area_stacked', accessors: ['a', 'b', 'c'], - } as DataLayerConfigResult, + } as XYDataLayerConfig, ], ['a', 'b', 'c'], getActiveData([ @@ -583,7 +583,7 @@ describe('reference_line helpers', () => { layerId: 'id-a', seriesType: 'area', accessors: ['a', 'b', 'c'], - } as DataLayerConfigResult, + } as XYDataLayerConfig, ], ['a', 'b', 'c'], getActiveData([ @@ -618,7 +618,7 @@ describe('reference_line helpers', () => { [ { layerId: 'id-a', seriesType: 'area', accessors: ['a', 'b', 'c'] }, { layerId: 'id-b', seriesType: 'line', accessors: ['d', 'e', 'f'] }, - ] as DataLayerConfigResult[], + ] as XYDataLayerConfig[], ['a', 'b'], getActiveData([{ id: 'id-c', rows: [{ a: 100, b: 100 }] }]) // mind the layer id here ) @@ -629,7 +629,7 @@ describe('reference_line helpers', () => { [ { layerId: 'id-a', seriesType: 'bar', accessors: ['a', 'b', 'c'] }, { layerId: 'id-b', seriesType: 'bar_stacked' }, - ] as DataLayerConfigResult[], + ] as XYDataLayerConfig[], ['a', 'b'], getActiveData([]) ) diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts b/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts index 520f8fdcb38cc..18cd16c17b365 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts @@ -16,10 +16,7 @@ import type { XYDataLayerConfig, XYReferenceLineLayerConfig, } from './types'; -import type { - DataLayerConfigResult, - SeriesType, -} from '../../../../../src/plugins/chart_expressions/expression_xy/common'; +import type { SeriesType } from '../../../../../src/plugins/chart_expressions/expression_xy/common'; import { layerTypes } from '../../common'; import { createMockDatasource, createMockFramePublicAPI } from '../mocks'; import { LensIconChartBar } from '../assets/chart_bar'; @@ -137,7 +134,7 @@ describe('xy_visualization', () => { return { ...state, layers: types.map((t, i) => ({ - ...(state.layers[0] as DataLayerConfigResult), + ...(state.layers[0] as XYDataLayerConfig), layerId: `layer_${i}`, seriesType: t, })), diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/layer_header.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/layer_header.tsx index 6aee6df1a88d6..2aabf255c5993 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/layer_header.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/layer_header.tsx @@ -9,11 +9,8 @@ import React, { useState } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiIcon, EuiPopover, EuiSelectable, EuiText, EuiPopoverTitle } from '@elastic/eui'; import type { VisualizationLayerWidgetProps, VisualizationType } from '../../types'; -import { State, visualizationTypes } from '../types'; -import { - DataLayerConfigResult, - SeriesType, -} from '../../../../../../src/plugins/chart_expressions/expression_xy/common'; +import { State, visualizationTypes, XYDataLayerConfig } from '../types'; +import { SeriesType } from '../../../../../../src/plugins/chart_expressions/expression_xy/common'; import { isHorizontalChart, isHorizontalSeries } from '../state_helpers'; import { trackUiEvent } from '../../lens_ui_telemetry'; import { StaticHeader } from '../../shared_components'; @@ -61,7 +58,7 @@ function AnnotationsLayerHeader() { function DataLayerHeader(props: VisualizationLayerWidgetProps) { const [isPopoverOpen, setPopoverIsOpen] = useState(false); const { state, layerId } = props; - const layers = state.layers as DataLayerConfigResult[]; + const layers = state.layers as XYDataLayerConfig[]; const index = layers.findIndex((l) => l.layerId === layerId); const layer = layers[index]; const currentVisType = visualizationTypes.find(({ id }) => id === layer.seriesType)!; From 81399866ec75c1cbc592adea0b4fd72d1ada8460 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Mon, 28 Mar 2022 13:03:55 +0300 Subject: [PATCH 068/133] Update src/plugins/chart_expressions/expression_xy/README.md Co-authored-by: Marta Bondyra --- src/plugins/chart_expressions/expression_xy/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/chart_expressions/expression_xy/README.md b/src/plugins/chart_expressions/expression_xy/README.md index 3b99441811825..5ad68bebd40fb 100755 --- a/src/plugins/chart_expressions/expression_xy/README.md +++ b/src/plugins/chart_expressions/expression_xy/README.md @@ -1,6 +1,6 @@ # expressionXY -A Kibana plugin +Expression XY plugin adds a `xy` renderer and function to the expression plugin. The renderer will display the `xy` chart. --- From 8307b736c0e0da1c54b2263068fc360e8a858a70 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Mon, 28 Mar 2022 10:11:02 +0000 Subject: [PATCH 069/133] [CI] Auto-commit changed files from 'node scripts/build_plugin_list_docs' --- docs/developer/plugin-list.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index 4d238f6cf7363..0b9e48020c680 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -165,7 +165,7 @@ for use in their own application. |{kib-repo}blob/{branch}/src/plugins/chart_expressions/expression_xy/README.md[expressionXY] -|A Kibana plugin +|Expression XY plugin adds a xy renderer and function to the expression plugin. The renderer will display the xy chart. |{kib-repo}blob/{branch}/src/plugins/field_formats/README.md[fieldFormats] From 500fb7833fccf56be6e45409709e7cf6d7b3e1ac Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Mon, 28 Mar 2022 15:46:34 +0300 Subject: [PATCH 070/133] Removed yConfig from *Layers types --- .../expression_xy/common/types/expression_functions.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts b/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts index 94b39344605be..98889da771c04 100644 --- a/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts +++ b/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts @@ -213,16 +213,14 @@ export interface LensMultiTable { }; } -export type ReferenceLineLayerConfigResult = Omit & { +export type ReferenceLineLayerConfigResult = ReferenceLineLayerArgs & { type: typeof REFERENCE_LINE_LAYER; layerType: typeof LayerTypes.REFERENCELINE; - yConfig?: YConfigResult[]; }; -export type DataLayerConfigResult = Omit & { +export type DataLayerConfigResult = DataLayerArgs & { type: typeof DATA_LAYER; layerType: typeof LayerTypes.DATA; - yConfig?: YConfigResult[]; }; export type YConfigResult = YConfig & { type: typeof Y_CONFIG }; From 839b0234934d437ca1b0c49e2669ccf70e1ee736 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Mon, 28 Mar 2022 16:09:57 +0300 Subject: [PATCH 071/133] Fixed styles. --- .../public/components/annotations.scss | 25 +++---------------- .../public/components/annotations.tsx | 10 ++++---- .../public/components/reference_lines.scss | 4 +-- .../public/components/reference_lines.tsx | 4 +-- .../public/components/xy_chart.scss | 10 +------- .../public/components/xy_chart.tsx | 2 +- .../xy_chart_renderer.tsx | 5 +++- .../static/components/empty_placeholder.tsx | 5 +++- 8 files changed, 22 insertions(+), 43 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/public/components/annotations.scss b/src/plugins/chart_expressions/expression_xy/public/components/annotations.scss index fc2b1204bb1d0..88881ae718925 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/annotations.scss +++ b/src/plugins/chart_expressions/expression_xy/public/components/annotations.scss @@ -1,37 +1,18 @@ -.lnsXyDecorationRotatedWrapper { - display: inline-block; - overflow: hidden; - line-height: 1.5; - - .lnsXyDecorationRotatedWrapper__label { - display: inline-block; - white-space: nowrap; - transform: translate(0, 100%) rotate(-90deg); - transform-origin: 0 0; - - &::after { - content: ''; - float: left; - margin-top: 100%; - } - } -} - -.lnsXyAnnotationNumberIcon { +.xyAnnotationNumberIcon { border-radius: $euiSize; min-width: $euiSize; height: $euiSize; background-color: currentColor; } -.lnsXyAnnotationNumberIcon__text { +.xyAnnotationNumberIcon__text { font-weight: 500; font-size: 9px; letter-spacing: -.5px; line-height: 11px; } -.lnsXyAnnotationIcon_rotate90 { +.xyAnnotationIcon_rotate90 { transform: rotate(45deg); transform-origin: center; } diff --git a/src/plugins/chart_expressions/expression_xy/public/components/annotations.tsx b/src/plugins/chart_expressions/expression_xy/public/components/annotations.tsx index d00174d9fd59e..4e8fa1b95775f 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/annotations.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/annotations.tsx @@ -190,7 +190,7 @@ export const Annotations = ({ isHorizontal: !isHorizontal, hasReducedPadding, label: annotation.label, - rotateClassName: isHorizontal ? 'lnsXyAnnotationIcon_rotate90' : undefined, + rotateClassName: isHorizontal ? 'xyAnnotationIcon_rotate90' : undefined, }} /> ) : undefined @@ -255,13 +255,13 @@ export function MarkerBody({ } return (
- + {number < 10 ? number : `9+`} diff --git a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.scss b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.scss index 07946b52b0000..2cd7fb9c26915 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.scss +++ b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.scss @@ -1,9 +1,9 @@ -.lnsXyDecorationRotatedWrapper { +.xyDecorationRotatedWrapper { display: inline-block; overflow: hidden; line-height: 1.5; - .lnsXyDecorationRotatedWrapper__label { + .xyDecorationRotatedWrapper__label { display: inline-block; white-space: nowrap; transform: translate(0, 100%) rotate(-90deg); diff --git a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.tsx b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.tsx index b151e495844b9..65bc91c06efe5 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.tsx @@ -141,13 +141,13 @@ function getMarkerBody(label: string | undefined, isHorizontal: boolean) { } return (
; + return ; } // use formatting hint of first x axis column to format ticks diff --git a/src/plugins/chart_expressions/expression_xy/public/expression_renderers/xy_chart_renderer.tsx b/src/plugins/chart_expressions/expression_xy/public/expression_renderers/xy_chart_renderer.tsx index 95fcaa87ee00e..70acc25330b87 100644 --- a/src/plugins/chart_expressions/expression_xy/public/expression_renderers/xy_chart_renderer.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/expression_renderers/xy_chart_renderer.tsx @@ -60,7 +60,10 @@ export const getXyChartRenderer = ({ ReactDOM.render( -
+
, dataTestSubj = 'emptyPlaceholder', + className, }: { icon: IconType; iconColor?: string; message?: JSX.Element; dataTestSubj?: string; + className?: string; }) => ( <> Date: Mon, 28 Mar 2022 16:19:53 +0300 Subject: [PATCH 072/133] Fixed types. --- .../public/helpers/reference_lines.ts | 4 +- .../expression_xy/public/types.ts | 38 +------------------ 2 files changed, 3 insertions(+), 39 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/reference_lines.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/reference_lines.ts index 86a87e22157c7..35419c3a40558 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/reference_lines.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/reference_lines.ts @@ -7,14 +7,14 @@ */ import { partition } from 'lodash'; +import { Datatable } from '../../../../expressions'; import type { DataLayerConfigResult } from '../../common'; -import type { FramePublicAPI } from '../types'; import { isStackedChart } from './state'; export function computeOverallDataDomain( dataLayers: DataLayerConfigResult[], accessorIds: string[], - activeData: NonNullable, + activeData: Record, allowStacking: boolean = true ) { const accessorMap = new Set(accessorIds); diff --git a/src/plugins/chart_expressions/expression_xy/public/types.ts b/src/plugins/chart_expressions/expression_xy/public/types.ts index 6fb2371c2aaf5..36b8f4c13a776 100755 --- a/src/plugins/chart_expressions/expression_xy/public/types.ts +++ b/src/plugins/chart_expressions/expression_xy/public/types.ts @@ -7,13 +7,12 @@ */ import { IconType } from '@elastic/eui'; -import { Query } from '../../../data/common'; import { DataPublicPluginSetup } from '../../../data/public'; import { FieldFormatsSetup } from '../../../field_formats/public'; import { ChartsPluginSetup } from '../../../charts/public'; import { IFieldFormat, SerializedFieldFormat } from '../../../../plugins/field_formats/common'; import type { RangeSelectContext, ValueClickContext } from '../../../../plugins/embeddable/public'; -import { Datatable, ExpressionsServiceStart, ExpressionsSetup } from '../../../expressions/public'; +import { ExpressionsServiceStart, ExpressionsSetup } from '../../../expressions/public'; export interface SetupDeps { expressions: ExpressionsSetup; @@ -74,41 +73,6 @@ export interface Operation extends OperationMetadata { sortingHint?: SortingHint; } -export interface FramePublicAPI { - datasourceLayers: Record; - appliedDatasourceLayers?: Record; // this is only set when auto-apply is turned off - /** - * Data of the chart currently rendered in the preview. - * This data might be not available (e.g. if the chart can't be rendered) or outdated and belonging to another chart. - * If accessing, make sure to check whether expected columns actually exist. - */ - activeData?: Record; -} - -/** - * This is an API provided to visualizations by the frame, which calls the publicAPI on the datasource - */ -export interface DatasourcePublicAPI { - datasourceId: string; - getTableSpec: () => Array<{ columnId: string; fields: string[] }>; - getOperationForColumnId: (columnId: string) => OperationDescriptor | null; - /** - * Collect all default visual values given the current state - */ - getVisualDefaults: () => Record>; - /** - * Retrieve the specific source id for the current state - */ - getSourceId: () => string | undefined; - /** - * Collect all defined filters from all the operations in the layer - */ - getFilters: (activeData?: FramePublicAPI['activeData']) => { - kuery: Query[][]; - lucene: Query[][]; - }; -} - /** * A visualization type advertised to the user in the chart switcher */ From 53a66598168cd1155e24b264c16c15d1e0676db9 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Mon, 28 Mar 2022 16:43:47 +0300 Subject: [PATCH 073/133] Removed not used utils and styles. --- .../annotations/expression.scss | 37 --- .../annotations/expression.tsx | 234 ---------------- .../xy_visualization/annotations_helpers.tsx | 257 ------------------ 3 files changed, 528 deletions(-) delete mode 100644 x-pack/plugins/lens/public/xy_visualization/annotations/expression.scss delete mode 100644 x-pack/plugins/lens/public/xy_visualization/annotations/expression.tsx delete mode 100644 x-pack/plugins/lens/public/xy_visualization/annotations_helpers.tsx diff --git a/x-pack/plugins/lens/public/xy_visualization/annotations/expression.scss b/x-pack/plugins/lens/public/xy_visualization/annotations/expression.scss deleted file mode 100644 index fc2b1204bb1d0..0000000000000 --- a/x-pack/plugins/lens/public/xy_visualization/annotations/expression.scss +++ /dev/null @@ -1,37 +0,0 @@ -.lnsXyDecorationRotatedWrapper { - display: inline-block; - overflow: hidden; - line-height: 1.5; - - .lnsXyDecorationRotatedWrapper__label { - display: inline-block; - white-space: nowrap; - transform: translate(0, 100%) rotate(-90deg); - transform-origin: 0 0; - - &::after { - content: ''; - float: left; - margin-top: 100%; - } - } -} - -.lnsXyAnnotationNumberIcon { - border-radius: $euiSize; - min-width: $euiSize; - height: $euiSize; - background-color: currentColor; -} - -.lnsXyAnnotationNumberIcon__text { - font-weight: 500; - font-size: 9px; - letter-spacing: -.5px; - line-height: 11px; -} - -.lnsXyAnnotationIcon_rotate90 { - transform: rotate(45deg); - transform-origin: center; -} diff --git a/x-pack/plugins/lens/public/xy_visualization/annotations/expression.tsx b/x-pack/plugins/lens/public/xy_visualization/annotations/expression.tsx deleted file mode 100644 index 2a103763b385c..0000000000000 --- a/x-pack/plugins/lens/public/xy_visualization/annotations/expression.tsx +++ /dev/null @@ -1,234 +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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import './expression.scss'; -import React from 'react'; -import { snakeCase } from 'lodash'; -import { - AnnotationDomainType, - AnnotationTooltipFormatter, - LineAnnotation, - Position, -} from '@elastic/charts'; -import type { FieldFormat } from 'src/plugins/field_formats/common'; -import type { EventAnnotationArgs } from 'src/plugins/event_annotation/common'; -import moment from 'moment'; -import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import { defaultAnnotationColor } from '../../../../../../src/plugins/event_annotation/public'; -import { hasIcon } from '../xy_config_panel/shared/icon_select'; -import { AnnotationLayerArgs } from '../../../../../../src/plugins/chart_expressions/expression_xy/common'; -import { - mapVerticalToHorizontalPlacement, - LINES_MARKER_SIZE, - MarkerBody, - Marker, - AnnotationIcon, -} from '../annotations_helpers'; - -const getRoundedTimestamp = (timestamp: number, firstTimestamp?: number, minInterval?: number) => { - if (!firstTimestamp || !minInterval) { - return timestamp; - } - return timestamp - ((timestamp - firstTimestamp) % minInterval); -}; - -export interface AnnotationsProps { - groupedAnnotations: CollectiveConfig[]; - formatter?: FieldFormat; - isHorizontal: boolean; - paddingMap: Partial>; - hide?: boolean; - minInterval?: number; - isBarChart?: boolean; -} - -interface CollectiveConfig extends EventAnnotationArgs { - roundedTimestamp: number; - axisMode: 'bottom'; - customTooltipDetails?: AnnotationTooltipFormatter | undefined; -} - -const groupVisibleConfigsByInterval = ( - layers: AnnotationLayerArgs[], - minInterval?: number, - firstTimestamp?: number -) => { - return layers - .flatMap(({ annotations }) => annotations.filter((a) => !a.isHidden)) - .sort((a, b) => moment(a.time).valueOf() - moment(b.time).valueOf()) - .reduce>((acc, current) => { - const roundedTimestamp = getRoundedTimestamp( - moment(current.time).valueOf(), - firstTimestamp, - minInterval - ); - return { - ...acc, - [roundedTimestamp]: acc[roundedTimestamp] ? [...acc[roundedTimestamp], current] : [current], - }; - }, {}); -}; - -const createCustomTooltipDetails = - ( - config: EventAnnotationArgs[], - formatter?: FieldFormat - ): AnnotationTooltipFormatter | undefined => - () => { - return ( -
- {config.map(({ icon, label, time, color }) => ( -
- - {hasIcon(icon) && ( - - - - )} - {label} - - {formatter?.convert(time) || String(time)} -
- ))} -
- ); - }; - -function getCommonProperty( - configArr: EventAnnotationArgs[], - propertyName: K, - fallbackValue: T -) { - const firstStyle = configArr[0][propertyName]; - if (configArr.every((config) => firstStyle === config[propertyName])) { - return firstStyle; - } - return fallbackValue; -} - -const getCommonStyles = (configArr: EventAnnotationArgs[]) => { - return { - color: getCommonProperty( - configArr, - 'color', - defaultAnnotationColor - ), - lineWidth: getCommonProperty(configArr, 'lineWidth', 1), - lineStyle: getCommonProperty(configArr, 'lineStyle', 'solid'), - textVisibility: getCommonProperty(configArr, 'textVisibility', false), - }; -}; - -export const getAnnotationsGroupedByInterval = ( - layers: AnnotationLayerArgs[], - minInterval?: number, - firstTimestamp?: number, - formatter?: FieldFormat -) => { - const visibleGroupedConfigs = groupVisibleConfigsByInterval(layers, minInterval, firstTimestamp); - let collectiveConfig: CollectiveConfig; - return Object.entries(visibleGroupedConfigs).map(([roundedTimestamp, configArr]) => { - collectiveConfig = { - ...configArr[0], - roundedTimestamp: Number(roundedTimestamp), - axisMode: 'bottom', - }; - if (configArr.length > 1) { - const commonStyles = getCommonStyles(configArr); - collectiveConfig = { - ...collectiveConfig, - ...commonStyles, - icon: String(configArr.length), - customTooltipDetails: createCustomTooltipDetails(configArr, formatter), - }; - } - return collectiveConfig; - }); -}; - -export const Annotations = ({ - groupedAnnotations, - formatter, - isHorizontal, - paddingMap, - hide, - minInterval, - isBarChart, -}: AnnotationsProps) => { - return ( - <> - {groupedAnnotations.map((annotation) => { - const markerPositionVertical = Position.Top; - const markerPosition = isHorizontal - ? mapVerticalToHorizontalPlacement(markerPositionVertical) - : markerPositionVertical; - const hasReducedPadding = paddingMap[markerPositionVertical] === LINES_MARKER_SIZE; - const id = snakeCase(annotation.label); - const { roundedTimestamp, time: exactTimestamp } = annotation; - const isGrouped = Boolean(annotation.customTooltipDetails); - const header = - formatter?.convert(isGrouped ? roundedTimestamp : exactTimestamp) || - moment(isGrouped ? roundedTimestamp : exactTimestamp).toISOString(); - const strokeWidth = annotation.lineWidth || 1; - return ( - - ) : undefined - } - markerBody={ - !hide ? ( - - ) : undefined - } - markerPosition={markerPosition} - dataValues={[ - { - dataValue: moment( - isBarChart && minInterval ? roundedTimestamp + minInterval / 2 : roundedTimestamp - ).valueOf(), - header, - details: annotation.label, - }, - ]} - customTooltipDetails={annotation.customTooltipDetails} - style={{ - line: { - strokeWidth, - stroke: annotation.color || defaultAnnotationColor, - dash: - annotation.lineStyle === 'dashed' - ? [strokeWidth * 3, strokeWidth] - : annotation.lineStyle === 'dotted' - ? [strokeWidth, strokeWidth] - : undefined, - opacity: 1, - }, - }} - /> - ); - })} - - ); -}; diff --git a/x-pack/plugins/lens/public/xy_visualization/annotations_helpers.tsx b/x-pack/plugins/lens/public/xy_visualization/annotations_helpers.tsx deleted file mode 100644 index b00a4e9a654f2..0000000000000 --- a/x-pack/plugins/lens/public/xy_visualization/annotations_helpers.tsx +++ /dev/null @@ -1,257 +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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import './expression_reference_lines.scss'; -import React from 'react'; -import { EuiFlexGroup, EuiIcon, EuiIconProps, EuiText } from '@elastic/eui'; -import { Position } from '@elastic/charts'; -import classnames from 'classnames'; -import { - IconPosition, - YAxisMode, - YConfig, -} from '../../../../../src/plugins/chart_expressions/expression_xy/common'; -import { hasIcon } from './xy_config_panel/shared/icon_select'; -import { annotationsIconSet } from './annotations/config_panel/icon_set'; - -export const LINES_MARKER_SIZE = 20; - -export const computeChartMargins = ( - referenceLinePaddings: Partial>, - labelVisibility: Partial>, - titleVisibility: Partial>, - axesMap: Record<'left' | 'right', unknown>, - isHorizontal: boolean -) => { - const result: Partial> = {}; - if (!labelVisibility?.x && !titleVisibility?.x && referenceLinePaddings.bottom) { - const placement = isHorizontal ? mapVerticalToHorizontalPlacement('bottom') : 'bottom'; - result[placement] = referenceLinePaddings.bottom; - } - if ( - referenceLinePaddings.left && - (isHorizontal || (!labelVisibility?.yLeft && !titleVisibility?.yLeft)) - ) { - const placement = isHorizontal ? mapVerticalToHorizontalPlacement('left') : 'left'; - result[placement] = referenceLinePaddings.left; - } - if ( - referenceLinePaddings.right && - (isHorizontal || !axesMap.right || (!labelVisibility?.yRight && !titleVisibility?.yRight)) - ) { - const placement = isHorizontal ? mapVerticalToHorizontalPlacement('right') : 'right'; - result[placement] = referenceLinePaddings.right; - } - // there's no top axis, so just check if a margin has been computed - if (referenceLinePaddings.top) { - const placement = isHorizontal ? mapVerticalToHorizontalPlacement('top') : 'top'; - result[placement] = referenceLinePaddings.top; - } - return result; -}; - -// Note: it does not take into consideration whether the reference line is in view or not - -export const getLinesCausedPaddings = ( - visualConfigs: Array< - Pick | undefined - >, - axesMap: Record<'left' | 'right', unknown> -) => { - // collect all paddings for the 4 axis: if any text is detected double it. - const paddings: Partial> = {}; - const icons: Partial> = {}; - visualConfigs?.forEach((config) => { - if (!config) { - return; - } - const { axisMode, icon, iconPosition, textVisibility } = config; - if (axisMode && (hasIcon(icon) || textVisibility)) { - const placement = getBaseIconPlacement(iconPosition, axesMap, axisMode); - paddings[placement] = Math.max( - paddings[placement] || 0, - LINES_MARKER_SIZE * (textVisibility ? 2 : 1) // double the padding size if there's text - ); - icons[placement] = (icons[placement] || 0) + (hasIcon(icon) ? 1 : 0); - } - }); - // post-process the padding based on the icon presence: - // if no icon is present for the placement, just reduce the padding - (Object.keys(paddings) as Position[]).forEach((placement) => { - if (!icons[placement]) { - paddings[placement] = LINES_MARKER_SIZE; - } - }); - return paddings; -}; - -export function mapVerticalToHorizontalPlacement(placement: Position) { - switch (placement) { - case Position.Top: - return Position.Right; - case Position.Bottom: - return Position.Left; - case Position.Left: - return Position.Bottom; - case Position.Right: - return Position.Top; - } -} - -// if there's just one axis, put it on the other one -// otherwise use the same axis -// this function assume the chart is vertical -export function getBaseIconPlacement( - iconPosition: IconPosition | undefined, - axesMap?: Record, - axisMode?: YAxisMode -) { - if (iconPosition === 'auto') { - if (axisMode === 'bottom') { - return Position.Top; - } - if (axesMap) { - if (axisMode === 'left') { - return axesMap.right ? Position.Left : Position.Right; - } - return axesMap.left ? Position.Right : Position.Left; - } - } - - if (iconPosition === 'left') { - return Position.Left; - } - if (iconPosition === 'right') { - return Position.Right; - } - if (iconPosition === 'below') { - return Position.Bottom; - } - return Position.Top; -} - -export function MarkerBody({ - label, - isHorizontal, -}: { - label: string | undefined; - isHorizontal: boolean; -}) { - if (!label) { - return null; - } - if (isHorizontal) { - return ( -
- {label} -
- ); - } - return ( -
-
- {label} -
-
- ); -} - -const isNumericalString = (value: string) => !isNaN(Number(value)); - -function NumberIcon({ number }: { number: number }) { - return ( - - - {number < 10 ? number : `9+`} - - - ); -} - -interface MarkerConfig { - axisMode?: YAxisMode; - icon?: string; - textVisibility?: boolean; - iconPosition?: IconPosition; -} - -export const AnnotationIcon = ({ - type, - rotateClassName = '', - isHorizontal, - renderedInChart, - ...rest -}: { - type: string; - rotateClassName?: string; - isHorizontal?: boolean; - renderedInChart?: boolean; -} & EuiIconProps) => { - if (isNumericalString(type)) { - return ; - } - const iconConfig = annotationsIconSet.find((i) => i.value === type); - if (!iconConfig) { - return null; - } - return ( - - ); -}; - -export function Marker({ - config, - isHorizontal, - hasReducedPadding, - label, - rotateClassName, -}: { - config: MarkerConfig; - isHorizontal: boolean; - hasReducedPadding: boolean; - label?: string; - rotateClassName?: string; -}) { - if (hasIcon(config.icon)) { - return ( - - ); - } - - // if there's some text, check whether to show it as marker, or just show some padding for the icon - if (config.textVisibility) { - if (hasReducedPadding) { - return ; - } - return ; - } - return null; -} From 65f95204aadce6b9a49d9610077afeb81fbb994c Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Tue, 29 Mar 2022 12:20:51 +0300 Subject: [PATCH 074/133] Fixed types and tests. --- .../annotation_layer_config.ts | 49 ------- .../data_layer_config.test.ts | 33 ----- .../expression_functions/data_layer_config.ts | 123 ------------------ .../reference_line_layer_config.ts | 62 --------- .../common/expression_functions/xy_vis.ts | 43 +++--- .../public/components/xy_chart.test.tsx | 36 ++++- 6 files changed, 60 insertions(+), 286 deletions(-) delete mode 100644 src/plugins/chart_expressions/expression_xy/common/expression_functions/annotation_layer_config.ts delete mode 100644 src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer_config.test.ts delete mode 100644 src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer_config.ts delete mode 100644 src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line_layer_config.ts diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/annotation_layer_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/annotation_layer_config.ts deleted file mode 100644 index 0862b69ca44f2..0000000000000 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/annotation_layer_config.ts +++ /dev/null @@ -1,49 +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 type { ExpressionFunctionDefinition } from '../../../../expressions/common'; -import { LayerTypes, ANNOTATION_LAYER } from '../constants'; -import { AnnotationLayerArgs, AnnotationLayerConfigResult } from '../types'; - -export function annotationLayerConfigFunction(): ExpressionFunctionDefinition< - typeof ANNOTATION_LAYER, - null, - AnnotationLayerArgs, - AnnotationLayerConfigResult -> { - return { - name: ANNOTATION_LAYER, - aliases: [], - type: ANNOTATION_LAYER, - inputTypes: ['null'], - help: 'Annotation layer in lens', - args: { - layerId: { - types: ['string'], - help: '', - }, - hide: { - types: ['boolean'], - default: false, - help: 'Show details', - }, - annotations: { - types: ['manual_event_annotation'], - help: '', - multi: true, - }, - }, - fn: (input, args) => { - return { - type: ANNOTATION_LAYER, - ...args, - layerType: LayerTypes.ANNOTATIONS, - }; - }, - }; -} diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer_config.test.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer_config.test.ts deleted file mode 100644 index ba7fafd3b3685..0000000000000 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer_config.test.ts +++ /dev/null @@ -1,33 +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 { DataLayerArgs } from '../types'; -import { dataLayerConfigFunction } from '../expression_functions'; -import { createMockExecutionContext } from '../../../../expressions/common/mocks'; -import { mockPaletteOutput } from '../__mocks__'; -import { LayerTypes } from '../constants'; - -describe('dataLayerConfig', () => { - test('produces the correct arguments', () => { - const args: DataLayerArgs = { - layerId: 'first', - seriesType: 'line', - xAccessor: 'c', - accessors: ['a', 'b'], - splitAccessor: 'd', - xScaleType: 'linear', - yScaleType: 'linear', - isHistogram: false, - palette: mockPaletteOutput, - }; - - const result = dataLayerConfigFunction.fn(null, args, createMockExecutionContext()); - - expect(result).toEqual({ type: 'dataLayer', layerType: LayerTypes.DATA, ...args }); - }); -}); diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer_config.ts deleted file mode 100644 index 3aac992d674d9..0000000000000 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer_config.ts +++ /dev/null @@ -1,123 +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 { i18n } from '@kbn/i18n'; -import type { ExpressionFunctionDefinition } from '../../../../expressions/common'; -import { DataLayerArgs, DataLayerConfigResult } from '../types'; -import { - DATA_LAYER, - LayerTypes, - SeriesTypes, - XScaleTypes, - YScaleTypes, - Y_CONFIG, -} from '../constants'; - -export const dataLayerConfigFunction: ExpressionFunctionDefinition< - typeof DATA_LAYER, - null, - DataLayerArgs, - DataLayerConfigResult -> = { - name: DATA_LAYER, - aliases: [], - type: DATA_LAYER, - help: i18n.translate('expressionXY.dataLayer.help', { - defaultMessage: `Configure a layer in the xy chart`, - }), - inputTypes: ['null'], - args: { - hide: { - types: ['boolean'], - default: false, - help: i18n.translate('expressionXY.dataLayer.hide.help', { - defaultMessage: 'Show / hide axis', - }), - }, - layerId: { - types: ['string'], - help: i18n.translate('expressionXY.dataLayer.layerId.help', { - defaultMessage: 'Layer ID', - }), - }, - xAccessor: { - types: ['string'], - help: i18n.translate('expressionXY.dataLayer.xAccessor.help', { - defaultMessage: 'X-axis', - }), - }, - seriesType: { - types: ['string'], - options: [...Object.values(SeriesTypes)], - help: i18n.translate('expressionXY.dataLayer.seriesType.help', { - defaultMessage: 'The type of chart to display.', - }), - }, - xScaleType: { - options: [...Object.values(XScaleTypes)], - help: i18n.translate('expressionXY.dataLayer.xScaleType.help', { - defaultMessage: 'The scale type of the x axis', - }), - default: XScaleTypes.ORDINAL, - }, - isHistogram: { - types: ['boolean'], - default: false, - help: i18n.translate('expressionXY.dataLayer.isHistogram.help', { - defaultMessage: 'Whether to layout the chart as a histogram', - }), - }, - yScaleType: { - options: [...Object.values(YScaleTypes)], - help: i18n.translate('expressionXY.dataLayer.yScaleType.help', { - defaultMessage: 'The scale type of the y axes', - }), - default: YScaleTypes.LINEAR, - }, - splitAccessor: { - types: ['string'], - help: i18n.translate('expressionXY.dataLayer.splitAccessor.help', { - defaultMessage: 'The column to split by', - }), - }, - accessors: { - types: ['string'], - help: i18n.translate('expressionXY.dataLayer.accessors.help', { - defaultMessage: 'The columns to display on the y axis.', - }), - multi: true, - }, - yConfig: { - types: [Y_CONFIG], - help: i18n.translate('expressionXY.dataLayer.yConfig.help', { - defaultMessage: 'Additional configuration for y axes', - }), - multi: true, - }, - columnToLabel: { - types: ['string'], - help: i18n.translate('expressionXY.dataLayer.columnToLabel.help', { - defaultMessage: 'JSON key-value pairs of column ID to label', - }), - }, - palette: { - default: `{theme "palette" default={system_palette name="default"} }`, - help: i18n.translate('expressionXY.dataLayer.palette.help', { - defaultMessage: 'Palette', - }), - types: ['palette'], - }, - }, - fn(input, args) { - return { - type: DATA_LAYER, - ...args, - layerType: LayerTypes.DATA, - }; - }, -}; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line_layer_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line_layer_config.ts deleted file mode 100644 index c5d0f17ff138d..0000000000000 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line_layer_config.ts +++ /dev/null @@ -1,62 +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 { i18n } from '@kbn/i18n'; -import type { ExpressionFunctionDefinition } from '../../../../expressions/common'; -import { LayerTypes, REFERENCE_LINE_LAYER, Y_CONFIG } from '../constants'; -import { ReferenceLineLayerArgs, ReferenceLineLayerConfigResult } from '../types'; - -export const referenceLineLayerConfigFunction: ExpressionFunctionDefinition< - typeof REFERENCE_LINE_LAYER, - null, - ReferenceLineLayerArgs, - ReferenceLineLayerConfigResult -> = { - name: REFERENCE_LINE_LAYER, - aliases: [], - type: REFERENCE_LINE_LAYER, - help: i18n.translate('expressionXY.referenceLineLayer.help', { - defaultMessage: `Configure a reference line in the xy chart`, - }), - inputTypes: ['null'], - args: { - layerId: { - types: ['string'], - help: i18n.translate('expressionXY.referenceLineLayer.layerId.help', { - defaultMessage: `Layer ID`, - }), - }, - accessors: { - types: ['string'], - help: i18n.translate('expressionXY.referenceLineLayer.accessors.help', { - defaultMessage: 'The columns to display on the y axis.', - }), - multi: true, - }, - yConfig: { - types: [Y_CONFIG], - help: i18n.translate('expressionXY.referenceLineLayer.yConfig.help', { - defaultMessage: 'Additional configuration for y axes', - }), - multi: true, - }, - columnToLabel: { - types: ['string'], - help: i18n.translate('expressionXY.referenceLineLayer.columnToLabel.help', { - defaultMessage: 'JSON key-value pairs of column ID to label', - }), - }, - }, - fn(input, args) { - return { - type: REFERENCE_LINE_LAYER, - ...args, - layerType: LayerTypes.REFERENCELINE, - }; - }, -}; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts index 0f83aeecc7a20..f565067b51c8c 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts @@ -7,13 +7,12 @@ */ import { i18n } from '@kbn/i18n'; -import type { ExpressionFunctionDefinition } from '../../../../expressions'; -import { LensMultiTable, XYArgs, XYRender } from '../types'; +import type { ExpressionFunctionDefinition, Datatable } from '../../../../expressions'; import { prepareLogTable } from '../../../../../../src/plugins/visualizations/common/utils'; +import { XYArgs, XYLayerConfigResult, XYRender } from '../types'; import { XY_VIS, DATA_LAYER, - MULTITABLE, XYCurveTypes, LEGEND_CONFIG, ValueLabelModes, @@ -26,7 +25,6 @@ import { LABELS_ORIENTATION_CONFIG, AXIS_TITLES_VISIBILITY_CONFIG, EndValues, - ANNOTATION_LAYER, LayerTypes, } from '../constants'; @@ -51,13 +49,13 @@ const strings = { export const xyVisFunction: ExpressionFunctionDefinition< typeof XY_VIS, - LensMultiTable, + Datatable, XYArgs, XYRender > = { name: XY_VIS, type: 'render', - inputTypes: [MULTITABLE], + inputTypes: ['datatable'], help: i18n.translate('expressionXY.xyVis.help', { defaultMessage: 'An X/Y chart', }), @@ -156,12 +154,17 @@ export const xyVisFunction: ExpressionFunctionDefinition< defaultMessage: 'Show x and y axes titles', }), }, - layers: { - types: [DATA_LAYER, REFERENCE_LINE_LAYER, ANNOTATION_LAYER], - help: i18n.translate('expressionXY.xyVis.layers.help', { - defaultMessage: 'Layers of visual series', + dataLayer: { + types: [DATA_LAYER], + help: i18n.translate('expressionXY.xyVis.dataLayer.help', { + defaultMessage: 'Data layer of visual series', + }), + }, + referenceLineLayer: { + types: [REFERENCE_LINE_LAYER], + help: i18n.translate('expressionXY.xyVis.referenceLineLayer.help', { + defaultMessage: 'Reference line layer', }), - multi: true, }, curveType: { types: ['string'], @@ -199,8 +202,14 @@ export const xyVisFunction: ExpressionFunctionDefinition< }, }, fn(data, args, handlers) { + const { dataLayer, referenceLineLayer, ...restArgs } = args; + const inputLayers: Array = [dataLayer, referenceLineLayer]; + const layers: XYLayerConfigResult[] = inputLayers.filter( + (layer): layer is XYLayerConfigResult => layer !== undefined + ); + if (handlers?.inspectorAdapters?.tables) { - args.layers.forEach((layer) => { + layers.forEach((layer, index) => { if (layer.layerType === LayerTypes.ANNOTATIONS) { return; } @@ -212,9 +221,9 @@ export const xyVisFunction: ExpressionFunctionDefinition< splitAccessor = layer.splitAccessor; } - const { layerId, accessors, layerType } = layer; + const { accessors, layerType } = layer; const logTable = prepareLogTable( - data.tables[layerId], + layer.table, [ [ accessors ? accessors : undefined, @@ -226,7 +235,7 @@ export const xyVisFunction: ExpressionFunctionDefinition< true ); - handlers.inspectorAdapters.tables.logDatatable(layerId, logTable); + handlers.inspectorAdapters.tables.logDatatable(index, logTable); // ? what to do with layer id while adding table to inspector. }); } @@ -234,9 +243,9 @@ export const xyVisFunction: ExpressionFunctionDefinition< type: 'render', as: XY_VIS_RENDERER, value: { - data, args: { - ...args, + ...restArgs, + layers, ariaLabel: args.ariaLabel ?? (handlers.variables?.embeddableTitle as string) ?? diff --git a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx index 9e7038b984087..7a99559160f06 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx @@ -185,6 +185,7 @@ describe('XYChart component', () => { ...(args.layers[0] as DataLayerConfigResult), seriesType: 'line', xScaleType: 'time', + table: timeSampleLayer.table, }, ], }} @@ -340,16 +341,47 @@ describe('XYChart component', () => { }); }); describe('endzones', () => { + const table = createSampleDatatableWithRows([ + { a: 1, b: 2, c: new Date('2021-04-22').valueOf(), d: 'Foo' }, + { a: 1, b: 2, c: new Date('2021-04-23').valueOf(), d: 'Foo' }, + { a: 1, b: 2, c: new Date('2021-04-24').valueOf(), d: 'Foo' }, + ]); + const newData = { + ...table, + type: 'datatable', + + columns: table.columns.map((c) => + c.id !== 'c' + ? c + : { + ...c, + meta: { + type: 'date', + source: 'esaggs', + sourceParams: { + type: 'date_histogram', + params: {}, + appliedTimeRange: { + from: '2021-04-22T12:00:00.000Z', + to: '2021-04-24T12:00:00.000Z', + }, + }, + }, + } + ), + }; const timeArgs: XYProps = { ...args, layers: [ { - ...(args.layers[0] as DataLayerConfigResult), + ...args.layers[0], + type: 'dataLayer', seriesType: 'line', xScaleType: 'time', isHistogram: true, splitAccessor: undefined, - }, + table: newData, + } as DataLayerConfigResult, ], }; From d7a014a42c73356bb798d6c605f8ac73f6d4b9af Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Tue, 29 Mar 2022 13:17:45 +0300 Subject: [PATCH 075/133] updated size. --- packages/kbn-optimizer/limits.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index 25656a3977fea..d80daf81ffd77 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -124,5 +124,5 @@ pageLoadAssetSize: sessionView: 77750 cloudSecurityPosture: 19109 visTypeGauge: 24113 - expressionXY: 41392 + expressionXY: 62885 eventAnnotation: 19334 From 1563ea0b2667adcb04f1ef7f9b3a5916d2ec0eaf Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Wed, 30 Mar 2022 12:40:11 +0300 Subject: [PATCH 076/133] Added right behavior, related to the tables, comming from the expression. --- .../expression_functions/layered_xy_vis.ts | 20 +---- .../expression_functions/xy_vis.test.ts | 2 +- .../common/expression_functions/xy_vis.ts | 76 +++++-------------- .../expression_xy/common/i18n/index.tsx | 28 +++++++ .../common/types/expression_functions.ts | 5 +- .../expression_xy/common/utils/index.tsx | 9 +++ .../common/utils/log_datatables.ts | 48 ++++++++++++ .../xy_visualization/annotations/helpers.tsx | 5 +- .../reference_line_helpers.tsx | 44 ++++++++--- .../xy_visualization/visualization.test.ts | 4 +- .../public/xy_visualization/visualization.tsx | 17 +++-- .../xy_config_panel/color_picker.tsx | 15 +++- .../xy_config_panel/index.tsx | 6 +- .../xy_config_panel/xy_config_panel.test.tsx | 2 +- 14 files changed, 184 insertions(+), 97 deletions(-) create mode 100644 src/plugins/chart_expressions/expression_xy/common/i18n/index.tsx create mode 100644 src/plugins/chart_expressions/expression_xy/common/utils/index.tsx create mode 100644 src/plugins/chart_expressions/expression_xy/common/utils/log_datatables.ts diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts index 7b5373b368dad..c33322e4ddd65 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts @@ -7,11 +7,7 @@ */ import { i18n } from '@kbn/i18n'; -import type { - ExpressionFunctionDefinition, - TablesAdapter, - Datatable, -} from '../../../../expressions'; +import type { ExpressionFunctionDefinition, Datatable } from '../../../../expressions'; import { LayeredXYArgs, XYExtendedLayerConfigResult, XYRender } from '../types'; import { XYCurveTypes, @@ -29,10 +25,7 @@ import { LAYERED_XY_VIS, EndValues, } from '../constants'; - -const logDataTable = (tableAdapter: TablesAdapter, datatables: Record = {}) => { - Object.entries(datatables).forEach(([key, table]) => tableAdapter.logDatatable(key, table)); -}; +import { logDatatables } from '../utils'; export const layeredXyVisFunction: ExpressionFunctionDefinition< typeof LAYERED_XY_VIS, @@ -188,14 +181,7 @@ export const layeredXyVisFunction: ExpressionFunctionDefinition< (layer): layer is XYExtendedLayerConfigResult => layer !== undefined ); - const tables = layers.reduce>((t, layer, index) => { - t[index] = data; - return t; - }, {}); - - if (handlers?.inspectorAdapters?.tables) { - logDataTable(handlers.inspectorAdapters.tables, tables); - } + logDatatables(layers, handlers); return { type: 'render', diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.test.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.test.ts index c70c9388fb943..691c2929d6624 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.test.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.test.ts @@ -17,7 +17,7 @@ describe('xyVis', () => { const { layers, ...rest } = args; const result = xyVisFunction.fn( data, - { ...rest, dataLayer: sampleLayer }, + { ...rest, dataLayers: [sampleLayer], referenceLineLayers: [], annotationLayers: [] }, createMockExecutionContext() ); diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts index f565067b51c8c..7608b0e73d481 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts @@ -8,7 +8,6 @@ import { i18n } from '@kbn/i18n'; import type { ExpressionFunctionDefinition, Datatable } from '../../../../expressions'; -import { prepareLogTable } from '../../../../../../src/plugins/visualizations/common/utils'; import { XYArgs, XYLayerConfigResult, XYRender } from '../types'; import { XY_VIS, @@ -25,27 +24,9 @@ import { LABELS_ORIENTATION_CONFIG, AXIS_TITLES_VISIBILITY_CONFIG, EndValues, - LayerTypes, + ANNOTATION_LAYER, } from '../constants'; - -const strings = { - getMetricHelp: () => - i18n.translate('expressionXY.xyVis.logDatatable.metric', { - defaultMessage: 'Vertical axis', - }), - getXAxisHelp: () => - i18n.translate('expressionXY.xyVis.logDatatable.x', { - defaultMessage: 'Horizontal axis', - }), - getBreakdownHelp: () => - i18n.translate('expressionXY.xyVis.logDatatable.breakDown', { - defaultMessage: 'Break down by', - }), - getReferenceLineHelp: () => - i18n.translate('expressionXY.xyVis.logDatatable.breakDown', { - defaultMessage: 'Break down by', - }), -}; +import { logDatatables } from '../utils'; export const xyVisFunction: ExpressionFunctionDefinition< typeof XY_VIS, @@ -154,17 +135,26 @@ export const xyVisFunction: ExpressionFunctionDefinition< defaultMessage: 'Show x and y axes titles', }), }, - dataLayer: { + dataLayers: { types: [DATA_LAYER], help: i18n.translate('expressionXY.xyVis.dataLayer.help', { defaultMessage: 'Data layer of visual series', }), + multi: true, }, - referenceLineLayer: { + referenceLineLayers: { types: [REFERENCE_LINE_LAYER], help: i18n.translate('expressionXY.xyVis.referenceLineLayer.help', { defaultMessage: 'Reference line layer', }), + multi: true, + }, + annotationLayers: { + types: [ANNOTATION_LAYER], + help: i18n.translate('expressionXY.xyVis.annotationLayer.help', { + defaultMessage: 'Annotation layer', + }), + multi: true, }, curveType: { types: ['string'], @@ -202,42 +192,18 @@ export const xyVisFunction: ExpressionFunctionDefinition< }, }, fn(data, args, handlers) { - const { dataLayer, referenceLineLayer, ...restArgs } = args; - const inputLayers: Array = [dataLayer, referenceLineLayer]; + const { dataLayers, referenceLineLayers, annotationLayers, ...restArgs } = args; + const inputLayers: Array = [ + ...dataLayers, + ...referenceLineLayers, + ...annotationLayers, + ]; + const layers: XYLayerConfigResult[] = inputLayers.filter( (layer): layer is XYLayerConfigResult => layer !== undefined ); - if (handlers?.inspectorAdapters?.tables) { - layers.forEach((layer, index) => { - if (layer.layerType === LayerTypes.ANNOTATIONS) { - return; - } - - let xAccessor; - let splitAccessor; - if (layer.layerType === LayerTypes.DATA) { - xAccessor = layer.xAccessor; - splitAccessor = layer.splitAccessor; - } - - const { accessors, layerType } = layer; - const logTable = prepareLogTable( - layer.table, - [ - [ - accessors ? accessors : undefined, - layerType === 'data' ? strings.getMetricHelp() : strings.getReferenceLineHelp(), - ], - [xAccessor ? [xAccessor] : undefined, strings.getXAxisHelp()], - [splitAccessor ? [splitAccessor] : undefined, strings.getBreakdownHelp()], - ], - true - ); - - handlers.inspectorAdapters.tables.logDatatable(index, logTable); // ? what to do with layer id while adding table to inspector. - }); - } + logDatatables(layers, handlers); return { type: 'render', diff --git a/src/plugins/chart_expressions/expression_xy/common/i18n/index.tsx b/src/plugins/chart_expressions/expression_xy/common/i18n/index.tsx new file mode 100644 index 0000000000000..229b83551e0f0 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/i18n/index.tsx @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 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 { i18n } from '@kbn/i18n'; + +export const strings = { + getMetricHelp: () => + i18n.translate('expressionXY.xyVis.logDatatable.metric', { + defaultMessage: 'Vertical axis', + }), + getXAxisHelp: () => + i18n.translate('expressionXY.xyVis.logDatatable.x', { + defaultMessage: 'Horizontal axis', + }), + getBreakdownHelp: () => + i18n.translate('expressionXY.xyVis.logDatatable.breakDown', { + defaultMessage: 'Break down by', + }), + getReferenceLineHelp: () => + i18n.translate('expressionXY.xyVis.logDatatable.breakDown', { + defaultMessage: 'Break down by', + }), +}; diff --git a/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts b/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts index fc13a048a2c53..433049f6f0073 100644 --- a/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts +++ b/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts @@ -185,8 +185,9 @@ export interface XYArgs { endValue?: EndValue; emphasizeFitting?: boolean; valueLabels: ValueLabelMode; - dataLayer?: DataLayerConfigResult; - referenceLineLayer?: ReferenceLineLayerConfigResult; + dataLayers: DataLayerConfigResult[]; + referenceLineLayers: ReferenceLineLayerConfigResult[]; + annotationLayers: AnnotationLayerConfigResult[]; fittingFunction?: FittingFunction; axisTitlesVisibilitySettings?: AxisTitlesVisibilityConfigResult; tickLabelsVisibilitySettings?: TickLabelsConfigResult; diff --git a/src/plugins/chart_expressions/expression_xy/common/utils/index.tsx b/src/plugins/chart_expressions/expression_xy/common/utils/index.tsx new file mode 100644 index 0000000000000..c8570ddd9a6a5 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/utils/index.tsx @@ -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 { logDatatables } from './log_datatables'; diff --git a/src/plugins/chart_expressions/expression_xy/common/utils/log_datatables.ts b/src/plugins/chart_expressions/expression_xy/common/utils/log_datatables.ts new file mode 100644 index 0000000000000..2e2304daadb64 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/utils/log_datatables.ts @@ -0,0 +1,48 @@ +/* + * 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 { ExecutionContext } from '../../../../expressions'; +import { prepareLogTable } from '../../../../visualizations/common/utils'; +import { LayerTypes } from '../constants'; +import { strings } from '../i18n'; +import { CommonXYLayerConfigResult } from '../types'; + +export const logDatatables = (layers: CommonXYLayerConfigResult[], handlers: ExecutionContext) => { + if (!handlers?.inspectorAdapters?.tables) { + return; + } + + layers.forEach((layer, index) => { + if (layer.layerType === LayerTypes.ANNOTATIONS) { + return; + } + + let xAccessor; + let splitAccessor; + if (layer.layerType === LayerTypes.DATA) { + xAccessor = layer.xAccessor; + splitAccessor = layer.splitAccessor; + } + + const { accessors, layerType } = layer; + const logTable = prepareLogTable( + layer.table, + [ + [ + accessors ? accessors : undefined, + layerType === LayerTypes.DATA ? strings.getMetricHelp() : strings.getReferenceLineHelp(), + ], + [xAccessor ? [xAccessor] : undefined, strings.getXAxisHelp()], + [splitAccessor ? [splitAccessor] : undefined, strings.getBreakdownHelp()], + ], + true + ); + + handlers.inspectorAdapters.tables.logDatatable(index, logTable); + }); +}; diff --git a/x-pack/plugins/lens/public/xy_visualization/annotations/helpers.tsx b/x-pack/plugins/lens/public/xy_visualization/annotations/helpers.tsx index 8f18450ba5a21..92a811e2da006 100644 --- a/x-pack/plugins/lens/public/xy_visualization/annotations/helpers.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/annotations/helpers.tsx @@ -22,6 +22,7 @@ import { LensIconChartBarAnnotations } from '../../assets/chart_bar_annotations' import { generateId } from '../../id_generator'; import { defaultAnnotationColor } from '../../../../../../src/plugins/event_annotation/public'; import { defaultAnnotationLabel } from './config_panel'; +import { convertActiveDataFromIndexesToLayers } from '../reference_line_helpers'; const MAX_DATE = 8640000000000000; const MIN_DATE = -8640000000000000; @@ -116,12 +117,14 @@ export const setAnnotationsDimension: Visualization['setDimension'] = ( : undefined; let resultAnnotations = [...inputAnnotations] as XYAnnotationLayerConfig['annotations']; + const activeData = convertActiveDataFromIndexesToLayers(frame?.activeData, prevState.layers); + if (!currentConfig) { resultAnnotations.push({ label: defaultAnnotationLabel, key: { type: 'point_in_time', - timestamp: getStaticDate(getDataLayers(prevState.layers), frame?.activeData), + timestamp: getStaticDate(getDataLayers(prevState.layers), activeData), }, icon: 'triangle', ...previousConfig, diff --git a/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx b/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx index af679d1354792..4080bc7595f5b 100644 --- a/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx @@ -107,6 +107,7 @@ export function getStaticValue( untouchedDataLayers, accessors, } = getAccessorCriteriaForGroup(groupId, dataLayers, activeData); + if ( groupId === 'x' && filteredLayers.length && @@ -114,6 +115,7 @@ export function getStaticValue( ) { return fallbackValue; } + const computedValue = computeStaticValueForGroup( filteredLayers, accessors, @@ -121,6 +123,7 @@ export function getStaticValue( groupId !== 'x', // histogram axis should compute the min based on the current data groupId !== 'x' ); + return computedValue ?? fallbackValue; } @@ -168,6 +171,7 @@ export function computeOverallDataDomain( const accessorMap = new Set(accessorIds); let min: number | undefined; let max: number | undefined; + const [stacked, unstacked] = partition( dataLayers, ({ seriesType }) => isStackedChart(seriesType) && allowStacking @@ -253,6 +257,24 @@ function computeStaticValueForGroup( } } +export const convertActiveDataFromIndexesToLayers = ( + activeData: Record | undefined = {}, + layers: XYState['layers'] = [] +) => { + const layersIdsToIndexes = layers.reduce>( + (layersWithIndexes, { layerId }, index) => ({ ...layersWithIndexes, [index]: layerId }), + {} + ); + + return Object.entries(activeData ?? {}).reduce>( + (dataByLayerIds, [layerIndex, dataPerLayer]) => ({ + ...dataByLayerIds, + [layersIdsToIndexes[layerIndex]]: dataPerLayer, + }), + {} + ); +}; + export const getReferenceSupportedLayer = ( state?: XYState, frame?: Pick @@ -271,13 +293,19 @@ export const getReferenceSupportedLayer = ( label: 'x' as const, }, ]; + + const layers = state?.layers || []; + const activeData = convertActiveDataFromIndexesToLayers(frame?.activeData, layers); + const referenceLineGroups = getGroupsRelatedToData( referenceLineGroupIds, state, frame?.datasourceLayers || {}, - frame?.activeData + activeData ); - const dataLayers = getDataLayers(state?.layers || []); + + const dataLayers = getDataLayers(layers); + const filledDataLayers = dataLayers.filter( ({ accessors, xAccessor }) => accessors.length || xAccessor ); @@ -293,12 +321,7 @@ export const getReferenceSupportedLayer = ( columnId: generateId(), dataType: 'number', label: getAxisName(label, { isHorizontal: isHorizontalChart(state?.layers || []) }), - staticValue: getStaticValue( - dataLayers, - label, - { activeData: frame?.activeData }, - layerHasNumberHistogram - ), + staticValue: getStaticValue(dataLayers, label, { activeData }, layerHasNumberHistogram), })) : undefined; @@ -320,6 +343,7 @@ export const getReferenceSupportedLayer = ( initialDimensions, }; }; + export const setReferenceDimension: Visualization['setDimension'] = ({ prevState, layerId, @@ -400,6 +424,8 @@ export const getReferenceConfiguration = ({ return axisMode; } ); + + const activeData = convertActiveDataFromIndexesToLayers(frame?.activeData, state.layers); const groupsToShow = getGroupsToShow( [ // When a reference layer panel is added, a static reference line should automatically be included by default @@ -425,7 +451,7 @@ export const getReferenceConfiguration = ({ ], state, frame.datasourceLayers, - frame?.activeData + activeData ); const isHorizontal = isHorizontalChart(state.layers); return { diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts b/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts index 18cd16c17b365..2e1d91446721c 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts @@ -1492,7 +1492,7 @@ describe('xy_visualization', () => { it('differ vertical axis if the formatters are not compatibles between each other', () => { const tables: Record = { - first: { + 0: { type: 'datatable', rows: [], columns: [ @@ -2185,7 +2185,7 @@ describe('xy_visualization', () => { }; frame.activeData = { - first: { + 0: { type: 'datatable', columns: [ { id: 'a', name: 'A', meta: { type: 'number' } }, diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx index c3bde3805b09f..cdc77f8ded423 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx @@ -34,6 +34,7 @@ import { toExpression, toPreviewExpression, getSortedAccessors } from './to_expr import { getAccessorColorConfig, getColorAssignments } from './color_assignment'; import { getColumnToLabelMap } from './state_helpers'; import { + convertActiveDataFromIndexesToLayers, getGroupsAvailableInData, getReferenceConfiguration, getReferenceSupportedLayer, @@ -180,6 +181,7 @@ export const getXyVisualization = ({ }, getConfiguration({ state, frame, layerId }) { + const activeData = convertActiveDataFromIndexesToLayers(frame.activeData, state.layers); const layer = state.layers.find((l) => l.layerId === layerId); if (!layer) { return { groups: [] }; @@ -214,7 +216,7 @@ export const getXyVisualization = ({ const dataLayers = getDataLayers(state.layers); const isHorizontal = isHorizontalChart(state.layers); - const { left, right } = groupAxesByType([layer], frame.activeData); + const { left, right } = groupAxesByType([layer], activeData); // Check locally if it has one accessor OR one accessor per axis const layerHasOnlyOneAccessor = Boolean( dataLayer.accessors.length < 2 || @@ -234,7 +236,7 @@ export const getXyVisualization = ({ Boolean(l.xAccessor) === Boolean(dataLayer.xAccessor) && Boolean(l.splitAccessor) === Boolean(dataLayer.splitAccessor) ) { - const { left: localLeft, right: localRight } = groupAxesByType([l], frame.activeData); + const { left: localLeft, right: localRight } = groupAxesByType([l], activeData); // return true only if matching axis are found return ( l.accessors.length && @@ -410,6 +412,8 @@ export const getXyVisualization = ({ }, removeDimension({ prevState, layerId, columnId, frame }) { + const activeData = convertActiveDataFromIndexesToLayers(frame.activeData, prevState.layers); + const foundLayer = prevState.layers.find((l) => l.layerId === layerId); if (!foundLayer) { return prevState; @@ -448,7 +452,7 @@ export const getXyVisualization = ({ const groupsAvailable = getGroupsAvailableInData( getDataLayers(prevState.layers), frame.datasourceLayers, - frame?.activeData + activeData ); if ( @@ -607,6 +611,7 @@ export const getXyVisualization = ({ if (state?.layers.length === 0 || !frame.activeData) { return; } + const activeData = convertActiveDataFromIndexesToLayers(frame.activeData, state.layers); const filteredLayers = [ ...getDataLayers(state.layers), @@ -615,7 +620,7 @@ export const getXyVisualization = ({ const accessorsWithArrayValues = []; for (const layer of filteredLayers) { const { layerId, accessors } = layer; - const rows = frame.activeData[layerId] && frame.activeData[layerId].rows; + const rows = activeData[layerId] && activeData[layerId].rows; if (!rows) { break; } @@ -689,9 +694,11 @@ const getMappedAccessors = ({ })); if (frame.activeData) { + const activeData = convertActiveDataFromIndexesToLayers(frame.activeData, state.layers); + const colorAssignments = getColorAssignments( getDataLayers(state.layers), - { tables: frame.activeData }, + { tables: activeData }, fieldFormats.deserialize ); mappedAccessors = getAccessorColorConfig( diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/color_picker.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/color_picker.tsx index 3f801fb92876d..d5fd534b4f6ad 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/color_picker.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/color_picker.tsx @@ -22,6 +22,7 @@ import { import { getSortedAccessors } from '../to_expression'; import { TooltipWrapper } from '../../shared_components'; import { isReferenceLayer, isAnnotationsLayer, getDataLayers } from '../visualization_helpers'; +import { convertActiveDataFromIndexesToLayers } from '../reference_line_helpers'; const tooltipContent = { auto: i18n.translate('xpack.lens.configPanel.color.tooltip.auto', { @@ -59,6 +60,7 @@ export const ColorPicker = ({ const layer = state.layers[index]; const overwriteColor = getSeriesColor(layer, accessor); + const activeData = convertActiveDataFromIndexesToLayers(frame.activeData, state.layers); const currentColor = useMemo(() => { if (overwriteColor || !frame.activeData) return overwriteColor; if (isReferenceLayer(layer)) { @@ -75,7 +77,7 @@ export const ColorPicker = ({ const colorAssignments = getColorAssignments( getDataLayers(state.layers), - { tables: frame.activeData }, + { tables: activeData }, formatFactory ); const mappedAccessors = getAccessorColorConfig( @@ -89,7 +91,16 @@ export const ColorPicker = ({ ); return mappedAccessors.find((a) => a.columnId === accessor)?.color || null; - }, [overwriteColor, frame, paletteService, state.layers, accessor, formatFactory, layer]); + }, [ + overwriteColor, + frame, + layer, + state.layers, + activeData, + formatFactory, + paletteService, + accessor, + ]); const [color, setColor] = useState(currentColor); diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/index.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/index.tsx index 42cf49c04fbd8..2f711d0dcf04d 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/index.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/index.tsx @@ -24,6 +24,7 @@ import { getScaleType } from '../to_expression'; import { TooltipWrapper } from '../../shared_components'; import { getDefaultVisualValuesForLayer } from '../../shared_components/datasource_default_values'; import { getDataLayers } from '../visualization_helpers'; +import { convertActiveDataFromIndexesToLayers } from '../reference_line_helpers'; type UnwrapArray = T extends Array ? P : T; type AxesSettingsConfigKeys = keyof AxesSettingsConfig; @@ -121,8 +122,9 @@ export const XyToolbar = memo(function XyToolbar( const dataLayers = getDataLayers(state?.layers); const shouldRotate = state?.layers.length ? isHorizontalChart(state.layers) : false; - const axisGroups = getAxesConfiguration(dataLayers, shouldRotate, frame.activeData); - const dataBounds = getDataBounds(frame.activeData, axisGroups); + const activeData = convertActiveDataFromIndexesToLayers(frame.activeData, state.layers); + const axisGroups = getAxesConfiguration(dataLayers, shouldRotate, activeData); + const dataBounds = getDataBounds(activeData, axisGroups); const tickLabelsVisibilitySettings = { x: state?.tickLabelsVisibilitySettings?.x ?? true, diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/xy_config_panel.test.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/xy_config_panel.test.tsx index 953828db48a72..2799a607e8805 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/xy_config_panel.test.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/xy_config_panel.test.tsx @@ -128,7 +128,7 @@ describe('XY Config panels', () => { it('should pass in information about current data bounds', () => { const state = testState(); frame.activeData = { - first: { + 0: { type: 'datatable', rows: [{ bar: -5 }, { bar: 50 }], columns: [ From 5e99314e166a1720251bff3bea9a8830efef17c3 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Thu, 31 Mar 2022 17:08:36 +0300 Subject: [PATCH 077/133] Fixed reference lines. --- .../indexpattern_datasource/indexpattern.tsx | 1 + .../reference_line_helpers.tsx | 39 ++++++++++++++----- .../public/xy_visualization/visualization.tsx | 10 +++-- .../xy_config_panel/color_picker.tsx | 2 +- 4 files changed, 38 insertions(+), 14 deletions(-) diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx index d0b644e2bf9b4..631a5f6fd9eb0 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx @@ -235,6 +235,7 @@ export function getIndexPatternDatasource({ if (staticValue == null) { return state; } + return mergeLayer({ state, layerId, diff --git a/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx b/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx index 4080bc7595f5b..daa4430ceda9f 100644 --- a/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx @@ -257,22 +257,43 @@ function computeStaticValueForGroup( } } +/** + * @function convertActiveDataFromIndexesToLayers - converts hashmap of tables, stored by layers' indexes + * (created at `layeredXyVis` expression function), to hashmap of tables, stored by layers' ids. Before, + * layers, passed to `xy` expression function contained layerIds. But it is impossible to continue using + * this approach any more, as far as the idea of multitable is going to be deprecated. + * @param activeData - hashmap of tables, containing requested data. + * @param layers - array of data visualization configuration. Each layer has its own table at the `activeData`. + * @returns - new hashmap of tables, where all the tables are mapped by layerId. + */ export const convertActiveDataFromIndexesToLayers = ( activeData: Record | undefined = {}, layers: XYState['layers'] = [] -) => { - const layersIdsToIndexes = layers.reduce>( - (layersWithIndexes, { layerId }, index) => ({ ...layersWithIndexes, [index]: layerId }), +): Record | undefined => { + if (!activeData) { + return activeData; + } + + const indexesToLayerIds = layers.reduce>( + (layersWithIndexes, { layerId }, index) => + layerId ? { ...layersWithIndexes, [index]: layerId } : layersWithIndexes, {} ); - return Object.entries(activeData ?? {}).reduce>( - (dataByLayerIds, [layerIndex, dataPerLayer]) => ({ + const convertedActiveData = Object.entries(activeData).reduce< + Record + >((dataByLayerIds, [layerIndex, dataPerLayer]) => { + // if layer index doesn't exist at the map of layer index, it means, that is + // a layerId and should be mapped without conveting from index to layerId. + const index = Number(layerIndex); + const layerId = isNaN(index) ? layerIndex : indexesToLayerIds[index] ?? layerIndex; + return { ...dataByLayerIds, - [layersIdsToIndexes[layerIndex]]: dataPerLayer, - }), - {} - ); + [layerId]: dataPerLayer, + }; + }, {}); + + return Object.keys(convertedActiveData).length ? convertedActiveData : undefined; }; export const getReferenceSupportedLayer = ( diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx index cdc77f8ded423..cccbec3968e6c 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx @@ -617,10 +617,12 @@ export const getXyVisualization = ({ ...getDataLayers(state.layers), ...getReferenceLayers(state.layers), ].filter(({ accessors }) => accessors.length > 0); + const accessorsWithArrayValues = []; + for (const layer of filteredLayers) { const { layerId, accessors } = layer; - const rows = activeData[layerId] && activeData[layerId].rows; + const rows = activeData?.[layerId] && activeData[layerId].rows; if (!rows) { break; } @@ -632,6 +634,7 @@ export const getXyVisualization = ({ } } } + return accessorsWithArrayValues.map((label) => ( Date: Thu, 31 Mar 2022 17:09:58 +0300 Subject: [PATCH 078/133] Fixed jsdoc. --- .../public/xy_visualization/reference_line_helpers.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx b/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx index daa4430ceda9f..7903aaee4a55e 100644 --- a/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx @@ -258,13 +258,13 @@ function computeStaticValueForGroup( } /** - * @function convertActiveDataFromIndexesToLayers - converts hashmap of tables, stored by layers' indexes + * Converts hashmap of tables, stored by layers' indexes * (created at `layeredXyVis` expression function), to hashmap of tables, stored by layers' ids. Before, * layers, passed to `xy` expression function contained layerIds. But it is impossible to continue using * this approach any more, as far as the idea of multitable is going to be deprecated. - * @param activeData - hashmap of tables, containing requested data. - * @param layers - array of data visualization configuration. Each layer has its own table at the `activeData`. - * @returns - new hashmap of tables, where all the tables are mapped by layerId. + * @param activeData hashmap of tables, containing requested data. + * @param layers array of data visualization configuration. Each layer has its own table at the `activeData`. + * @returns new hashmap of tables, where all the tables are mapped by layerId. */ export const convertActiveDataFromIndexesToLayers = ( activeData: Record | undefined = {}, From ed03e2cf0156bf9e09aad9840a120513c6c26126 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Fri, 1 Apr 2022 12:25:35 +0300 Subject: [PATCH 079/133] Added annotations to layeredXyVIs. --- .../extended_annotation_layer.ts | 53 +++++++++++++++++++ .../common/expression_functions/index.ts | 1 + .../expression_functions/layered_xy_vis.ts | 3 +- .../expression_xy/common/index.ts | 1 + .../expression_xy/public/plugin.ts | 2 + .../expression_xy/server/plugin.ts | 2 + .../public/xy_visualization/to_expression.ts | 9 ++-- 7 files changed, 65 insertions(+), 6 deletions(-) create mode 100644 src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_annotation_layer.ts diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_annotation_layer.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_annotation_layer.ts new file mode 100644 index 0000000000000..ebc0941e159bc --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_annotation_layer.ts @@ -0,0 +1,53 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import type { Datatable, ExpressionFunctionDefinition } from '../../../../expressions/common'; +import { LayerTypes, EXTENDED_ANNOTATION_LAYER } from '../constants'; +import { ExtendedAnnotationLayerArgs, ExtendedAnnotationLayerConfigResult } from '../types'; + +export function extendedAnnotationLayerFunction(): ExpressionFunctionDefinition< + typeof EXTENDED_ANNOTATION_LAYER, + Datatable, + ExtendedAnnotationLayerArgs, + ExtendedAnnotationLayerConfigResult +> { + return { + name: EXTENDED_ANNOTATION_LAYER, + aliases: [], + type: EXTENDED_ANNOTATION_LAYER, + inputTypes: ['datatable'], + help: 'Annotation layer in lens', + args: { + hide: { + types: ['boolean'], + default: false, + help: 'Show details', + }, + annotations: { + types: ['manual_event_annotation'], + help: '', + multi: true, + }, + table: { + types: ['datatable'], + help: i18n.translate('expressionXY.dataLayer.table.help', { + defaultMessage: 'Table', + }), + }, + }, + fn: (input, args) => { + return { + type: EXTENDED_ANNOTATION_LAYER, + ...args, + layerType: LayerTypes.ANNOTATIONS, + table: args.table ?? input, + }; + }, + }; +} diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/index.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/index.ts index b385a0cf17b22..4f9e7340046ab 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/index.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/index.ts @@ -10,6 +10,7 @@ export * from './xy_vis'; export * from './layered_xy_vis'; export * from './legend_config'; export * from './annotation_layer'; +export * from './extended_annotation_layer'; export * from './y_axis_config'; export * from './data_layer'; export * from './extended_data_layer'; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts index c33322e4ddd65..0ff8849a42744 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts @@ -24,6 +24,7 @@ import { EXTENDED_REFERENCE_LINE_LAYER, LAYERED_XY_VIS, EndValues, + EXTENDED_ANNOTATION_LAYER, } from '../constants'; import { logDatatables } from '../utils'; @@ -135,7 +136,7 @@ export const layeredXyVisFunction: ExpressionFunctionDefinition< }), }, layers: { - types: [EXTENDED_DATA_LAYER, EXTENDED_REFERENCE_LINE_LAYER], + types: [EXTENDED_DATA_LAYER, EXTENDED_REFERENCE_LINE_LAYER, EXTENDED_ANNOTATION_LAYER], help: i18n.translate('expressionXY.layeredXyVis.layers.help', { defaultMessage: 'Layers of visual series', }), diff --git a/src/plugins/chart_expressions/expression_xy/common/index.ts b/src/plugins/chart_expressions/expression_xy/common/index.ts index e3e978412fb63..50c3afc50b204 100755 --- a/src/plugins/chart_expressions/expression_xy/common/index.ts +++ b/src/plugins/chart_expressions/expression_xy/common/index.ts @@ -17,6 +17,7 @@ export { gridlinesConfigFunction, dataLayerFunction, annotationLayerFunction, + extendedAnnotationLayerFunction, extendedDataLayerFunction, axisExtentConfigFunction, tickLabelsConfigFunction, diff --git a/src/plugins/chart_expressions/expression_xy/public/plugin.ts b/src/plugins/chart_expressions/expression_xy/public/plugin.ts index 31d0dfdf8f0b8..946a346a67789 100755 --- a/src/plugins/chart_expressions/expression_xy/public/plugin.ts +++ b/src/plugins/chart_expressions/expression_xy/public/plugin.ts @@ -26,6 +26,7 @@ import { referenceLineLayerFunction, extendedReferenceLineLayerFunction, annotationLayerFunction, + extendedAnnotationLayerFunction, labelsOrientationConfigFunction, axisTitlesVisibilityConfigFunction, } from '../common'; @@ -61,6 +62,7 @@ export class ExpressionXyPlugin { expressions.registerFunction(axisExtentConfigFunction); expressions.registerFunction(tickLabelsConfigFunction); expressions.registerFunction(annotationLayerFunction); + expressions.registerFunction(extendedAnnotationLayerFunction); expressions.registerFunction(labelsOrientationConfigFunction); expressions.registerFunction(referenceLineLayerFunction); expressions.registerFunction(extendedReferenceLineLayerFunction); diff --git a/src/plugins/chart_expressions/expression_xy/server/plugin.ts b/src/plugins/chart_expressions/expression_xy/server/plugin.ts index 65f773c31ed71..71dad2f115b0a 100755 --- a/src/plugins/chart_expressions/expression_xy/server/plugin.ts +++ b/src/plugins/chart_expressions/expression_xy/server/plugin.ts @@ -24,6 +24,7 @@ import { extendedDataLayerFunction, extendedReferenceLineLayerFunction, layeredXyVisFunction, + extendedAnnotationLayerFunction, } from '../common'; import { SetupDeps } from './types'; @@ -39,6 +40,7 @@ export class ExpressionXyPlugin expressions.registerFunction(axisExtentConfigFunction); expressions.registerFunction(tickLabelsConfigFunction); expressions.registerFunction(annotationLayerFunction); + expressions.registerFunction(extendedAnnotationLayerFunction); expressions.registerFunction(labelsOrientationConfigFunction); expressions.registerFunction(referenceLineLayerFunction); expressions.registerFunction(extendedReferenceLineLayerFunction); diff --git a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts index 3a69176d950d8..c5302ee587cb2 100644 --- a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts +++ b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts @@ -417,7 +417,7 @@ const referenceLineLayerToExpression = ( : [], accessors: layer.accessors, columnToLabel: [JSON.stringify(getColumnToLabelMap(layer, datasourceLayer))], - table: [buildTableExpression(datasourceExpression)], + ...(datasourceExpression ? { table: [buildTableExpression(datasourceExpression)] } : {}), }, }, ], @@ -434,11 +434,10 @@ const annotationLayerToExpression = ( chain: [ { type: 'function', - function: 'annotationLayer', + function: 'extendedAnnotationLayer', arguments: { hide: [Boolean(layer.hide)], - layerId: [layer.layerId], - table: [buildTableExpression(datasourceExpression)], + ...(datasourceExpression ? { table: [buildTableExpression(datasourceExpression)] } : {}), annotations: layer.annotations ? layer.annotations.map( (ann): Ast => @@ -499,7 +498,7 @@ const dataLayerToExpression = ( seriesType: [layer.seriesType], accessors: layer.accessors, columnToLabel: [JSON.stringify(columnToLabel)], - table: [buildTableExpression(datasourceExpression)], + ...(datasourceExpression ? { table: [buildTableExpression(datasourceExpression)] } : {}), palette: [ { type: 'expression', From c4416521ab16ad1c896306d43747d308fa6e7571 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Fri, 1 Apr 2022 12:53:09 +0300 Subject: [PATCH 080/133] Fixed limits. --- packages/kbn-optimizer/limits.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index e137bd0055900..efb096940ab31 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -124,5 +124,5 @@ pageLoadAssetSize: sessionView: 77750 cloudSecurityPosture: 19109 visTypeGauge: 24113 - expressionXY: 26500 + expressionXY: 47156 eventAnnotation: 19334 From b200e37c9f78190c4934838331227122d852160c Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Fri, 1 Apr 2022 16:24:33 +0300 Subject: [PATCH 081/133] Refactored the implementation to be reusable. --- .../editor_frame/config_panel/layer_panel.tsx | 29 +++-- .../config_panel/layer_settings.tsx | 5 +- .../workspace_panel_wrapper.tsx | 2 +- .../lens/public/embeddable/embeddable.tsx | 11 ++ x-pack/plugins/lens/public/types.ts | 15 ++- .../xy_visualization/annotations/helpers.tsx | 4 +- .../reference_line_helpers.tsx | 15 ++- .../public/xy_visualization/visualization.tsx | 110 +++++++++++++----- .../xy_config_panel/color_picker.tsx | 15 +-- 9 files changed, 140 insertions(+), 66 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 e404faacb8f97..620eda0e80907 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 @@ -145,8 +145,6 @@ export function LayerPanel( const isEmptyLayer = !groups.some((d) => d.accessors.length > 0); const { activeId, activeGroup } = activeDimension; - const { setDimension, removeDimension } = activeVisualization; - const allAccessors = groups.flatMap((group) => group.accessors.map((accessor) => accessor.columnId) ); @@ -209,7 +207,7 @@ export function LayerPanel( previousColumn = typeof dropResult === 'object' ? dropResult.deleted : undefined; } } - const newVisState = setDimension({ + const newVisState = activeVisualization.setDimension({ columnId, groupId, layerId: targetLayerId, @@ -221,7 +219,7 @@ export function LayerPanel( if (typeof dropResult === 'object') { // When a column is moved, we delete the reference to the old updateVisualization( - removeDimension({ + activeVisualization.removeDimension({ columnId: dropResult.deleted, layerId: targetLayerId, prevState: newVisState, @@ -234,7 +232,7 @@ export function LayerPanel( } } else { if (dropType === 'duplicate_compatible' || dropType === 'reorder') { - const newVisState = setDimension({ + const newVisState = activeVisualization.setDimension({ columnId, groupId, layerId: targetLayerId, @@ -247,16 +245,15 @@ export function LayerPanel( } }; }, [ - framePublicAPI, + layerDatasource, + setNextFocusedButtonId, groups, layerDatasourceOnDrop, + layerDatasourceDropProps, + activeVisualization, props.visualizationState, + framePublicAPI, updateVisualization, - setDimension, - removeDimension, - layerDatasourceDropProps, - setNextFocusedButtonId, - layerDatasource, ]); const isDimensionPanelOpen = Boolean(activeId); @@ -360,7 +357,7 @@ export function LayerPanel( <> {layerDatasource ? ( {activeGroup && activeId && layerDatasource && ( + ); } diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.tsx index bb85ed4019412..b63607af99fa3 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.tsx @@ -129,7 +129,7 @@ export function WorkspacePanelWrapper({ {activeVisualization && activeVisualization.renderToolbar && ( { this.activeDataInfo.activeData = adapters?.tables?.tables; + if (this.savedVis?.visualizationType) { + const { activeData } = this.activeDataInfo; + this.activeDataInfo.activeData = + this.deps.visualizationMap[this.savedVis.visualizationType].convertActiveData?.( + activeData, + this.savedVis.state.visualization + ) ?? activeData; + } + if (this.input.onLoad) { // once onData$ is get's called from expression renderer, loading becomes false this.input.onLoad(false); diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index 91cd86572d3e8..6083f66ddbe07 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -588,7 +588,7 @@ export type VisualizationDimensionGroupConfig = SharedDimensionProps & { labels?: { buttonAriaLabel: string; buttonLabel: string }; }; -interface VisualizationDimensionChangeProps { +export interface VisualizationDimensionChangeProps { layerId: string; columnId: string; prevState: T; @@ -922,7 +922,20 @@ export interface Visualization { */ onEditAction?: (state: T, event: LensEditEvent) => T; + /** + * `datasourceExpressionsByLayers` will be passed to the params of `toExpression` and `toPreviewExpression` + * functions and datasource expressions will not be appended to the expression automatically. + */ shouldBuildDatasourceExpressionManually?: () => boolean; + + /** + * Converts `activeData`, came from expressions in the form of hashmap as `{ [index]: table, ...}`, to the hashmap of + * layer ids and tables as `{ [layerId]: table }`. + */ + convertActiveData?: ( + activeData?: FramePublicAPI['activeData'], + state?: T + ) => FramePublicAPI['activeData']; } export interface LensFilterEvent { diff --git a/x-pack/plugins/lens/public/xy_visualization/annotations/helpers.tsx b/x-pack/plugins/lens/public/xy_visualization/annotations/helpers.tsx index 5cca5f44d3864..ba54486782e47 100644 --- a/x-pack/plugins/lens/public/xy_visualization/annotations/helpers.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/annotations/helpers.tsx @@ -21,7 +21,6 @@ import { import { LensIconChartBarAnnotations } from '../../assets/chart_bar_annotations'; import { generateId } from '../../id_generator'; import { defaultAnnotationColor } from '../../../../../../src/plugins/event_annotation/public'; -import { convertActiveDataFromIndexesToLayers } from '../reference_line_helpers'; const MAX_DATE = 8640000000000000; const MIN_DATE = -8640000000000000; @@ -120,14 +119,13 @@ export const setAnnotationsDimension: Visualization['setDimension'] = ( : undefined; let resultAnnotations = [...inputAnnotations] as XYAnnotationLayerConfig['annotations']; - const activeData = convertActiveDataFromIndexesToLayers(frame?.activeData, prevState.layers); if (!currentConfig) { resultAnnotations.push({ label: defaultAnnotationLabel, key: { type: 'point_in_time', - timestamp: getStaticDate(getDataLayers(prevState.layers), activeData), + timestamp: getStaticDate(getDataLayers(prevState.layers), frame.activeData), }, icon: 'triangle', ...previousConfig, diff --git a/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx b/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx index 7903aaee4a55e..0d2a7d5ebf5db 100644 --- a/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx @@ -267,7 +267,7 @@ function computeStaticValueForGroup( * @returns new hashmap of tables, where all the tables are mapped by layerId. */ export const convertActiveDataFromIndexesToLayers = ( - activeData: Record | undefined = {}, + activeData: Record | undefined, layers: XYState['layers'] = [] ): Record | undefined => { if (!activeData) { @@ -316,13 +316,12 @@ export const getReferenceSupportedLayer = ( ]; const layers = state?.layers || []; - const activeData = convertActiveDataFromIndexesToLayers(frame?.activeData, layers); const referenceLineGroups = getGroupsRelatedToData( referenceLineGroupIds, state, frame?.datasourceLayers || {}, - activeData + frame?.activeData ); const dataLayers = getDataLayers(layers); @@ -342,7 +341,12 @@ export const getReferenceSupportedLayer = ( columnId: generateId(), dataType: 'number', label: getAxisName(label, { isHorizontal: isHorizontalChart(state?.layers || []) }), - staticValue: getStaticValue(dataLayers, label, { activeData }, layerHasNumberHistogram), + staticValue: getStaticValue( + dataLayers, + label, + { activeData: frame?.activeData }, + layerHasNumberHistogram + ), })) : undefined; @@ -446,7 +450,6 @@ export const getReferenceConfiguration = ({ } ); - const activeData = convertActiveDataFromIndexesToLayers(frame?.activeData, state.layers); const groupsToShow = getGroupsToShow( [ // When a reference layer panel is added, a static reference line should automatically be included by default @@ -472,7 +475,7 @@ export const getReferenceConfiguration = ({ ], state, frame.datasourceLayers, - activeData + frame.activeData ); const isHorizontal = isHorizontalChart(state.layers); return { diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx index b83aa188b3326..04b5661e75b3f 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx @@ -20,7 +20,14 @@ import { getSuggestions } from './xy_suggestions'; import { XyToolbar } from './xy_config_panel'; import { DimensionEditor } from './xy_config_panel/dimension_editor'; import { LayerHeader } from './xy_config_panel/layer_header'; -import type { Visualization, AccessorConfig, FramePublicAPI } from '../types'; +import type { + Visualization, + AccessorConfig, + FramePublicAPI, + VisualizationDimensionChangeProps, + VisualizationConfigProps, + VisualizationToolbarProps, +} from '../types'; import { FillStyle, SeriesType, @@ -72,6 +79,46 @@ import { AnnotationsPanel } from './xy_config_panel/annotations_config_panel'; import { DimensionTrigger } from '../shared_components/dimension_trigger'; import { defaultAnnotationLabel } from './annotations/helpers'; +type ConvertActiveDataFn = ( + activeData?: FramePublicAPI['activeData'], + state?: State +) => FramePublicAPI['activeData']; + +const updateFrame = ( + state: State | undefined, + frame: FramePublicAPI, + convertActiveData?: ConvertActiveDataFn +) => { + if (!frame) { + return frame; + } + + const activeData = convertActiveData?.(frame?.activeData, state) ?? frame?.activeData; + return Object.assign(frame, { activeData }); +}; + +const isVisualizationDimensionChangeProps = ( + props: + | VisualizationConfigProps + | VisualizationDimensionChangeProps + | VisualizationToolbarProps +): props is VisualizationDimensionChangeProps => { + if ((props as VisualizationDimensionChangeProps).prevState) { + return true; + } + return false; +}; + +function updateProps< + T extends + | VisualizationConfigProps + | VisualizationDimensionChangeProps + | VisualizationToolbarProps +>(props: T, convertActiveData?: ConvertActiveDataFn) { + const state = isVisualizationDimensionChangeProps(props) ? props.prevState : props.state; + return { ...props, frame: updateFrame(state, props.frame), convertActiveData }; +} + export const getXyVisualization = ({ paletteService, fieldFormats, @@ -174,35 +221,36 @@ export const getXyVisualization = ({ }, getSupportedLayers(state, frame) { + const newFrame = frame ? updateFrame(state, frame, this.convertActiveData) : frame; return [ supportedDataLayer, - getAnnotationsSupportedLayer(state, frame), - getReferenceSupportedLayer(state, frame), + getAnnotationsSupportedLayer(state, newFrame), + getReferenceSupportedLayer(state, newFrame), ]; }, getConfiguration({ state, frame, layerId }) { - const activeData = convertActiveDataFromIndexesToLayers(frame.activeData, state.layers); + const newFrame = updateFrame(state, frame, this.convertActiveData); const layer = state.layers.find((l) => l.layerId === layerId); if (!layer) { return { groups: [] }; } if (isAnnotationsLayer(layer)) { - return getAnnotationsConfiguration({ state, frame, layer }); + return getAnnotationsConfiguration({ state, frame: newFrame, layer }); } const sortedAccessors: string[] = getSortedAccessors( - frame.datasourceLayers[layer.layerId], + newFrame.datasourceLayers[layer.layerId], layer ); if (isReferenceLayer(layer)) { - return getReferenceConfiguration({ state, frame, layer, sortedAccessors }); + return getReferenceConfiguration({ state, frame: newFrame, layer, sortedAccessors }); } const mappedAccessors = getMappedAccessors({ state, - frame, + frame: newFrame, layer, fieldFormats, paletteService, @@ -210,14 +258,14 @@ export const getXyVisualization = ({ }); if (isReferenceLayer(layer)) { - return getReferenceConfiguration({ state, frame, layer, sortedAccessors }); + return getReferenceConfiguration({ state, frame: newFrame, layer, sortedAccessors }); } const dataLayer: XYDataLayerConfig = layer; const dataLayers = getDataLayers(state.layers); const isHorizontal = isHorizontalChart(state.layers); - const { left, right } = groupAxesByType([layer], activeData); + const { left, right } = groupAxesByType([layer], newFrame.activeData); // Check locally if it has one accessor OR one accessor per axis const layerHasOnlyOneAccessor = Boolean( dataLayer.accessors.length < 2 || @@ -237,7 +285,10 @@ export const getXyVisualization = ({ Boolean(l.xAccessor) === Boolean(dataLayer.xAccessor) && Boolean(l.splitAccessor) === Boolean(dataLayer.splitAccessor) ) { - const { left: localLeft, right: localRight } = groupAxesByType([l], activeData); + const { left: localLeft, right: localRight } = groupAxesByType( + [l], + newFrame.activeData + ); // return true only if matching axis are found return ( l.accessors.length && @@ -301,7 +352,9 @@ export const getXyVisualization = ({ }, setDimension(props) { - const { prevState, layerId, columnId, groupId } = props; + const newProps = updateProps(props, this.convertActiveData); + const { prevState, layerId, columnId, groupId } = newProps; + const foundLayer: XYLayerConfig | undefined = prevState.layers.find( (l) => l.layerId === layerId ); @@ -310,10 +363,10 @@ export const getXyVisualization = ({ } if (isReferenceLayer(foundLayer)) { - return setReferenceDimension(props); + return setReferenceDimension(newProps); } if (isAnnotationsLayer(foundLayer)) { - return setAnnotationsDimension(props); + return setAnnotationsDimension(newProps); } const newLayer: XYDataLayerConfig = Object.assign({}, foundLayer); @@ -413,8 +466,7 @@ export const getXyVisualization = ({ }, removeDimension({ prevState, layerId, columnId, frame }) { - const activeData = convertActiveDataFromIndexesToLayers(frame.activeData, prevState.layers); - + const newFrame = updateFrame(prevState, frame, this.convertActiveData); const foundLayer = prevState.layers.find((l) => l.layerId === layerId); if (!foundLayer) { return prevState; @@ -452,8 +504,8 @@ export const getXyVisualization = ({ // check for data layers if they all still have xAccessors const groupsAvailable = getGroupsAvailableInData( getDataLayers(prevState.layers), - frame.datasourceLayers, - activeData + newFrame.datasourceLayers, + newFrame.activeData ); if ( @@ -473,10 +525,11 @@ export const getXyVisualization = ({ }, renderLayerHeader(domElement, props) { + const newProps = updateProps(props, this.convertActiveData); render( - + , domElement @@ -484,10 +537,11 @@ export const getXyVisualization = ({ }, renderToolbar(domElement, props) { + const newProps = updateProps(props, this.convertActiveData); render( - + , domElement @@ -495,8 +549,10 @@ export const getXyVisualization = ({ }, renderDimensionEditor(domElement, props) { + const newProps = updateProps(props, this.convertActiveData); + const allProps = { - ...props, + ...newProps, formatFactory: fieldFormats.deserialize, paletteService, }; @@ -519,6 +575,9 @@ export const getXyVisualization = ({ shouldBuildDatasourceExpressionManually: () => true, + convertActiveData: (activeData, state) => + convertActiveDataFromIndexesToLayers(activeData, state?.layers), + toExpression: (state, layers, attributes, datasourceExpressionsByLayers = {}) => toExpression( state, @@ -612,7 +671,7 @@ export const getXyVisualization = ({ if (state?.layers.length === 0 || !frame.activeData) { return; } - const activeData = convertActiveDataFromIndexesToLayers(frame.activeData, state.layers); + const newFrame = updateFrame(state, frame, this.convertActiveData); const filteredLayers = [ ...getDataLayers(state.layers), @@ -623,7 +682,7 @@ export const getXyVisualization = ({ for (const layer of filteredLayers) { const { layerId, accessors } = layer; - const rows = activeData?.[layerId] && activeData[layerId].rows; + const rows = newFrame.activeData?.[layerId] && newFrame.activeData[layerId].rows; if (!rows) { break; } @@ -697,11 +756,10 @@ const getMappedAccessors = ({ columnId: accessor, })); - const activeData = convertActiveDataFromIndexesToLayers(frame.activeData, state.layers); - if (activeData) { + if (frame.activeData) { const colorAssignments = getColorAssignments( getDataLayers(state.layers), - { tables: activeData }, + { tables: frame.activeData }, fieldFormats.deserialize ); mappedAccessors = getAccessorColorConfig( diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/color_picker.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/color_picker.tsx index 6196b915a47c5..5671d4ba6bc24 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/color_picker.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/color_picker.tsx @@ -22,7 +22,6 @@ import { import { getSortedAccessors } from '../to_expression'; import { TooltipWrapper } from '../../shared_components'; import { isReferenceLayer, isAnnotationsLayer, getDataLayers } from '../visualization_helpers'; -import { convertActiveDataFromIndexesToLayers } from '../reference_line_helpers'; const tooltipContent = { auto: i18n.translate('xpack.lens.configPanel.color.tooltip.auto', { @@ -60,7 +59,6 @@ export const ColorPicker = ({ const layer = state.layers[index]; const overwriteColor = getSeriesColor(layer, accessor); - const activeData = convertActiveDataFromIndexesToLayers(frame.activeData, state.layers); const currentColor = useMemo(() => { if (overwriteColor || !frame.activeData) return overwriteColor; if (isReferenceLayer(layer)) { @@ -77,7 +75,7 @@ export const ColorPicker = ({ const colorAssignments = getColorAssignments( getDataLayers(state.layers), - { tables: activeData ?? {} }, + { tables: frame.activeData ?? {} }, formatFactory ); const mappedAccessors = getAccessorColorConfig( @@ -91,16 +89,7 @@ export const ColorPicker = ({ ); return mappedAccessors.find((a) => a.columnId === accessor)?.color || null; - }, [ - overwriteColor, - frame, - layer, - state.layers, - activeData, - formatFactory, - paletteService, - accessor, - ]); + }, [overwriteColor, frame, layer, state.layers, formatFactory, paletteService, accessor]); const [color, setColor] = useState(currentColor); From 8c2ea64aa9e794979125f8132eb77d72d950413c Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Mon, 4 Apr 2022 19:20:12 +0300 Subject: [PATCH 082/133] Fixed undefined layers. --- .../expression_xy/common/expression_functions/xy_vis.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts index e580e20d38053..f5b94e6c9e3c3 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts @@ -194,7 +194,7 @@ export const xyVisFunction: ExpressionFunctionDefinition< }, }, fn(data, args, handlers) { - const { dataLayers, referenceLineLayers, annotationLayers, ...restArgs } = args; + const { dataLayers = [], referenceLineLayers = [], annotationLayers = [], ...restArgs } = args; const inputLayers: Array = [ ...dataLayers, ...referenceLineLayers, From fb370d995a7a2bce4ae0d2ee599acb70f142d6e3 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Tue, 5 Apr 2022 13:28:41 +0300 Subject: [PATCH 083/133] Fixed empty arrays problems. --- .../common/expression_functions/annotation_layer.ts | 1 + .../expression_xy/common/expression_functions/data_layer.ts | 1 + .../common/expression_functions/extended_data_layer.ts | 1 + .../expression_functions/extended_reference_line_layer.ts | 1 + .../expression_xy/common/expression_functions/layered_xy_vis.ts | 2 +- .../common/expression_functions/reference_line_layer.ts | 1 + .../expression_xy/common/expression_functions/xy_vis.ts | 2 +- .../expression_xy/common/types/expression_functions.ts | 2 +- 8 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/annotation_layer.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/annotation_layer.ts index aa85a912161ad..a20eb91bdfc71 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/annotation_layer.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/annotation_layer.ts @@ -38,6 +38,7 @@ export function annotationLayerFunction(): ExpressionFunctionDefinition< return { type: ANNOTATION_LAYER, ...args, + annotations: args.annotations ?? [], layerType: LayerTypes.ANNOTATIONS, table: input, }; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer.ts index cc4d49455f2cc..b78592aaa2821 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer.ts @@ -111,6 +111,7 @@ export const dataLayerFunction: ExpressionFunctionDefinition< return { type: DATA_LAYER, ...args, + accessors: args.accessors ?? [], layerType: LayerTypes.DATA, table, }; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_data_layer.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_data_layer.ts index 01ee4f2a8c40b..adebc5d4540e0 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_data_layer.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_data_layer.ts @@ -117,6 +117,7 @@ export const extendedDataLayerFunction: ExpressionFunctionDefinition< return { type: EXTENDED_DATA_LAYER, ...args, + accessors: args.accessors ?? [], layerType: LayerTypes.DATA, table: args.table ?? input, }; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_reference_line_layer.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_reference_line_layer.ts index 7765591b05810..29d888c87d283 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_reference_line_layer.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_reference_line_layer.ts @@ -56,6 +56,7 @@ export const extendedReferenceLineLayerFunction: ExpressionFunctionDefinition< return { type: EXTENDED_REFERENCE_LINE_LAYER, ...args, + accessors: args.accessors ?? [], layerType: LayerTypes.REFERENCELINE, table: args.table ?? input, }; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts index 0ff8849a42744..3e614ef81a0a4 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts @@ -178,7 +178,7 @@ export const layeredXyVisFunction: ExpressionFunctionDefinition< }, }, fn(data, args, handlers) { - const layers = args.layers.filter( + const layers = (args.layers ?? []).filter( (layer): layer is XYExtendedLayerConfigResult => layer !== undefined ); diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line_layer.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line_layer.ts index 5cb75884f31bb..0a93b31e8c623 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line_layer.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line_layer.ts @@ -50,6 +50,7 @@ export const referenceLineLayerFunction: ExpressionFunctionDefinition< return { type: REFERENCE_LINE_LAYER, ...args, + accessors: args.accessors ?? [], layerType: LayerTypes.REFERENCELINE, table, }; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts index f5b94e6c9e3c3..ffc7674d00bff 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts @@ -190,7 +190,6 @@ export const xyVisFunction: ExpressionFunctionDefinition< help: i18n.translate('expressionXY.xyVis.ariaLabel.help', { defaultMessage: 'Specifies the aria label of the xy chart', }), - required: false, }, }, fn(data, args, handlers) { @@ -218,6 +217,7 @@ export const xyVisFunction: ExpressionFunctionDefinition< handlers.inspectorAdapters.tables.logDatatable('default', logTable); } + return { type: 'render', as: XY_VIS_RENDERER, diff --git a/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts b/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts index 433049f6f0073..cf73edb25a6b2 100644 --- a/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts +++ b/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts @@ -212,7 +212,7 @@ export interface LayeredXYArgs { endValue?: EndValue; emphasizeFitting?: boolean; valueLabels: ValueLabelMode; - layers: XYExtendedLayerConfigResult[]; + layers?: XYExtendedLayerConfigResult[]; fittingFunction?: FittingFunction; axisTitlesVisibilitySettings?: AxisTitlesVisibilityConfigResult; tickLabelsVisibilitySettings?: TickLabelsConfigResult; From 10c85fe4bd28dc44d09e79f6cee514d993c02cb5 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Tue, 5 Apr 2022 14:42:38 +0300 Subject: [PATCH 084/133] Fixed input translations and removed not used arguments. --- .../expression_functions/annotation_layer.ts | 13 ++++-- .../extended_annotation_layer.ts | 10 ++-- .../extended_data_layer.ts | 26 +++++------ .../extended_reference_line_layer.ts | 10 ++-- .../expression_functions/layered_xy_vis.ts | 46 ++++++++----------- .../common/expression_functions/xy_vis.ts | 8 ---- .../common/types/expression_functions.ts | 6 --- .../public/xy_visualization/to_expression.ts | 4 -- 8 files changed, 54 insertions(+), 69 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/annotation_layer.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/annotation_layer.ts index a20eb91bdfc71..5dadec184aa65 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/annotation_layer.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/annotation_layer.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import { i18n } from '@kbn/i18n'; import type { Datatable, ExpressionFunctionDefinition } from '../../../../expressions/common'; import { LayerTypes, ANNOTATION_LAYER } from '../constants'; import { AnnotationLayerArgs, AnnotationLayerConfigResult } from '../types'; @@ -21,16 +22,22 @@ export function annotationLayerFunction(): ExpressionFunctionDefinition< aliases: [], type: ANNOTATION_LAYER, inputTypes: ['datatable'], - help: 'Annotation layer in lens', + help: i18n.translate('expressionXY.annotationLayer.help', { + defaultMessage: `Configure an annotation layer in the xy chart`, + }), args: { hide: { types: ['boolean'], default: false, - help: 'Show details', + help: i18n.translate('expressionXY.annotationLayer.hide.help', { + defaultMessage: 'Show / hide details', + }), }, annotations: { types: ['manual_event_annotation'], - help: '', + help: i18n.translate('expressionXY.annotationLayer.annotations.help', { + defaultMessage: 'Annotationss', + }), multi: true, }, }, diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_annotation_layer.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_annotation_layer.ts index ebc0941e159bc..0962cc472ab26 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_annotation_layer.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_annotation_layer.ts @@ -22,7 +22,9 @@ export function extendedAnnotationLayerFunction(): ExpressionFunctionDefinition< aliases: [], type: EXTENDED_ANNOTATION_LAYER, inputTypes: ['datatable'], - help: 'Annotation layer in lens', + help: i18n.translate('expressionXY.extendedAnnotationLayer.help', { + defaultMessage: `Configure an annotation layer in the xy chart`, + }), args: { hide: { types: ['boolean'], @@ -31,12 +33,14 @@ export function extendedAnnotationLayerFunction(): ExpressionFunctionDefinition< }, annotations: { types: ['manual_event_annotation'], - help: '', + help: i18n.translate('expressionXY.extendedAnnotationLayer.annotations.help', { + defaultMessage: 'Annotationss', + }), multi: true, }, table: { types: ['datatable'], - help: i18n.translate('expressionXY.dataLayer.table.help', { + help: i18n.translate('expressionXY.extendedAnnotationLayer.table.help', { defaultMessage: 'Table', }), }, diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_data_layer.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_data_layer.ts index adebc5d4540e0..7df82d9d3627a 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_data_layer.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_data_layer.ts @@ -27,7 +27,7 @@ export const extendedDataLayerFunction: ExpressionFunctionDefinition< name: EXTENDED_DATA_LAYER, aliases: [], type: EXTENDED_DATA_LAYER, - help: i18n.translate('expressionXY.dataLayer.help', { + help: i18n.translate('expressionXY.extendedDataLayer.help', { defaultMessage: `Configure a layer in the xy chart`, }), inputTypes: ['datatable'], @@ -35,26 +35,26 @@ export const extendedDataLayerFunction: ExpressionFunctionDefinition< hide: { types: ['boolean'], default: false, - help: i18n.translate('expressionXY.dataLayer.hide.help', { + help: i18n.translate('expressionXY.extendedDataLayer.hide.help', { defaultMessage: 'Show / hide axis', }), }, xAccessor: { types: ['string'], - help: i18n.translate('expressionXY.dataLayer.xAccessor.help', { + help: i18n.translate('expressionXY.extendedDataLayer.xAccessor.help', { defaultMessage: 'X-axis', }), }, seriesType: { types: ['string'], options: [...Object.values(SeriesTypes)], - help: i18n.translate('expressionXY.dataLayer.seriesType.help', { + help: i18n.translate('expressionXY.extendedDataLayer.seriesType.help', { defaultMessage: 'The type of chart to display.', }), }, xScaleType: { options: [...Object.values(XScaleTypes)], - help: i18n.translate('expressionXY.dataLayer.xScaleType.help', { + help: i18n.translate('expressionXY.extendedDataLayer.xScaleType.help', { defaultMessage: 'The scale type of the x axis', }), default: XScaleTypes.ORDINAL, @@ -62,53 +62,53 @@ export const extendedDataLayerFunction: ExpressionFunctionDefinition< isHistogram: { types: ['boolean'], default: false, - help: i18n.translate('expressionXY.dataLayer.isHistogram.help', { + help: i18n.translate('expressionXY.extendedDataLayer.isHistogram.help', { defaultMessage: 'Whether to layout the chart as a histogram', }), }, yScaleType: { options: [...Object.values(YScaleTypes)], - help: i18n.translate('expressionXY.dataLayer.yScaleType.help', { + help: i18n.translate('expressionXY.extendedDataLayer.yScaleType.help', { defaultMessage: 'The scale type of the y axes', }), default: YScaleTypes.LINEAR, }, splitAccessor: { types: ['string'], - help: i18n.translate('expressionXY.dataLayer.splitAccessor.help', { + help: i18n.translate('expressionXY.extendedDataLayer.splitAccessor.help', { defaultMessage: 'The column to split by', }), }, accessors: { types: ['string'], - help: i18n.translate('expressionXY.dataLayer.accessors.help', { + help: i18n.translate('expressionXY.extendedDataLayer.accessors.help', { defaultMessage: 'The columns to display on the y axis.', }), multi: true, }, yConfig: { types: [Y_CONFIG], - help: i18n.translate('expressionXY.dataLayer.yConfig.help', { + help: i18n.translate('expressionXY.extendedDataLayer.yConfig.help', { defaultMessage: 'Additional configuration for y axes', }), multi: true, }, columnToLabel: { types: ['string'], - help: i18n.translate('expressionXY.dataLayer.columnToLabel.help', { + help: i18n.translate('expressionXY.extendedDataLayer.columnToLabel.help', { defaultMessage: 'JSON key-value pairs of column ID to label', }), }, palette: { default: `{theme "palette" default={system_palette name="default"} }`, - help: i18n.translate('expressionXY.dataLayer.palette.help', { + help: i18n.translate('expressionXY.extendedDataLayer.palette.help', { defaultMessage: 'Palette', }), types: ['palette'], }, table: { types: ['datatable'], - help: i18n.translate('expressionXY.dataLayer.table.help', { + help: i18n.translate('expressionXY.extendedDataLayer.table.help', { defaultMessage: 'Table', }), }, diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_reference_line_layer.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_reference_line_layer.ts index 29d888c87d283..d015e355ce7f0 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_reference_line_layer.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_reference_line_layer.ts @@ -20,34 +20,34 @@ export const extendedReferenceLineLayerFunction: ExpressionFunctionDefinition< name: EXTENDED_REFERENCE_LINE_LAYER, aliases: [], type: EXTENDED_REFERENCE_LINE_LAYER, - help: i18n.translate('expressionXY.referenceLineLayer.help', { + help: i18n.translate('expressionXY.extendedReferenceLineLayer.help', { defaultMessage: `Configure a reference line in the xy chart`, }), inputTypes: ['datatable'], args: { accessors: { types: ['string'], - help: i18n.translate('expressionXY.referenceLineLayer.accessors.help', { + help: i18n.translate('expressionXY.extendedReferenceLineLayer.accessors.help', { defaultMessage: 'The columns to display on the y axis.', }), multi: true, }, yConfig: { types: [Y_CONFIG], - help: i18n.translate('expressionXY.referenceLineLayer.yConfig.help', { + help: i18n.translate('expressionXY.extendedReferenceLineLayer.yConfig.help', { defaultMessage: 'Additional configuration for y axes', }), multi: true, }, columnToLabel: { types: ['string'], - help: i18n.translate('expressionXY.referenceLineLayer.columnToLabel.help', { + help: i18n.translate('expressionXY.extendedReferenceLineLayer.columnToLabel.help', { defaultMessage: 'JSON key-value pairs of column ID to label', }), }, table: { types: ['datatable'], - help: i18n.translate('expressionXY.dataLayer.table.help', { + help: i18n.translate('expressionXY.extendedReferenceLineLayer.table.help', { defaultMessage: 'Table', }), }, diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts index 3e614ef81a0a4..b50f44cd55789 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts @@ -37,65 +37,57 @@ export const layeredXyVisFunction: ExpressionFunctionDefinition< name: LAYERED_XY_VIS, type: 'render', inputTypes: ['datatable'], - help: i18n.translate('expressionXY.xyVis.help', { + help: i18n.translate('expressionXY.layeredXyVis.help', { defaultMessage: 'An X/Y chart', }), args: { - title: { - types: ['string'], - help: 'The chart title.', - }, - description: { - types: ['string'], - help: '', - }, xTitle: { types: ['string'], - help: i18n.translate('expressionXY.xyVis.xTitle.help', { + help: i18n.translate('expressionXY.layeredXyVis.xTitle.help', { defaultMessage: 'X axis title', }), }, yTitle: { types: ['string'], - help: i18n.translate('expressionXY.xyVis.yLeftTitle.help', { + help: i18n.translate('expressionXY.layeredXyVis.yLeftTitle.help', { defaultMessage: 'Y left axis title', }), }, yRightTitle: { types: ['string'], - help: i18n.translate('expressionXY.xyVis.yRightTitle.help', { + help: i18n.translate('expressionXY.layeredXyVis.yRightTitle.help', { defaultMessage: 'Y right axis title', }), }, yLeftExtent: { types: [AXIS_EXTENT_CONFIG], - help: i18n.translate('expressionXY.xyVis.yLeftExtent.help', { + help: i18n.translate('expressionXY.layeredXyVis.yLeftExtent.help', { defaultMessage: 'Y left axis extents', }), }, yRightExtent: { types: [AXIS_EXTENT_CONFIG], - help: i18n.translate('expressionXY.xyVis.yRightExtent.help', { + help: i18n.translate('expressionXY.layeredXyVis.yRightExtent.help', { defaultMessage: 'Y right axis extents', }), }, legend: { types: [LEGEND_CONFIG], - help: i18n.translate('expressionXY.xyVis.legend.help', { + help: i18n.translate('expressionXY.layeredXyVis.legend.help', { defaultMessage: 'Configure the chart legend.', }), }, fittingFunction: { types: ['string'], options: [...Object.values(FittingFunctions)], - help: i18n.translate('expressionXY.xyVis.fittingFunction.help', { + help: i18n.translate('expressionXY.layeredXyVis.fittingFunction.help', { defaultMessage: 'Define how missing values are treated', }), }, endValue: { types: ['string'], options: [...Object.values(EndValues)], - help: i18n.translate('expressionXY.xyVis.endValue.help', { + help: i18n.translate('expressionXY.layeredXyVis.endValue.help', { defaultMessage: 'End value', }), }, @@ -107,31 +99,31 @@ export const layeredXyVisFunction: ExpressionFunctionDefinition< valueLabels: { types: ['string'], options: [...Object.values(ValueLabelModes)], - help: i18n.translate('expressionXY.xyVis.valueLabels.help', { + help: i18n.translate('expressionXY.layeredXyVis.valueLabels.help', { defaultMessage: 'Value labels mode', }), }, tickLabelsVisibilitySettings: { types: [TICK_LABELS_CONFIG], - help: i18n.translate('expressionXY.xyVis.tickLabelsVisibilitySettings.help', { + help: i18n.translate('expressionXY.layeredXyVis.tickLabelsVisibilitySettings.help', { defaultMessage: 'Show x and y axes tick labels', }), }, labelsOrientation: { types: [LABELS_ORIENTATION_CONFIG], - help: i18n.translate('expressionXY.xyVis.labelsOrientation.help', { + help: i18n.translate('expressionXY.layeredXyVis.labelsOrientation.help', { defaultMessage: 'Defines the rotation of the axis labels', }), }, gridlinesVisibilitySettings: { types: [GRID_LINES_CONFIG], - help: i18n.translate('expressionXY.xyVis.gridlinesVisibilitySettings.help', { + help: i18n.translate('expressionXY.layeredXyVis.gridlinesVisibilitySettings.help', { defaultMessage: 'Show x and y axes gridlines', }), }, axisTitlesVisibilitySettings: { types: [AXIS_TITLES_VISIBILITY_CONFIG], - help: i18n.translate('expressionXY.xyVis.axisTitlesVisibilitySettings.help', { + help: i18n.translate('expressionXY.layeredXyVis.axisTitlesVisibilitySettings.help', { defaultMessage: 'Show x and y axes titles', }), }, @@ -145,33 +137,33 @@ export const layeredXyVisFunction: ExpressionFunctionDefinition< curveType: { types: ['string'], options: [...Object.values(XYCurveTypes)], - help: i18n.translate('expressionXY.xyVis.curveType.help', { + help: i18n.translate('expressionXY.layeredXyVis.curveType.help', { defaultMessage: 'Define how curve type is rendered for a line chart', }), }, fillOpacity: { types: ['number'], - help: i18n.translate('expressionXY.xyVis.fillOpacity.help', { + help: i18n.translate('expressionXY.layeredXyVis.fillOpacity.help', { defaultMessage: 'Define the area chart fill opacity', }), }, hideEndzones: { types: ['boolean'], default: false, - help: i18n.translate('expressionXY.xyVis.hideEndzones.help', { + help: i18n.translate('expressionXY.layeredXyVis.hideEndzones.help', { defaultMessage: 'Hide endzone markers for partial data', }), }, valuesInLegend: { types: ['boolean'], default: false, - help: i18n.translate('expressionXY.xyVis.valuesInLegend.help', { + help: i18n.translate('expressionXY.layeredXyVis.valuesInLegend.help', { defaultMessage: 'Show values in legend', }), }, ariaLabel: { types: ['string'], - help: i18n.translate('expressionXY.xyVis.ariaLabel.help', { + help: i18n.translate('expressionXY.layeredXyVis.ariaLabel.help', { defaultMessage: 'Specifies the aria label of the xy chart', }), required: false, diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts index ffc7674d00bff..f8fc5e9d37272 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts @@ -43,14 +43,6 @@ export const xyVisFunction: ExpressionFunctionDefinition< defaultMessage: 'An X/Y chart', }), args: { - title: { - types: ['string'], - help: 'The chart title.', - }, - description: { - types: ['string'], - help: '', - }, xTitle: { types: ['string'], help: i18n.translate('expressionXY.xyVis.xTitle.help', { diff --git a/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts b/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts index cf73edb25a6b2..199720be115ac 100644 --- a/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts +++ b/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts @@ -174,8 +174,6 @@ export interface LabelsOrientationConfig { // Arguments to XY chart expression, with computed properties export interface XYArgs { - title?: string; - description?: string; xTitle: string; yTitle: string; yRightTitle: string; @@ -201,8 +199,6 @@ export interface XYArgs { } export interface LayeredXYArgs { - title?: string; - description?: string; xTitle: string; yTitle: string; yRightTitle: string; @@ -226,8 +222,6 @@ export interface LayeredXYArgs { } export interface XYProps { - title?: string; - description?: string; xTitle: string; yTitle: string; yRightTitle: string; diff --git a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts index c5302ee587cb2..abc53c92fd854 100644 --- a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts +++ b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts @@ -76,7 +76,6 @@ export const toExpression = ( metadata, datasourceLayers, paletteService, - attributes, datasourceExpressionsByLayers, eventAnnotationService ); @@ -163,7 +162,6 @@ export const buildExpression = ( metadata: Record>, datasourceLayers: Record, paletteService: PaletteRegistry, - attributes: Partial<{ title: string; description: string }> = {}, datasourceExpressionsByLayers: Record, eventAnnotationService: EventAnnotationServiceType ): Ast | null => { @@ -205,8 +203,6 @@ export const buildExpression = ( type: 'function', function: 'layeredXyVis', arguments: { - title: [attributes?.title || ''], - description: [attributes?.description || ''], xTitle: [state.xTitle || ''], yTitle: [state.yTitle || ''], yRightTitle: [state.yRightTitle || ''], From bb91cf896cf92444e5c5ba5aafa782f9bc19362b Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Tue, 5 Apr 2022 19:16:52 +0300 Subject: [PATCH 085/133] Fixed missing required args error, and added required to arguments. --- .../common/expression_functions/data_layer.ts | 1 + .../extended_data_layer.ts | 1 + .../common/expression_functions/xy_vis.ts | 3 ++ .../public/components/xy_chart.tsx | 31 ++++++++++--------- .../public/helpers/axes_configuration.ts | 2 +- .../expressions/common/execution/execution.ts | 2 +- 6 files changed, 24 insertions(+), 16 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer.ts index b78592aaa2821..c6d9d57c9b1e4 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer.ts @@ -51,6 +51,7 @@ export const dataLayerFunction: ExpressionFunctionDefinition< help: i18n.translate('expressionXY.dataLayer.seriesType.help', { defaultMessage: 'The type of chart to display.', }), + required: true, }, xScaleType: { options: [...Object.values(XScaleTypes)], diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_data_layer.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_data_layer.ts index 7df82d9d3627a..a81a50ad97639 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_data_layer.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_data_layer.ts @@ -51,6 +51,7 @@ export const extendedDataLayerFunction: ExpressionFunctionDefinition< help: i18n.translate('expressionXY.extendedDataLayer.seriesType.help', { defaultMessage: 'The type of chart to display.', }), + required: true, }, xScaleType: { options: [...Object.values(XScaleTypes)], diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts index f8fc5e9d37272..643aa5320017d 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts @@ -66,18 +66,21 @@ export const xyVisFunction: ExpressionFunctionDefinition< help: i18n.translate('expressionXY.xyVis.yLeftExtent.help', { defaultMessage: 'Y left axis extents', }), + required: true, }, yRightExtent: { types: [AXIS_EXTENT_CONFIG], help: i18n.translate('expressionXY.xyVis.yRightExtent.help', { defaultMessage: 'Y right axis extents', }), + required: true, }, legend: { types: [LEGEND_CONFIG], help: i18n.translate('expressionXY.xyVis.legend.help', { defaultMessage: 'Configure the chart legend.', }), + required: true, }, fittingFunction: { types: ['string'], diff --git a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx index 75cb2e02e449c..9f0afcab3a414 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx @@ -80,6 +80,7 @@ import { CommonXYDataLayerConfigResult, CommonXYLayerConfigResult } from '../../ import './xy_chart.scss'; import { Annotations, getAnnotationsGroupedByInterval } from './annotations'; +import { SeriesTypes } from '../../common/constants'; declare global { interface Window { @@ -182,7 +183,9 @@ export function XYChart({ }); if (filteredLayers.length === 0) { - const icon: IconType = getIconForSeriesType(getDataLayers(layers)?.[0]?.seriesType || 'bar'); + const icon: IconType = getIconForSeriesType( + getDataLayers(layers)?.[0]?.seriesType || SeriesTypes.BAR + ); return ; } @@ -250,7 +253,7 @@ export function XYChart({ right: yAxesConfiguration.find(({ groupId }) => groupId === 'right'), }; - const getYAxesTitles = (axisSeries: Series[], groupId: string) => { + const getYAxesTitles = (axisSeries: Series[], groupId: 'right' | 'left') => { const yTitle = groupId === 'right' ? args.yRightTitle : args.yTitle; return ( yTitle || @@ -826,7 +829,7 @@ export function XYChart({ ) { return splitFormatter.convert(key); } - return splitAccessor && i === 0 ? key : columnToLabelMap[key] ?? ''; + return splitAccessor && i === 0 ? key : columnToLabelMap[key] ?? null; }) .join(' - '); return result; @@ -843,7 +846,7 @@ export function XYChart({ // This handles both split and single-y cases: // * If split series without formatting, show the value literally // * If single Y, the seriesKey will be the accessor, so we show the human-readable name - return splitAccessor ? d.seriesKeys[0] : columnToLabelMap[d.seriesKeys[0]] ?? ''; + return splitAccessor ? d.seriesKeys[0] : columnToLabelMap[d.seriesKeys[0]] ?? null; }, }; @@ -852,7 +855,7 @@ export function XYChart({ const curveType = args.curveType ? CurveType[args.curveType] : undefined; switch (seriesType) { - case 'line': + case SeriesTypes.LINE: return ( ); - case 'bar': - case 'bar_stacked': - case 'bar_percentage_stacked': - case 'bar_horizontal': - case 'bar_horizontal_stacked': - case 'bar_horizontal_percentage_stacked': + case SeriesTypes.BAR: + case SeriesTypes.BAR_STACKED: + case SeriesTypes.BAR_PERCENTAGE_STACKED: + case SeriesTypes.BAR_HORIZONTAL: + case SeriesTypes.BAR_HORIZONTAL_STACKED: + case SeriesTypes.BAR_HORIZONTAL_PERCENTAGE_STACKED: const valueLabelsSettings = { displayValueSettings: { // This format double fixes two issues in elastic-chart @@ -883,8 +886,8 @@ export function XYChart({ }, }; return ; - case 'area_stacked': - case 'area_percentage_stacked': + case SeriesTypes.AREA_STACKED: + case SeriesTypes.AREA_PERCENTAGE_STACKED: return ( ); - case 'area': + case SeriesTypes.AREA: return ( Date: Tue, 5 Apr 2022 20:13:46 +0300 Subject: [PATCH 086/133] Simplified expression configuration. --- .../expression_xy/common/expression_functions/xy_vis.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts index 643aa5320017d..ee282ea557dcb 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts @@ -66,21 +66,21 @@ export const xyVisFunction: ExpressionFunctionDefinition< help: i18n.translate('expressionXY.xyVis.yLeftExtent.help', { defaultMessage: 'Y left axis extents', }), - required: true, + default: `{${AXIS_EXTENT_CONFIG}}`, }, yRightExtent: { types: [AXIS_EXTENT_CONFIG], help: i18n.translate('expressionXY.xyVis.yRightExtent.help', { defaultMessage: 'Y right axis extents', }), - required: true, + default: `{${AXIS_EXTENT_CONFIG}}`, }, legend: { types: [LEGEND_CONFIG], help: i18n.translate('expressionXY.xyVis.legend.help', { defaultMessage: 'Configure the chart legend.', }), - required: true, + default: `{${LEGEND_CONFIG}}`, }, fittingFunction: { types: ['string'], From 7bb36b0a68e8ae97d0255e8721ddf03ed8ba8017 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Tue, 5 Apr 2022 20:17:44 +0300 Subject: [PATCH 087/133] Added strict to all the expressions. --- .../expression_xy/common/expression_functions/data_layer.ts | 3 +++ .../common/expression_functions/extended_data_layer.ts | 3 +++ .../common/expression_functions/layered_xy_vis.ts | 3 +++ .../common/expression_functions/legend_config.ts | 3 +++ .../expression_xy/common/expression_functions/xy_vis.ts | 3 +++ .../common/expression_functions/y_axis_config.ts | 4 ++++ 6 files changed, 19 insertions(+) diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer.ts index c6d9d57c9b1e4..dcef42eaef0b2 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer.ts @@ -52,6 +52,7 @@ export const dataLayerFunction: ExpressionFunctionDefinition< defaultMessage: 'The type of chart to display.', }), required: true, + strict: true, }, xScaleType: { options: [...Object.values(XScaleTypes)], @@ -59,6 +60,7 @@ export const dataLayerFunction: ExpressionFunctionDefinition< defaultMessage: 'The scale type of the x axis', }), default: XScaleTypes.ORDINAL, + strict: true, }, isHistogram: { types: ['boolean'], @@ -73,6 +75,7 @@ export const dataLayerFunction: ExpressionFunctionDefinition< defaultMessage: 'The scale type of the y axes', }), default: YScaleTypes.LINEAR, + strict: true, }, splitAccessor: { types: ['string'], diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_data_layer.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_data_layer.ts index a81a50ad97639..a6edc20db60dd 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_data_layer.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_data_layer.ts @@ -52,6 +52,7 @@ export const extendedDataLayerFunction: ExpressionFunctionDefinition< defaultMessage: 'The type of chart to display.', }), required: true, + strict: true, }, xScaleType: { options: [...Object.values(XScaleTypes)], @@ -59,6 +60,7 @@ export const extendedDataLayerFunction: ExpressionFunctionDefinition< defaultMessage: 'The scale type of the x axis', }), default: XScaleTypes.ORDINAL, + strict: true, }, isHistogram: { types: ['boolean'], @@ -73,6 +75,7 @@ export const extendedDataLayerFunction: ExpressionFunctionDefinition< defaultMessage: 'The scale type of the y axes', }), default: YScaleTypes.LINEAR, + strict: true, }, splitAccessor: { types: ['string'], diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts index b50f44cd55789..29bcd7e05cec6 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts @@ -83,6 +83,7 @@ export const layeredXyVisFunction: ExpressionFunctionDefinition< help: i18n.translate('expressionXY.layeredXyVis.fittingFunction.help', { defaultMessage: 'Define how missing values are treated', }), + strict: true, }, endValue: { types: ['string'], @@ -102,6 +103,7 @@ export const layeredXyVisFunction: ExpressionFunctionDefinition< help: i18n.translate('expressionXY.layeredXyVis.valueLabels.help', { defaultMessage: 'Value labels mode', }), + strict: true, }, tickLabelsVisibilitySettings: { types: [TICK_LABELS_CONFIG], @@ -140,6 +142,7 @@ export const layeredXyVisFunction: ExpressionFunctionDefinition< help: i18n.translate('expressionXY.layeredXyVis.curveType.help', { defaultMessage: 'Define how curve type is rendered for a line chart', }), + strict: true, }, fillOpacity: { types: ['number'], diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/legend_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/legend_config.ts index 384f23aee811a..ba65c8aee161f 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/legend_config.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/legend_config.ts @@ -38,6 +38,7 @@ export const legendConfigFunction: ExpressionFunctionDefinition< help: i18n.translate('expressionXY.legendConfig.position.help', { defaultMessage: 'Specifies the legend position.', }), + strict: true, }, showSingleSeries: { types: ['boolean'], @@ -58,6 +59,7 @@ export const legendConfigFunction: ExpressionFunctionDefinition< defaultMessage: 'Specifies the horizontal alignment of the legend when it is displayed inside chart.', }), + strict: true, }, verticalAlignment: { types: ['string'], @@ -66,6 +68,7 @@ export const legendConfigFunction: ExpressionFunctionDefinition< defaultMessage: 'Specifies the vertical alignment of the legend when it is displayed inside chart.', }), + strict: true, }, floatingColumns: { types: ['number'], diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts index ee282ea557dcb..a50d0973b763d 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts @@ -88,6 +88,7 @@ export const xyVisFunction: ExpressionFunctionDefinition< help: i18n.translate('expressionXY.xyVis.fittingFunction.help', { defaultMessage: 'Define how missing values are treated', }), + strict: true, }, endValue: { types: ['string'], @@ -95,6 +96,7 @@ export const xyVisFunction: ExpressionFunctionDefinition< help: i18n.translate('expressionXY.xyVis.endValue.help', { defaultMessage: 'End value', }), + strict: true, }, emphasizeFitting: { types: ['boolean'], @@ -107,6 +109,7 @@ export const xyVisFunction: ExpressionFunctionDefinition< help: i18n.translate('expressionXY.xyVis.valueLabels.help', { defaultMessage: 'Value labels mode', }), + strict: true, }, tickLabelsVisibilitySettings: { types: [TICK_LABELS_CONFIG], diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/y_axis_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/y_axis_config.ts index e665fc2b8cea0..d6432d1f373bd 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/y_axis_config.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/y_axis_config.ts @@ -37,6 +37,7 @@ export const yAxisConfigFunction: ExpressionFunctionDefinition< help: i18n.translate('expressionXY.yConfig.axisMode.help', { defaultMessage: 'The axis mode of the metric', }), + strict: true, }, color: { types: ['string'], @@ -50,6 +51,7 @@ export const yAxisConfigFunction: ExpressionFunctionDefinition< help: i18n.translate('expressionXY.yConfig.lineStyle.help', { defaultMessage: 'The style of the reference line', }), + strict: true, }, lineWidth: { types: ['number'], @@ -69,6 +71,7 @@ export const yAxisConfigFunction: ExpressionFunctionDefinition< help: i18n.translate('expressionXY.yConfig.iconPosition.help', { defaultMessage: 'The placement of the icon for the reference line', }), + strict: true, }, textVisibility: { types: ['boolean'], @@ -82,6 +85,7 @@ export const yAxisConfigFunction: ExpressionFunctionDefinition< help: i18n.translate('expressionXY.yConfig.fill.help', { defaultMessage: 'Fill', }), + strict: true, }, }, fn(input, args) { From 7979799fdd183cd7f10fa3ac8d83de9305097b41 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Tue, 5 Apr 2022 20:26:16 +0300 Subject: [PATCH 088/133] refactored code, according to the nit. --- .../expression_xy/public/components/xy_chart.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx index 9f0afcab3a414..5074213b4fd11 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx @@ -192,9 +192,7 @@ export function XYChart({ const dataLayers: CommonXYDataLayerConfigResult[] = filteredLayers.filter(isDataLayer); // use formatting hint of first x axis column to format ticks - const xAxisColumn = dataLayers[0]?.table.columns.find( - ({ id }) => isDataLayer(dataLayers[0]) && id === dataLayers[0].xAccessor - ); + const xAxisColumn = dataLayers[0]?.table.columns.find(({ id }) => id === dataLayers[0].xAccessor); const xAxisFormatter = formatFactory(xAxisColumn && xAxisColumn.meta?.params); const layersAlreadyFormatted: Record = {}; From 595ace3b2e6fa435a6c429056b15075974099221 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Wed, 6 Apr 2022 21:37:15 +0300 Subject: [PATCH 089/133] Moved dataLayer to the separate component. --- .../public/components/data_layers.tsx | 194 ++++++++++ .../public/components/legend_action.tsx | 15 +- .../public/components/x_domain.tsx | 4 +- .../public/components/xy_chart.tsx | 342 ++---------------- .../public/helpers/data_layers.tsx | 290 +++++++++++++++ .../expression_xy/public/helpers/index.ts | 1 + 6 files changed, 528 insertions(+), 318 deletions(-) create mode 100644 src/plugins/chart_expressions/expression_xy/public/components/data_layers.tsx create mode 100644 src/plugins/chart_expressions/expression_xy/public/helpers/data_layers.tsx diff --git a/src/plugins/chart_expressions/expression_xy/public/components/data_layers.tsx b/src/plugins/chart_expressions/expression_xy/public/components/data_layers.tsx new file mode 100644 index 0000000000000..94b19f31a83e3 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/public/components/data_layers.tsx @@ -0,0 +1,194 @@ +/* + * 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 { + AreaSeries, + BarSeries, + CurveType, + LabelOverflowConstraint, + LineSeries, +} from '@elastic/charts'; +import React, { FC } from 'react'; +import { i18n } from '@kbn/i18n'; +import { PaletteRegistry } from '../../../../charts/public'; +import { FormatFactory } from '../../../../field_formats/common'; +import { Datatable } from '../../../../expressions'; +import { + CommonXYDataLayerConfigResult, + EndValue, + FittingFunction, + ValueLabelMode, + XYCurveType, +} from '../../common'; +import { SeriesTypes } from '../../common/constants'; +import { + getColorAssignments, + getFitOptions, + getFormattedTable, + GroupsConfiguration, + getSeriesProps, +} from '../helpers'; + +interface Props { + layers: CommonXYDataLayerConfigResult[]; + formatFactory: FormatFactory; + chartHasMoreThanOneBarSeries?: boolean; + yAxesConfiguration: GroupsConfiguration; + curveType?: XYCurveType; + fittingFunction?: FittingFunction; + endValue?: EndValue | undefined; + paletteService: PaletteRegistry; + areLayersAlreadyFormatted: Record>; + syncColors?: boolean; + timeZone?: string; + emphasizeFitting?: boolean; + fillOpacity?: number; + shouldShowValueLabels?: boolean; + valueLabels: ValueLabelMode; +} + +export const DataLayers: FC = ({ + layers, + endValue, + timeZone, + curveType, + syncColors, + valueLabels, + fillOpacity, + formatFactory, + paletteService, + fittingFunction, + emphasizeFitting, + yAxesConfiguration, + shouldShowValueLabels, + areLayersAlreadyFormatted, + chartHasMoreThanOneBarSeries, +}) => { + const colorAssignments = getColorAssignments(layers, formatFactory); + return ( + <> + {layers.flatMap((layer, layerIndex) => + layer.accessors.map((accessor, accessorIndex) => { + const { splitAccessor, seriesType, xAccessor, table, columnToLabel, xScaleType } = layer; + const columnToLabelMap: Record = columnToLabel + ? JSON.parse(columnToLabel) + : {}; + + // what if row values are not primitive? That is the case of, for instance, Ranges + // remaps them to their serialized version with the formatHint metadata + // In order to do it we need to make a copy of the table as the raw one is required for more features (filters, etc...) later on + const formattedTable: Datatable = getFormattedTable( + table, + formatFactory, + xAccessor, + xScaleType + ); + + const isPercentage = seriesType.includes('percentage'); + + // For date histogram chart type, we're getting the rows that represent intervals without data. + // To not display them in the legend, they need to be filtered out. + const rows = formattedTable.rows.filter( + (row) => + !(xAccessor && typeof row[xAccessor] === 'undefined') && + !( + splitAccessor && + typeof row[splitAccessor] === 'undefined' && + typeof row[accessor] === 'undefined' + ) + ); + + if (!xAccessor) { + rows.forEach((row) => { + row.unifiedX = i18n.translate('expressionXY.xyChart.emptyXLabel', { + defaultMessage: '(empty)', + }); + }); + } + + const yAxis = yAxesConfiguration.find((axisConfiguration) => + axisConfiguration.series.find((currentSeries) => currentSeries.accessor === accessor) + ); + + const seriesProps = getSeriesProps({ + layer, + layerId: layerIndex, + accessor, + chartHasMoreThanOneBarSeries, + colorAssignments, + formatFactory, + columnToLabelMap, + paletteService, + alreadyFormattedColumns: areLayersAlreadyFormatted[layerIndex] ?? {}, + syncColors, + yAxis, + timeZone, + emphasizeFitting, + fillOpacity, + }); + + const index = `${layerIndex}-${accessorIndex}`; + + const curve = curveType ? CurveType[curveType] : undefined; + + switch (seriesType) { + case SeriesTypes.LINE: + return ( + + ); + case SeriesTypes.BAR: + case SeriesTypes.BAR_STACKED: + case SeriesTypes.BAR_PERCENTAGE_STACKED: + case SeriesTypes.BAR_HORIZONTAL: + case SeriesTypes.BAR_HORIZONTAL_STACKED: + case SeriesTypes.BAR_HORIZONTAL_PERCENTAGE_STACKED: + const valueLabelsSettings = { + displayValueSettings: { + // This format double fixes two issues in elastic-chart + // * when rotating the chart, the formatter is not correctly picked + // * in some scenarios value labels are not strings, and this breaks the elastic-chart lib + valueFormatter: (d: unknown) => yAxis?.formatter?.convert(d) || '', + showValueLabel: shouldShowValueLabels && valueLabels !== 'hide', + isValueContainedInElement: false, + isAlternatingValueLabel: false, + overflowConstraints: [ + LabelOverflowConstraint.ChartEdges, + LabelOverflowConstraint.BarGeometry, + ], + }, + }; + return ; + case SeriesTypes.AREA_STACKED: + case SeriesTypes.AREA_PERCENTAGE_STACKED: + return ( + + ); + case SeriesTypes.AREA: + return ( + + ); + } + }) + )} + + ); +}; diff --git a/src/plugins/chart_expressions/expression_xy/public/components/legend_action.tsx b/src/plugins/chart_expressions/expression_xy/public/components/legend_action.tsx index e0467eac25c1f..53e3708323702 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/legend_action.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/legend_action.tsx @@ -14,17 +14,22 @@ import type { FormatFactory } from '../types'; import { LegendActionPopover } from './legend_action_popover'; export const getLegendAction = ( - filteredLayers: CommonXYDataLayerConfigResult[], + dataLayers: CommonXYDataLayerConfigResult[], onFilter: (data: FilterEvent['data']) => void, formatFactory: FormatFactory, - layersAlreadyFormatted: Record + layersAlreadyFormatted: Record> ): LegendAction => React.memo(({ series: [xySeries] }) => { const series = xySeries as XYChartSeriesIdentifier; - const layer = filteredLayers.find((l) => + const layerIndex = dataLayers.findIndex((l) => series.seriesKeys.some((key: string | number) => l.accessors.includes(key.toString())) ); + if (layerIndex === -1) { + return null; + } + + const layer = dataLayers[layerIndex]; if (!layer || !layer.splitAccessor) { return null; } @@ -37,7 +42,7 @@ export const getLegendAction = ( const formatter = formatFactory(splitColumn && splitColumn.meta?.params); const rowIndex = table.rows.findIndex((row) => { - if (layersAlreadyFormatted[accessor]) { + if (layersAlreadyFormatted[layerIndex]?.[accessor]) { // stringify the value to compare with the chart value return formatter.convert(row[accessor]) === splitLabel; } @@ -62,7 +67,7 @@ export const getLegendAction = ( return ( - table.rows.map((row) => row[xAccessor!].valueOf() as number) + .flatMap(({ table, xAccessor }) => + table.rows.map((row) => row[xAccessor!].valueOf()) ) .sort() ); diff --git a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx index 5074213b4fd11..8c5533981cc66 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx @@ -11,52 +11,37 @@ import { Chart, Settings, Axis, - LineSeries, - AreaSeries, - BarSeries, Position, GeometryValue, XYChartSeriesIdentifier, - StackMode, VerticalAlignment, HorizontalAlignment, LayoutDirection, ElementClickListener, BrushEndListener, XYBrushEvent, - CurveType, LegendPositionConfig, - LabelOverflowConstraint, DisplayValueStyle, RecursivePartial, AxisStyle, - ScaleType, - AreaSeriesProps, - BarSeriesProps, - LineSeriesProps, - ColorVariant, } from '@elastic/charts'; import { IconType } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import type { Datatable, DatatableRow, DatatableColumn } from '../../../../expressions/public'; import { RenderMode } from '../../../../expressions/common'; -import { FieldFormat } from '../../../../field_formats/common'; import { EmptyPlaceholder } from '../../../../../plugins/charts/public'; import type { FilterEvent, BrushEvent, FormatFactory } from '../types'; import type { SeriesType, XYChartProps } from '../../common/types'; import { isHorizontalChart, - getSeriesColor, getAnnotationsLayers, getDataLayers, Series, + getAreAlreadyFormattedLayersInfo, } from '../helpers'; import { EventAnnotationServiceType } from '../../../../event_annotation/public'; import { ChartsPluginSetup, ChartsPluginStart, PaletteRegistry, - SeriesLayer, useActiveCursor, } from '../../../../../plugins/charts/public'; import { MULTILAYER_TIME_AXIS_STYLE } from '../../../../../plugins/charts/common'; @@ -64,12 +49,10 @@ import { getFilteredLayers, getReferenceLayers, isDataLayer, - getFitOptions, getAxesConfiguration, GroupsConfiguration, validateExtent, computeOverallDataDomain, - getColorAssignments, getLinesCausedPaddings, } from '../helpers'; import { getXDomain, XyEndzones } from './x_domain'; @@ -81,6 +64,7 @@ import { CommonXYDataLayerConfigResult, CommonXYLayerConfigResult } from '../../ import './xy_chart.scss'; import { Annotations, getAnnotationsGroupedByInterval } from './annotations'; import { SeriesTypes } from '../../common/constants'; +import { DataLayers } from './data_layers'; declare global { interface Window { @@ -91,8 +75,6 @@ declare global { } } -type SeriesSpec = LineSeriesProps & BarSeriesProps & AreaSeriesProps; - export type XYChartRenderProps = XYChartProps & { chartsThemeService: ChartsPluginSetup['theme']; chartsActiveCursorService: ChartsPluginStart['activeCursor']; @@ -109,8 +91,6 @@ export type XYChartRenderProps = XYChartProps & { eventAnnotationService: EventAnnotationServiceType; }; -const isPrimitive = (value: unknown): boolean => value != null && typeof value !== 'object'; - function getValueLabelsStyling(isHorizontal: boolean): { displayValue: RecursivePartial; } { @@ -195,11 +175,11 @@ export function XYChart({ const xAxisColumn = dataLayers[0]?.table.columns.find(({ id }) => id === dataLayers[0].xAccessor); const xAxisFormatter = formatFactory(xAxisColumn && xAxisColumn.meta?.params); - const layersAlreadyFormatted: Record = {}; + const areLayersAlreadyFormatted = getAreAlreadyFormattedLayersInfo(dataLayers, formatFactory); // This is a safe formatter for the xAccessor that abstracts the knowledge of already formatted layers const safeXAccessorLabelRenderer = (value: unknown): string => - xAxisColumn && layersAlreadyFormatted[xAxisColumn.id] + xAxisColumn && areLayersAlreadyFormatted[0]?.[xAxisColumn.id] ? String(value) : String(xAxisFormatter.convert(value)); @@ -372,11 +352,7 @@ export function XYChart({ } } - return { - fit, - min, - max, - }; + return { fit, min, max }; }; const shouldShowValueLabels = @@ -388,31 +364,31 @@ export function XYChart({ const valueLabelsStyling = shouldShowValueLabels && valueLabels !== 'hide' && getValueLabelsStyling(shouldRotate); - const colorAssignments = getColorAssignments(getDataLayers(args.layers), formatFactory); - const clickHandler: ElementClickListener = ([[geometry, series]]) => { // for xyChart series is always XYChartSeriesIdentifier and geometry is always type of GeometryValue const xySeries = series as XYChartSeriesIdentifier; const xyGeometry = geometry as GeometryValue; - const layer = dataLayers.find((l) => + const layerIndex = dataLayers.findIndex((l) => xySeries.seriesKeys.some((key: string | number) => l.accessors.includes(key.toString())) ); - if (!layer) { + + if (layerIndex === -1) { return; } + const layer = dataLayers[layerIndex]; const { table } = layer; const xColumn = table.columns.find((col) => col.id === layer.xAccessor); const currentXFormatter = - layer.xAccessor && layersAlreadyFormatted[layer.xAccessor] && xColumn + layer.xAccessor && areLayersAlreadyFormatted[layerIndex]?.[layer.xAccessor] && xColumn ? formatFactory(xColumn.meta.params) : xAxisFormatter; const rowIndex = table.rows.findIndex((row) => { if (layer.xAccessor) { - if (layersAlreadyFormatted[layer.xAccessor]) { + if (areLayersAlreadyFormatted[layerIndex]?.[layer.xAccessor]) { // stringify the value to compare with the chart value return currentXFormatter.convert(row[layer.xAccessor]) === xyGeometry.x; } @@ -437,7 +413,7 @@ export function XYChart({ points.push({ row: table.rows.findIndex((row) => { if (layer.splitAccessor) { - if (layersAlreadyFormatted[layer.splitAccessor]) { + if (areLayersAlreadyFormatted[layerIndex]?.[layer.splitAccessor]) { return splitFormatter.convert(row[layer.splitAccessor]) === pointValue; } return row[layer.splitAccessor] === pointValue; @@ -531,7 +507,6 @@ export function XYChart({ : undefined, }, }; - return ( )} - {dataLayers.flatMap((layer, layerIndex) => - layer.accessors.map((accessor, accessorIndex) => { - const { - splitAccessor, - seriesType, - accessors, - xAccessor, - table, - columnToLabel, - yScaleType, - xScaleType, - isHistogram, - palette, - } = layer; - const columnToLabelMap: Record = columnToLabel - ? JSON.parse(columnToLabel) - : {}; - - const formatterPerColumn = new Map(); - for (const column of table.columns) { - formatterPerColumn.set(column, formatFactory(column.meta.params)); - } - - // what if row values are not primitive? That is the case of, for instance, Ranges - // remaps them to their serialized version with the formatHint metadata - // In order to do it we need to make a copy of the table as the raw one is required for more features (filters, etc...) later on - const tableConverted: Datatable = { - ...table, - rows: table.rows.map((row: DatatableRow) => { - const newRow = { ...row }; - for (const column of table.columns) { - const record = newRow[column.id]; - if ( - record != null && - // pre-format values for ordinal x axes because there can only be a single x axis formatter on chart level - (!isPrimitive(record) || (column.id === xAccessor && xScaleType === 'ordinal')) - ) { - newRow[column.id] = formatterPerColumn.get(column)!.convert(record); - } - } - return newRow; - }), - }; - - // save the id of the layer with the custom table - table.columns.reduce>( - (alreadyFormatted: Record, { id }) => { - if (alreadyFormatted[id]) { - return alreadyFormatted; - } - alreadyFormatted[id] = table.rows.some( - (row, i) => row[id] !== tableConverted.rows[i][id] - ); - return alreadyFormatted; - }, - layersAlreadyFormatted - ); - - const isStacked = seriesType.includes('stacked'); - const isPercentage = seriesType.includes('percentage'); - const isBarChart = seriesType.includes('bar'); - const enableHistogramMode = - isHistogram && - (isStacked || !splitAccessor) && - (isStacked || !isBarChart || !chartHasMoreThanOneBarSeries); - - // For date histogram chart type, we're getting the rows that represent intervals without data. - // To not display them in the legend, they need to be filtered out. - const rows = tableConverted.rows.filter( - (row) => - !(xAccessor && typeof row[xAccessor] === 'undefined') && - !( - splitAccessor && - typeof row[splitAccessor] === 'undefined' && - typeof row[accessor] === 'undefined' - ) - ); - - if (!xAccessor) { - rows.forEach((row) => { - row.unifiedX = i18n.translate('expressionXY.xyChart.emptyXLabel', { - defaultMessage: '(empty)', - }); - }); - } - - const yAxis = yAxesConfiguration.find((axisConfiguration) => - axisConfiguration.series.find((currentSeries) => currentSeries.accessor === accessor) - ); - - const formatter = table?.columns.find((column) => column.id === accessor)?.meta?.params; - const splitHint = table.columns.find((col) => col.id === splitAccessor)?.meta?.params; - const splitFormatter = formatFactory(splitHint); - const seriesProps: SeriesSpec = { - splitSeriesAccessors: splitAccessor ? [splitAccessor] : [], - stackAccessors: isStacked ? [xAccessor as string] : [], - id: `${splitAccessor}-${accessor}`, - xAccessor: xAccessor || 'unifiedX', - yAccessors: [accessor], - data: rows, - xScaleType: xAccessor ? xScaleType : 'ordinal', - yScaleType: - formatter?.id === 'bytes' && yScaleType === ScaleType.Linear - ? ScaleType.LinearBinary - : yScaleType, - color: ({ yAccessor, seriesKeys }) => { - const overwriteColor = getSeriesColor(layer, accessor); - if (overwriteColor !== null) { - return overwriteColor; - } - const colorAssignment = colorAssignments[palette.name]; - const seriesLayers: SeriesLayer[] = [ - { - name: splitAccessor ? String(seriesKeys[0]) : columnToLabelMap[seriesKeys[0]], - totalSeriesAtDepth: colorAssignment.totalSeriesCount, - rankAtDepth: colorAssignment.getRank( - layer, - layerIndex, - String(seriesKeys[0]), - String(yAccessor) - ), - }, - ]; - return paletteService.get(palette.name).getCategoricalColor( - seriesLayers, - { - maxDepth: 1, - behindText: false, - totalSeries: colorAssignment.totalSeriesCount, - syncColors, - }, - palette.params - ); - }, - groupId: yAxis?.groupId, - enableHistogramMode, - stackMode: isPercentage ? StackMode.Percentage : undefined, - timeZone, - areaSeriesStyle: { - point: { - visible: !xAccessor, - radius: xAccessor && !emphasizeFitting ? 5 : 0, - }, - ...(args.fillOpacity && { area: { opacity: args.fillOpacity } }), - ...(emphasizeFitting && { - fit: { - area: { - opacity: args.fillOpacity || 0.5, - }, - line: { - visible: true, - stroke: ColorVariant.Series, - opacity: 1, - dash: [], - }, - }, - }), - }, - lineSeriesStyle: { - point: { - visible: !xAccessor, - radius: xAccessor && !emphasizeFitting ? 5 : 0, - }, - ...(emphasizeFitting && { - fit: { - line: { - visible: true, - stroke: ColorVariant.Series, - opacity: 1, - dash: [], - }, - }, - }), - }, - name(d) { - // For multiple y series, the name of the operation is used on each, either: - // * Key - Y name - // * Formatted value - Y name - if (accessors.length > 1) { - const result = d.seriesKeys - .map((key: string | number, i) => { - if ( - i === 0 && - splitHint && - splitAccessor && - !layersAlreadyFormatted[splitAccessor] - ) { - return splitFormatter.convert(key); - } - return splitAccessor && i === 0 ? key : columnToLabelMap[key] ?? null; - }) - .join(' - '); - return result; - } - - // For formatted split series, format the key - // This handles splitting by dates, for example - if (splitHint) { - if (splitAccessor && layersAlreadyFormatted[splitAccessor]) { - return d.seriesKeys[0]; - } - return splitFormatter.convert(d.seriesKeys[0]); - } - // This handles both split and single-y cases: - // * If split series without formatting, show the value literally - // * If single Y, the seriesKey will be the accessor, so we show the human-readable name - return splitAccessor ? d.seriesKeys[0] : columnToLabelMap[d.seriesKeys[0]] ?? null; - }, - }; - - const index = `${layerIndex}-${accessorIndex}`; - - const curveType = args.curveType ? CurveType[args.curveType] : undefined; - - switch (seriesType) { - case SeriesTypes.LINE: - return ( - - ); - case SeriesTypes.BAR: - case SeriesTypes.BAR_STACKED: - case SeriesTypes.BAR_PERCENTAGE_STACKED: - case SeriesTypes.BAR_HORIZONTAL: - case SeriesTypes.BAR_HORIZONTAL_STACKED: - case SeriesTypes.BAR_HORIZONTAL_PERCENTAGE_STACKED: - const valueLabelsSettings = { - displayValueSettings: { - // This format double fixes two issues in elastic-chart - // * when rotating the chart, the formatter is not correctly picked - // * in some scenarios value labels are not strings, and this breaks the elastic-chart lib - valueFormatter: (d: unknown) => yAxis?.formatter?.convert(d) || '', - showValueLabel: shouldShowValueLabels && valueLabels !== 'hide', - isValueContainedInElement: false, - isAlternatingValueLabel: false, - overflowConstraints: [ - LabelOverflowConstraint.ChartEdges, - LabelOverflowConstraint.BarGeometry, - ], - }, - }; - return ; - case SeriesTypes.AREA_STACKED: - case SeriesTypes.AREA_PERCENTAGE_STACKED: - return ( - - ); - case SeriesTypes.AREA: - return ( - - ); - default: - return assertNever(seriesType); - } - }) + {dataLayers.length && ( + )} {referenceLineLayers.length ? ( ); } - -function assertNever(x: never): never { - throw new Error('Unexpected series type: ' + x); -} diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/data_layers.tsx b/src/plugins/chart_expressions/expression_xy/public/helpers/data_layers.tsx new file mode 100644 index 0000000000000..ab7aa9c0b77fe --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/data_layers.tsx @@ -0,0 +1,290 @@ +/* + * 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 { + AreaSeriesProps, + BarSeriesProps, + ColorVariant, + LineSeriesProps, + ScaleType, + SeriesName, + StackMode, + XYChartSeriesIdentifier, +} from '@elastic/charts'; +import { i18n } from '@kbn/i18n'; +import { + FieldFormat, + FieldFormatParams, + SerializedFieldFormat, +} from 'src/plugins/field_formats/common'; +import { Datatable, DatatableRow } from '../../../../expressions'; +import { CommonXYDataLayerConfigResult, XScaleType } from '../../common'; +import { FormatFactory } from '../types'; +import { PaletteRegistry, SeriesLayer } from '../../../../charts/public'; +import { getSeriesColor } from './state'; +import { ColorAssignments } from './color_assignment'; +import { GroupsConfiguration } from './axes_configuration'; + +type SeriesSpec = LineSeriesProps & BarSeriesProps & AreaSeriesProps; + +type GetSeriesPropsFn = (config: { + layer: CommonXYDataLayerConfigResult; + layerId: number; + accessor: string; + chartHasMoreThanOneBarSeries?: boolean; + formatFactory: FormatFactory; + colorAssignments: ColorAssignments; + columnToLabelMap: Record; + paletteService: PaletteRegistry; + alreadyFormattedColumns: Record; + syncColors?: boolean; + yAxis?: GroupsConfiguration[number]; + timeZone?: string; + emphasizeFitting?: boolean; + fillOpacity?: number; +}) => SeriesSpec; + +const isPrimitive = (value: unknown): boolean => value != null && typeof value !== 'object'; + +export const getFormattedTable = ( + table: Datatable, + formatFactory: FormatFactory, + xAccessor: string | undefined, + xScaleType: XScaleType +): Datatable => ({ + ...table, + rows: table.rows.map((row: DatatableRow) => { + const newRow = { ...row }; + for (const column of table.columns) { + const record = newRow[column.id]; + if ( + record != null && + // pre-format values for ordinal x axes because there can only be a single x axis formatter on chart level + (!isPrimitive(record) || (column.id === xAccessor && xScaleType === 'ordinal')) + ) { + newRow[column.id] = formatFactory(column.meta.params)!.convert(record); + } + } + return newRow; + }), +}); + +export const getIsAlreadyFormattedLayerInfo = ( + { table, xAccessor, xScaleType }: CommonXYDataLayerConfigResult, + formatFactory: FormatFactory +): Record => { + const formattedTable = getFormattedTable(table, formatFactory, xAccessor, xScaleType); + return table.columns.reduce>( + (alreadyFormatted: Record, { id }) => { + if (alreadyFormatted[id]) { + return alreadyFormatted; + } + return { + ...alreadyFormatted, + [id]: table.rows.some((row, i) => row[id] !== formattedTable.rows[i][id]), + }; + }, + {} + ); +}; + +export const getAreAlreadyFormattedLayersInfo = ( + layers: CommonXYDataLayerConfigResult[], + formatFactory: FormatFactory +): Record> => + layers.reduce>>( + (areAlreadyFormatted, layer, index) => ({ + ...areAlreadyFormatted, + [index]: getIsAlreadyFormattedLayerInfo(layer, formatFactory), + }), + {} + ); + +type GetSeriesNameFn = ( + data: XYChartSeriesIdentifier, + config: { + layer: CommonXYDataLayerConfigResult; + splitHint: SerializedFieldFormat | undefined; + splitFormatter: FieldFormat; + alreadyFormattedColumns: Record; + columnToLabelMap: Record; + } +) => SeriesName; + +const getSeriesName: GetSeriesNameFn = ( + data, + { layer, splitHint, splitFormatter, alreadyFormattedColumns, columnToLabelMap } +) => { + // For multiple y series, the name of the operation is used on each, either: + // * Key - Y name + // * Formatted value - Y name + if (layer.splitAccessor && layer.accessors.length > 1) { + const formatted = alreadyFormattedColumns[layer.splitAccessor]; + const result = data.seriesKeys + .map((key: string | number, i) => { + if (i === 0 && splitHint && layer.splitAccessor && !formatted) { + return splitFormatter.convert(key); + } + return layer.splitAccessor && i === 0 ? key : columnToLabelMap[key] ?? null; + }) + .join(' - '); + return result; + } + + // For formatted split series, format the key + // This handles splitting by dates, for example + if (splitHint) { + if (layer.splitAccessor && alreadyFormattedColumns[layer.splitAccessor]) { + return data.seriesKeys[0]; + } + return splitFormatter.convert(data.seriesKeys[0]); + } + // This handles both split and single-y cases: + // * If split series without formatting, show the value literally + // * If single Y, the seriesKey will be the accessor, so we show the human-readable name + return layer.splitAccessor ? data.seriesKeys[0] : columnToLabelMap[data.seriesKeys[0]] ?? null; +}; + +export const getSeriesProps: GetSeriesPropsFn = ({ + layer, + layerId, + accessor, + chartHasMoreThanOneBarSeries, + colorAssignments, + formatFactory, + columnToLabelMap, + paletteService, + alreadyFormattedColumns, + syncColors, + yAxis, + timeZone, + emphasizeFitting, + fillOpacity, +}): SeriesSpec => { + const { table } = layer; + const isStacked = layer.seriesType.includes('stacked'); + const isPercentage = layer.seriesType.includes('percentage'); + const isBarChart = layer.seriesType.includes('bar'); + const enableHistogramMode = + layer.isHistogram && + (isStacked || !layer.splitAccessor) && + (isStacked || !isBarChart || !chartHasMoreThanOneBarSeries); + + const formatter = table?.columns.find((column) => column.id === accessor)?.meta?.params; + const splitHint = table?.columns.find((col) => col.id === layer.splitAccessor)?.meta?.params; + const splitFormatter = formatFactory(splitHint); + + // what if row values are not primitive? That is the case of, for instance, Ranges + // remaps them to their serialized version with the formatHint metadata + // In order to do it we need to make a copy of the table as the raw one is required for more features (filters, etc...) later on + const formattedTable: Datatable = getFormattedTable( + table, + formatFactory, + layer.xAccessor, + layer.xScaleType + ); + + // For date histogram chart type, we're getting the rows that represent intervals without data. + // To not display them in the legend, they need to be filtered out. + const rows = formattedTable.rows.filter( + (row) => + !(layer.xAccessor && typeof row[layer.xAccessor] === 'undefined') && + !( + layer.splitAccessor && + typeof row[layer.splitAccessor] === 'undefined' && + typeof row[accessor] === 'undefined' + ) + ); + + if (!layer.xAccessor) { + rows.forEach((row) => { + row.unifiedX = i18n.translate('expressionXY.xyChart.emptyXLabel', { + defaultMessage: '(empty)', + }); + }); + } + return { + splitSeriesAccessors: layer.splitAccessor ? [layer.splitAccessor] : [], + stackAccessors: isStacked ? [layer.xAccessor as string] : [], + id: `${layer.splitAccessor}-${accessor}`, + xAccessor: layer.xAccessor || 'unifiedX', + yAccessors: [accessor], + data: rows, + xScaleType: layer.xAccessor ? layer.xScaleType : 'ordinal', + yScaleType: + formatter?.id === 'bytes' && layer.yScaleType === ScaleType.Linear + ? ScaleType.LinearBinary + : layer.yScaleType, + color: ({ yAccessor, seriesKeys }) => { + const overwriteColor = getSeriesColor(layer, accessor); + if (overwriteColor !== null) { + return overwriteColor; + } + const colorAssignment = colorAssignments[layer.palette.name]; + const seriesLayers: SeriesLayer[] = [ + { + name: layer.splitAccessor ? String(seriesKeys[0]) : columnToLabelMap[seriesKeys[0]], + totalSeriesAtDepth: colorAssignment.totalSeriesCount, + rankAtDepth: colorAssignment.getRank( + layer, + layerId, + String(seriesKeys[0]), + String(yAccessor) + ), + }, + ]; + return paletteService.get(layer.palette.name).getCategoricalColor( + seriesLayers, + { + maxDepth: 1, + behindText: false, + totalSeries: colorAssignment.totalSeriesCount, + syncColors, + }, + layer.palette.params + ); + }, + groupId: yAxis?.groupId, + enableHistogramMode, + stackMode: isPercentage ? StackMode.Percentage : undefined, + timeZone, + areaSeriesStyle: { + point: { + visible: !layer.xAccessor, + radius: layer.xAccessor && !emphasizeFitting ? 5 : 0, + }, + ...(fillOpacity && { area: { opacity: fillOpacity } }), + ...(emphasizeFitting && { + fit: { + area: { opacity: fillOpacity || 0.5 }, + line: { visible: true, stroke: ColorVariant.Series, opacity: 1, dash: [] }, + }, + }), + }, + lineSeriesStyle: { + point: { + visible: !layer.xAccessor, + radius: layer.xAccessor && !emphasizeFitting ? 5 : 0, + }, + ...(emphasizeFitting && { + fit: { + line: { visible: true, stroke: ColorVariant.Series, opacity: 1, dash: [] }, + }, + }), + }, + name(d) { + return getSeriesName(d, { + layer, + splitHint, + splitFormatter, + alreadyFormattedColumns, + columnToLabelMap, + }); + }, + }; +}; diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/index.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/index.ts index cb0300e47ae70..24304132500ec 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/index.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/index.ts @@ -17,3 +17,4 @@ export * from './icon'; export * from './color_assignment'; export * from './annotations_icon_set'; export * from './annotations'; +export * from './data_layers'; From 1baee1b4726bd0a6f56d4ab2d012c7861df145d3 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Thu, 7 Apr 2022 09:14:06 +0300 Subject: [PATCH 090/133] Fixed jest tests. --- .../__snapshots__/xy_chart.test.tsx.snap | 2697 ++++++++++++----- .../public/components/xy_chart.test.tsx | 212 +- 2 files changed, 2029 insertions(+), 880 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/public/components/__snapshots__/xy_chart.test.tsx.snap b/src/plugins/chart_expressions/expression_xy/public/components/__snapshots__/xy_chart.test.tsx.snap index 828a62c85cce3..69ebbab885ba5 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/__snapshots__/xy_chart.test.tsx.snap +++ b/src/plugins/chart_expressions/expression_xy/public/components/__snapshots__/xy_chart.test.tsx.snap @@ -322,127 +322,292 @@ exports[`XYChart component it renders area 1`] = ` darkMode={false} histogramMode={false} /> - - `; @@ -556,139 +721,292 @@ exports[`XYChart component it renders bar 1`] = ` darkMode={false} histogramMode={false} /> - - `; @@ -802,139 +1120,292 @@ exports[`XYChart component it renders horizontal bar 1`] = ` darkMode={false} histogramMode={false} /> - - `; @@ -1048,127 +1519,292 @@ exports[`XYChart component it renders line 1`] = ` darkMode={false} histogramMode={false} /> - - `; @@ -1282,135 +1918,292 @@ exports[`XYChart component it renders stacked area 1`] = ` darkMode={false} histogramMode={false} /> - - `; @@ -1524,147 +2317,292 @@ exports[`XYChart component it renders stacked bar 1`] = ` darkMode={false} histogramMode={false} /> - - `; @@ -1778,147 +2716,292 @@ exports[`XYChart component it renders stacked horizontal bar 1`] = ` darkMode={false} histogramMode={false} /> - - `; diff --git a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx index 7a99559160f06..9c42a4e6c270a 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx @@ -50,6 +50,7 @@ import { XYChart, XYChartRenderProps } from './xy_chart'; import { ExtendedDataLayerConfigResult, XYChartProps, XYProps } from '../../common/types'; import { eventAnnotationServiceMock } from '../../../../event_annotation/public/mocks'; import { EventAnnotationOutput } from '../../../../event_annotation/common'; +import { DataLayers } from './data_layers'; const onClickValue = jest.fn(); const onSelectRange = jest.fn(); @@ -125,9 +126,11 @@ describe('XYChart component', () => { /> ); expect(component).toMatchSnapshot(); - expect(component.find(LineSeries)).toHaveLength(2); - expect(component.find(LineSeries).at(0).prop('yAccessors')).toEqual(['a']); - expect(component.find(LineSeries).at(1).prop('yAccessors')).toEqual(['b']); + + const lineSeries = component.find(DataLayers).dive().find(LineSeries); + expect(lineSeries).toHaveLength(2); + expect(lineSeries.at(0).prop('yAccessors')).toEqual(['a']); + expect(lineSeries.at(1).prop('yAccessors')).toEqual(['b']); }); describe('date range', () => { @@ -712,9 +715,11 @@ describe('XYChart component', () => { /> ); expect(component).toMatchSnapshot(); - expect(component.find(BarSeries)).toHaveLength(2); - expect(component.find(BarSeries).at(0).prop('yAccessors')).toEqual(['a']); - expect(component.find(BarSeries).at(1).prop('yAccessors')).toEqual(['b']); + + const barSeries = component.find(DataLayers).dive().find(BarSeries); + expect(barSeries).toHaveLength(2); + expect(barSeries.at(0).prop('yAccessors')).toEqual(['a']); + expect(barSeries.at(1).prop('yAccessors')).toEqual(['b']); }); test('it renders area', () => { @@ -729,9 +734,11 @@ describe('XYChart component', () => { /> ); expect(component).toMatchSnapshot(); - expect(component.find(AreaSeries)).toHaveLength(2); - expect(component.find(AreaSeries).at(0).prop('yAccessors')).toEqual(['a']); - expect(component.find(AreaSeries).at(1).prop('yAccessors')).toEqual(['b']); + + const areaSeries = component.find(DataLayers).dive().find(AreaSeries); + expect(areaSeries).toHaveLength(2); + expect(areaSeries.at(0).prop('yAccessors')).toEqual(['a']); + expect(areaSeries.at(1).prop('yAccessors')).toEqual(['b']); }); test('it renders horizontal bar', () => { @@ -746,9 +753,11 @@ describe('XYChart component', () => { /> ); expect(component).toMatchSnapshot(); - expect(component.find(BarSeries)).toHaveLength(2); - expect(component.find(BarSeries).at(0).prop('yAccessors')).toEqual(['a']); - expect(component.find(BarSeries).at(1).prop('yAccessors')).toEqual(['b']); + + const barSeries = component.find(DataLayers).dive().find(BarSeries); + expect(barSeries).toHaveLength(2); + expect(barSeries.at(0).prop('yAccessors')).toEqual(['a']); + expect(barSeries.at(1).prop('yAccessors')).toEqual(['b']); expect(component.find(Settings).prop('rotation')).toEqual(90); }); @@ -1235,9 +1244,11 @@ describe('XYChart component', () => { /> ); expect(component).toMatchSnapshot(); - expect(component.find(BarSeries)).toHaveLength(2); - expect(component.find(BarSeries).at(0).prop('stackAccessors')).toHaveLength(1); - expect(component.find(BarSeries).at(1).prop('stackAccessors')).toHaveLength(1); + + const barSeries = component.find(DataLayers).dive().find(BarSeries); + expect(barSeries).toHaveLength(2); + expect(barSeries.at(0).prop('stackAccessors')).toHaveLength(1); + expect(barSeries.at(1).prop('stackAccessors')).toHaveLength(1); }); test('it renders stacked area', () => { @@ -1252,9 +1263,11 @@ describe('XYChart component', () => { /> ); expect(component).toMatchSnapshot(); - expect(component.find(AreaSeries)).toHaveLength(2); - expect(component.find(AreaSeries).at(0).prop('stackAccessors')).toHaveLength(1); - expect(component.find(AreaSeries).at(1).prop('stackAccessors')).toHaveLength(1); + + const areaSeries = component.find(DataLayers).dive().find(AreaSeries); + expect(areaSeries).toHaveLength(2); + expect(areaSeries.at(0).prop('stackAccessors')).toHaveLength(1); + expect(areaSeries.at(1).prop('stackAccessors')).toHaveLength(1); }); test('it renders stacked horizontal bar', () => { @@ -1271,9 +1284,11 @@ describe('XYChart component', () => { /> ); expect(component).toMatchSnapshot(); - expect(component.find(BarSeries)).toHaveLength(2); - expect(component.find(BarSeries).at(0).prop('stackAccessors')).toHaveLength(1); - expect(component.find(BarSeries).at(1).prop('stackAccessors')).toHaveLength(1); + + const barSeries = component.find(DataLayers).dive().find(BarSeries); + expect(barSeries).toHaveLength(2); + expect(barSeries.at(0).prop('stackAccessors')).toHaveLength(1); + expect(barSeries.at(1).prop('stackAccessors')).toHaveLength(1); expect(component.find(Settings).prop('rotation')).toEqual(90); }); @@ -1297,15 +1312,17 @@ describe('XYChart component', () => { /> ); - expect(component.find(BarSeries)).toHaveLength(0); + expect(component.find(DataLayers)).toHaveLength(0); expect(component.find(EmptyPlaceholder).prop('icon')).toBeDefined(); }); test('it passes time zone to the series', () => { const { args } = sampleArgs(); const component = shallow(); - expect(component.find(LineSeries).at(0).prop('timeZone')).toEqual('CEST'); - expect(component.find(LineSeries).at(1).prop('timeZone')).toEqual('CEST'); + + const lineSeries = component.find(DataLayers).dive().find(LineSeries); + expect(lineSeries.at(0).prop('timeZone')).toEqual('CEST'); + expect(lineSeries.at(1).prop('timeZone')).toEqual('CEST'); }); test('it applies histogram mode to the series for single series', () => { @@ -1320,7 +1337,9 @@ describe('XYChart component', () => { const component = shallow( ); - expect(component.find(BarSeries).at(0).prop('enableHistogramMode')).toEqual(true); + expect( + component.find(DataLayers).dive().find(BarSeries).at(0).prop('enableHistogramMode') + ).toEqual(true); }); test('it does not apply histogram mode to more than one bar series for unstacked bar chart', () => { @@ -1334,8 +1353,10 @@ describe('XYChart component', () => { const component = shallow( ); - expect(component.find(BarSeries).at(0).prop('enableHistogramMode')).toEqual(false); - expect(component.find(BarSeries).at(1).prop('enableHistogramMode')).toEqual(false); + + const barSeries = component.find(DataLayers).dive().find(BarSeries); + expect(barSeries.at(0).prop('enableHistogramMode')).toEqual(false); + expect(barSeries.at(1).prop('enableHistogramMode')).toEqual(false); }); test('it applies histogram mode to more than one the series for unstacked line/area chart', () => { @@ -1355,8 +1376,10 @@ describe('XYChart component', () => { const component = shallow( ); - expect(component.find(LineSeries).at(0).prop('enableHistogramMode')).toEqual(true); - expect(component.find(LineSeries).at(1).prop('enableHistogramMode')).toEqual(true); + + const lineSeries = component.find(DataLayers).dive().find(LineSeries); + expect(lineSeries.at(0).prop('enableHistogramMode')).toEqual(true); + expect(lineSeries.at(1).prop('enableHistogramMode')).toEqual(true); }); test('it applies histogram mode to the series for stacked series', () => { @@ -1376,8 +1399,10 @@ describe('XYChart component', () => { }} /> ); - expect(component.find(BarSeries).at(0).prop('enableHistogramMode')).toEqual(true); - expect(component.find(BarSeries).at(1).prop('enableHistogramMode')).toEqual(true); + + const barSeries = component.find(DataLayers).dive().find(BarSeries); + expect(barSeries.at(0).prop('enableHistogramMode')).toEqual(true); + expect(barSeries.at(1).prop('enableHistogramMode')).toEqual(true); }); test('it does not apply histogram mode for splitted series', () => { @@ -1393,8 +1418,10 @@ describe('XYChart component', () => { }} /> ); - expect(component.find(BarSeries).at(0).prop('enableHistogramMode')).toEqual(false); - expect(component.find(BarSeries).at(1).prop('enableHistogramMode')).toEqual(false); + + const barSeries = component.find(DataLayers).dive().find(BarSeries); + expect(barSeries.at(0).prop('enableHistogramMode')).toEqual(false); + expect(barSeries.at(1).prop('enableHistogramMode')).toEqual(false); }); describe('y axes', () => { @@ -1441,8 +1468,10 @@ describe('XYChart component', () => { const component = getRenderedComponent(newArgs); const axes = component.find(Axis); expect(axes).toHaveLength(3); - expect(component.find(LineSeries).at(0).prop('groupId')).toEqual(axes.at(1).prop('groupId')); - expect(component.find(LineSeries).at(1).prop('groupId')).toEqual(axes.at(2).prop('groupId')); + + const lineSeries = component.find(DataLayers).dive().find(LineSeries); + expect(lineSeries.at(0).prop('groupId')).toEqual(axes.at(1).prop('groupId')); + expect(lineSeries.at(1).prop('groupId')).toEqual(axes.at(2).prop('groupId')); }); test('multiple axes because of incompatible formatters', () => { @@ -1460,8 +1489,10 @@ describe('XYChart component', () => { const component = getRenderedComponent(newArgs); const axes = component.find(Axis); expect(axes).toHaveLength(3); - expect(component.find(LineSeries).at(0).prop('groupId')).toEqual(axes.at(1).prop('groupId')); - expect(component.find(LineSeries).at(1).prop('groupId')).toEqual(axes.at(2).prop('groupId')); + + const lineSeries = component.find(DataLayers).dive().find(LineSeries); + expect(lineSeries.at(0).prop('groupId')).toEqual(axes.at(1).prop('groupId')); + expect(lineSeries.at(1).prop('groupId')).toEqual(axes.at(2).prop('groupId')); }); test('single axis despite different formatters if enforced', () => { @@ -1539,20 +1570,21 @@ describe('XYChart component', () => { }; const component = getRenderedComponent(newArgs); + const lineSeries = component.find(DataLayers).dive().find(LineSeries); expect( - (component.find(LineSeries).at(0).prop('color') as Function)!({ + (lineSeries.at(0).prop('color') as Function)!({ yAccessor: 'a', seriesKeys: ['a'], }) ).toEqual('#550000'); expect( - (component.find(LineSeries).at(1).prop('color') as Function)!({ + (lineSeries.at(1).prop('color') as Function)!({ yAccessor: 'b', seriesKeys: ['b'], }) ).toEqual('#FFFF00'); expect( - (component.find(LineSeries).at(2).prop('color') as Function)!({ + (lineSeries.at(2).prop('color') as Function)!({ yAccessor: 'c', seriesKeys: ['c'], }) @@ -1583,14 +1615,16 @@ describe('XYChart component', () => { }; const component = getRenderedComponent(newArgs); + + const lineSeries = component.find(DataLayers).dive().find(LineSeries); expect( - (component.find(LineSeries).at(0).prop('color') as Function)!({ + (lineSeries.at(0).prop('color') as Function)!({ yAccessor: 'a', seriesKeys: ['a'], }) ).toEqual('blue'); expect( - (component.find(LineSeries).at(1).prop('color') as Function)!({ + (lineSeries.at(1).prop('color') as Function)!({ yAccessor: 'c', seriesKeys: ['c'], }) @@ -1623,11 +1657,15 @@ describe('XYChart component', () => { }; const component = getRenderedComponent(newArgs); - const nameFn = component.find(LineSeries).prop('name') as SeriesNameFn; + const nameFn = component + .find(DataLayers) + .dive() + .find(LineSeries) + .prop('name') as SeriesNameFn; // In this case, the ID is used as the name. This shouldn't happen in practice - expect(nameFn({ ...nameFnArgs, seriesKeys: ['a'] }, false)).toEqual(''); - expect(nameFn({ ...nameFnArgs, seriesKeys: ['nonsense'] }, false)).toEqual(''); + expect(nameFn({ ...nameFnArgs, seriesKeys: ['a'] }, false)).toEqual(null); + expect(nameFn({ ...nameFnArgs, seriesKeys: ['nonsense'] }, false)).toEqual(null); }); test('simplest xy chart with empty name', () => { @@ -1646,11 +1684,15 @@ describe('XYChart component', () => { }; const component = getRenderedComponent(newArgs); - const nameFn = component.find(LineSeries).prop('name') as SeriesNameFn; + const nameFn = component + .find(DataLayers) + .dive() + .find(LineSeries) + .prop('name') as SeriesNameFn; // In this case, the ID is used as the name. This shouldn't happen in practice expect(nameFn({ ...nameFnArgs, seriesKeys: ['a'] }, false)).toEqual(''); - expect(nameFn({ ...nameFnArgs, seriesKeys: ['nonsense'] }, false)).toEqual(''); + expect(nameFn({ ...nameFnArgs, seriesKeys: ['nonsense'] }, false)).toEqual(null); }); test('simplest xy chart with human-readable name', () => { @@ -1669,7 +1711,11 @@ describe('XYChart component', () => { }; const component = getRenderedComponent(newArgs); - const nameFn = component.find(LineSeries).prop('name') as SeriesNameFn; + const nameFn = component + .find(DataLayers) + .dive() + .find(LineSeries) + .prop('name') as SeriesNameFn; expect(nameFn({ ...nameFnArgs, seriesKeys: ['a'] }, false)).toEqual('Column A'); }); @@ -1690,14 +1736,16 @@ describe('XYChart component', () => { }; const component = getRenderedComponent(newArgs); - const nameFn1 = component.find(LineSeries).at(0).prop('name') as SeriesNameFn; - const nameFn2 = component.find(LineSeries).at(1).prop('name') as SeriesNameFn; + + const lineSeries = component.find(DataLayers).dive().find(LineSeries); + const nameFn1 = lineSeries.at(0).prop('name') as SeriesNameFn; + const nameFn2 = lineSeries.at(1).prop('name') as SeriesNameFn; // This accessor has a human-readable name expect(nameFn1({ ...nameFnArgs, seriesKeys: ['a'] }, false)).toEqual('Label A'); // This accessor does not - expect(nameFn2({ ...nameFnArgs, seriesKeys: ['b'] }, false)).toEqual(''); - expect(nameFn1({ ...nameFnArgs, seriesKeys: ['nonsense'] }, false)).toEqual(''); + expect(nameFn2({ ...nameFnArgs, seriesKeys: ['b'] }, false)).toEqual(null); + expect(nameFn1({ ...nameFnArgs, seriesKeys: ['nonsense'] }, false)).toEqual(null); }); test('split series without formatting and single y accessor', () => { @@ -1716,7 +1764,11 @@ describe('XYChart component', () => { }; const component = getRenderedComponent(newArgs); - const nameFn = component.find(LineSeries).prop('name') as SeriesNameFn; + const nameFn = component + .find(DataLayers) + .dive() + .find(LineSeries) + .prop('name') as SeriesNameFn; expect(nameFn({ ...nameFnArgs, seriesKeys: ['split1', 'a'] }, false)).toEqual('split1'); }); @@ -1737,7 +1789,11 @@ describe('XYChart component', () => { }; const component = getRenderedComponent(newArgs); - const nameFn = component.find(LineSeries).prop('name') as SeriesNameFn; + const nameFn = component + .find(DataLayers) + .dive() + .find(LineSeries) + .prop('name') as SeriesNameFn; convertSpy.mockReturnValueOnce('formatted'); expect(nameFn({ ...nameFnArgs, seriesKeys: ['split1', 'a'] }, false)).toEqual('formatted'); @@ -1760,8 +1816,10 @@ describe('XYChart component', () => { }; const component = getRenderedComponent(newArgs); - const nameFn1 = component.find(LineSeries).at(0).prop('name') as SeriesNameFn; - const nameFn2 = component.find(LineSeries).at(0).prop('name') as SeriesNameFn; + + const lineSeries = component.find(DataLayers).dive().find(LineSeries); + const nameFn1 = lineSeries.at(0).prop('name') as SeriesNameFn; + const nameFn2 = lineSeries.at(0).prop('name') as SeriesNameFn; expect(nameFn1({ ...nameFnArgs, seriesKeys: ['split1', 'a'] }, false)).toEqual( 'split1 - Label A' @@ -1787,8 +1845,10 @@ describe('XYChart component', () => { }; const component = getRenderedComponent(newArgs); - const nameFn1 = component.find(LineSeries).at(0).prop('name') as SeriesNameFn; - const nameFn2 = component.find(LineSeries).at(1).prop('name') as SeriesNameFn; + + const lineSeries = component.find(DataLayers).dive().find(LineSeries); + const nameFn1 = lineSeries.at(0).prop('name') as SeriesNameFn; + const nameFn2 = lineSeries.at(1).prop('name') as SeriesNameFn; convertSpy.mockReturnValueOnce('formatted1').mockReturnValueOnce('formatted2'); expect(nameFn1({ ...nameFnArgs, seriesKeys: ['split1', 'a'] }, false)).toEqual( @@ -1812,8 +1872,10 @@ describe('XYChart component', () => { }} /> ); - expect(component.find(LineSeries).at(0).prop('xScaleType')).toEqual(ScaleType.Ordinal); - expect(component.find(LineSeries).at(1).prop('xScaleType')).toEqual(ScaleType.Ordinal); + + const lineSeries = component.find(DataLayers).dive().find(LineSeries); + expect(lineSeries.at(0).prop('xScaleType')).toEqual(ScaleType.Ordinal); + expect(lineSeries.at(1).prop('xScaleType')).toEqual(ScaleType.Ordinal); }); test('it set the scale of the y axis according to the args prop', () => { @@ -1828,8 +1890,10 @@ describe('XYChart component', () => { }} /> ); - expect(component.find(LineSeries).at(0).prop('yScaleType')).toEqual(ScaleType.Sqrt); - expect(component.find(LineSeries).at(1).prop('yScaleType')).toEqual(ScaleType.Sqrt); + + const lineSeries = component.find(DataLayers).dive().find(LineSeries); + expect(lineSeries.at(0).prop('yScaleType')).toEqual(ScaleType.Sqrt); + expect(lineSeries.at(1).prop('yScaleType')).toEqual(ScaleType.Sqrt); }); test('it gets the formatter for the x axis', () => { @@ -2093,7 +2157,7 @@ describe('XYChart component', () => { const component = shallow(); - const series = component.find(LineSeries); + const series = component.find(DataLayers).dive().find(LineSeries); // Only one series should be rendered, even though 2 are configured // This one series should only have one row, even though 2 are sent @@ -2166,7 +2230,7 @@ describe('XYChart component', () => { const component = shallow(); - const series = component.find(LineSeries); + const series = component.find(DataLayers).dive().find(LineSeries); expect(series.prop('data')).toEqual([ { a: 0, b: 2, c: 5 }, @@ -2340,13 +2404,13 @@ describe('XYChart component', () => { const component = shallow( ); - - expect(component.find(LineSeries).prop('fit')).toEqual({ type: Fit.Carry }); - expect(component.find(BarSeries).prop('fit')).toEqual(undefined); - expect(component.find(AreaSeries).at(0).prop('fit')).toEqual({ type: Fit.Carry }); - expect(component.find(AreaSeries).at(0).prop('stackAccessors')).toEqual([]); - expect(component.find(AreaSeries).at(1).prop('fit')).toEqual({ type: Fit.Carry }); - expect(component.find(AreaSeries).at(1).prop('stackAccessors')).toEqual(['c']); + const dataLayers = component.find(DataLayers).dive(); + expect(dataLayers.find(LineSeries).prop('fit')).toEqual({ type: Fit.Carry }); + expect(dataLayers.find(BarSeries).prop('fit')).toEqual(undefined); + expect(dataLayers.find(AreaSeries).at(0).prop('fit')).toEqual({ type: Fit.Carry }); + expect(dataLayers.find(AreaSeries).at(0).prop('stackAccessors')).toEqual([]); + expect(dataLayers.find(AreaSeries).at(1).prop('fit')).toEqual({ type: Fit.Carry }); + expect(dataLayers.find(AreaSeries).at(1).prop('stackAccessors')).toEqual(['c']); }); test('it should apply None fitting function if not specified', () => { @@ -2356,7 +2420,9 @@ describe('XYChart component', () => { const component = shallow(); - expect(component.find(LineSeries).prop('fit')).toEqual({ type: Fit.None }); + expect(component.find(DataLayers).dive().find(LineSeries).prop('fit')).toEqual({ + type: Fit.None, + }); }); test('it should apply the xTitle if is specified', () => { @@ -2458,7 +2524,7 @@ describe('XYChart component', () => { ); - expect(component.find(LineSeries).at(1).prop('data')).toEqual([ + expect(component.find(DataLayers).dive().find(LineSeries).at(1).prop('data')).toEqual([ { a: 5, b: 2, From a78a466c1f68259cf261428c7d862eb42a447288 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Thu, 7 Apr 2022 09:47:37 +0300 Subject: [PATCH 091/133] Fixed tests. --- src/plugins/expressions/common/execution/execution.test.ts | 2 +- src/plugins/expressions/common/execution/execution.ts | 4 ---- .../__snapshots__/to_expression.test.ts.snap | 6 ------ 3 files changed, 1 insertion(+), 11 deletions(-) diff --git a/src/plugins/expressions/common/execution/execution.test.ts b/src/plugins/expressions/common/execution/execution.test.ts index 786b18405309f..a8fbe49521019 100644 --- a/src/plugins/expressions/common/execution/execution.test.ts +++ b/src/plugins/expressions/common/execution/execution.test.ts @@ -714,7 +714,7 @@ describe('Execution', () => { expect(result).toMatchObject({ type: 'error', error: { - message: '[requiredArg] > requiredArg requires an argument', + message: '[requiredArg] > requiredArg requires an "arg" argument', }, }); }); diff --git a/src/plugins/expressions/common/execution/execution.ts b/src/plugins/expressions/common/execution/execution.ts index 3b296c7af88c6..eeeed5af5d3e5 100644 --- a/src/plugins/expressions/common/execution/execution.ts +++ b/src/plugins/expressions/common/execution/execution.ts @@ -489,10 +489,6 @@ export class Execution< continue; } - if (!aliases?.length && name === '_') { - throw new Error(`${fnDef.name} requires an argument`); - } - // use an alias if _ is the missing arg const errorArg = name === '_' ? aliases[0] : name; throw new Error(`${fnDef.name} requires an "${errorArg}" argument`); diff --git a/x-pack/plugins/lens/public/xy_visualization/__snapshots__/to_expression.test.ts.snap b/x-pack/plugins/lens/public/xy_visualization/__snapshots__/to_expression.test.ts.snap index a38f1e92f1174..0743946c4b9e0 100644 --- a/x-pack/plugins/lens/public/xy_visualization/__snapshots__/to_expression.test.ts.snap +++ b/x-pack/plugins/lens/public/xy_visualization/__snapshots__/to_expression.test.ts.snap @@ -30,9 +30,6 @@ Object { "curveType": Array [ "LINEAR", ], - "description": Array [ - "", - ], "emphasizeFitting": Array [ true, ], @@ -213,9 +210,6 @@ Object { "type": "expression", }, ], - "title": Array [ - "", - ], "valueLabels": Array [ "hide", ], From 5c8981156892705ad8c3972610cf890aae4b09bf Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Thu, 7 Apr 2022 12:51:46 +0300 Subject: [PATCH 092/133] Refactored dataLayers helpers and xy_chart. --- .../public/components/xy_chart.tsx | 19 +-- .../public/helpers/data_layers.tsx | 113 +++++++++++------- 2 files changed, 70 insertions(+), 62 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx index 8c5533981cc66..f7f9164bf9eec 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx @@ -203,11 +203,7 @@ export function XYChart({ yRight: true, }; - const labelsOrientation = args.labelsOrientation || { - x: 0, - yLeft: 0, - yRight: 0, - }; + const labelsOrientation = args.labelsOrientation || { x: 0, yLeft: 0, yRight: 0 }; const filteredBarLayers = dataLayers.filter((layer) => layer.seriesType.includes('bar')); @@ -424,12 +420,7 @@ export function XYChart({ }); } const context: FilterEvent['data'] = { - data: points.map((point) => ({ - row: point.row, - column: point.column, - value: point.value, - table, - })), + data: points.map(({ row, column, value }) => ({ row, column, value, table })), }; onClickValue(context); }; @@ -447,11 +438,7 @@ export function XYChart({ const xAxisColumnIndex = table.columns.findIndex((el) => el.id === dataLayers[0].xAccessor); - const context: BrushEvent['data'] = { - range: [min, max], - table, - column: xAxisColumnIndex, - }; + const context: BrushEvent['data'] = { range: [min, max], table, column: xAxisColumnIndex }; onSelectRange(context); }; diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/data_layers.tsx b/src/plugins/chart_expressions/expression_xy/public/helpers/data_layers.tsx index ab7aa9c0b77fe..925b086b02006 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/data_layers.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/data_layers.tsx @@ -150,6 +150,59 @@ const getSeriesName: GetSeriesNameFn = ( return layer.splitAccessor ? data.seriesKeys[0] : columnToLabelMap[data.seriesKeys[0]] ?? null; }; +const getPointConfig = (xAccessor?: string, emphasizeFitting?: boolean) => ({ + visible: !xAccessor, + radius: xAccessor && !emphasizeFitting ? 5 : 0, +}); + +const getLineConfig = () => ({ visible: true, stroke: ColorVariant.Series, opacity: 1, dash: [] }); + +type GetColorFn = ( + seriesIdentifier: XYChartSeriesIdentifier, + config: { + layer: CommonXYDataLayerConfigResult; + layerId: number; + accessor: string; + colorAssignments: ColorAssignments; + columnToLabelMap: Record; + paletteService: PaletteRegistry; + syncColors?: boolean; + } +) => string | null; + +const getColor: GetColorFn = ( + { yAccessor, seriesKeys }, + { layer, layerId, accessor, colorAssignments, columnToLabelMap, paletteService, syncColors } +) => { + const overwriteColor = getSeriesColor(layer, accessor); + if (overwriteColor !== null) { + return overwriteColor; + } + const colorAssignment = colorAssignments[layer.palette.name]; + const seriesLayers: SeriesLayer[] = [ + { + name: layer.splitAccessor ? String(seriesKeys[0]) : columnToLabelMap[seriesKeys[0]], + totalSeriesAtDepth: colorAssignment.totalSeriesCount, + rankAtDepth: colorAssignment.getRank( + layer, + layerId, + String(seriesKeys[0]), + String(yAccessor) + ), + }, + ]; + return paletteService.get(layer.palette.name).getCategoricalColor( + seriesLayers, + { + maxDepth: 1, + behindText: false, + totalSeries: colorAssignment.totalSeriesCount, + syncColors, + }, + layer.palette.params + ); +}; + export const getSeriesProps: GetSeriesPropsFn = ({ layer, layerId, @@ -220,62 +273,30 @@ export const getSeriesProps: GetSeriesPropsFn = ({ formatter?.id === 'bytes' && layer.yScaleType === ScaleType.Linear ? ScaleType.LinearBinary : layer.yScaleType, - color: ({ yAccessor, seriesKeys }) => { - const overwriteColor = getSeriesColor(layer, accessor); - if (overwriteColor !== null) { - return overwriteColor; - } - const colorAssignment = colorAssignments[layer.palette.name]; - const seriesLayers: SeriesLayer[] = [ - { - name: layer.splitAccessor ? String(seriesKeys[0]) : columnToLabelMap[seriesKeys[0]], - totalSeriesAtDepth: colorAssignment.totalSeriesCount, - rankAtDepth: colorAssignment.getRank( - layer, - layerId, - String(seriesKeys[0]), - String(yAccessor) - ), - }, - ]; - return paletteService.get(layer.palette.name).getCategoricalColor( - seriesLayers, - { - maxDepth: 1, - behindText: false, - totalSeries: colorAssignment.totalSeriesCount, - syncColors, - }, - layer.palette.params - ); - }, + color: (series) => + getColor(series, { + layer, + layerId, + accessor, + colorAssignments, + columnToLabelMap, + paletteService, + syncColors, + }), groupId: yAxis?.groupId, enableHistogramMode, stackMode: isPercentage ? StackMode.Percentage : undefined, timeZone, areaSeriesStyle: { - point: { - visible: !layer.xAccessor, - radius: layer.xAccessor && !emphasizeFitting ? 5 : 0, - }, + point: getPointConfig(layer.xAccessor, emphasizeFitting), ...(fillOpacity && { area: { opacity: fillOpacity } }), ...(emphasizeFitting && { - fit: { - area: { opacity: fillOpacity || 0.5 }, - line: { visible: true, stroke: ColorVariant.Series, opacity: 1, dash: [] }, - }, + fit: { area: { opacity: fillOpacity || 0.5 }, line: getLineConfig() }, }), }, lineSeriesStyle: { - point: { - visible: !layer.xAccessor, - radius: layer.xAccessor && !emphasizeFitting ? 5 : 0, - }, - ...(emphasizeFitting && { - fit: { - line: { visible: true, stroke: ColorVariant.Series, opacity: 1, dash: [] }, - }, - }), + point: getPointConfig(layer.xAccessor, emphasizeFitting), + ...(emphasizeFitting && { fit: { line: getLineConfig() } }), }, name(d) { return getSeriesName(d, { From b43a793402da6491fcd74c7a7f199983db72f43c Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Thu, 7 Apr 2022 18:12:55 +0300 Subject: [PATCH 093/133] More fixes of the expression Added extendedYConfig for dataLayers. Added yConfig for referenceLineLayers. Fixed undefined id at tooltip. --- .../expression_xy/common/constants.ts | 1 + .../extended_reference_line_layer.ts | 4 +- .../extended_y_axis_config.ts | 97 +++++++++++++++++++ .../common/expression_functions/index.ts | 1 + .../reference_line_layer.ts | 4 +- .../expression_functions/y_axis_config.ts | 44 +-------- .../expression_xy/common/index.ts | 4 +- .../common/types/expression_functions.ts | 17 ++-- .../components/reference_lines.test.tsx | 14 +-- .../public/helpers/annotations.tsx | 6 +- .../public/helpers/data_layers.tsx | 50 +++++----- .../expression_xy/public/helpers/state.ts | 8 +- .../expression_xy/public/plugin.ts | 2 + .../expression_xy/server/plugin.ts | 2 + x-pack/plugins/lens/public/index.ts | 4 +- .../reference_line_helpers.tsx | 4 +- .../public/xy_visualization/state_helpers.ts | 5 +- .../public/xy_visualization/to_expression.ts | 24 ++++- .../lens/public/xy_visualization/types.ts | 3 +- .../public/xy_visualization/visualization.tsx | 4 +- .../xy_config_panel/dimension_editor.tsx | 4 +- .../xy_config_panel/reference_line_panel.tsx | 10 +- .../shared/exploratory_view/types.ts | 4 +- 23 files changed, 202 insertions(+), 114 deletions(-) create mode 100644 src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_y_axis_config.ts diff --git a/src/plugins/chart_expressions/expression_xy/common/constants.ts b/src/plugins/chart_expressions/expression_xy/common/constants.ts index e912defcc4642..6652f025a67e5 100644 --- a/src/plugins/chart_expressions/expression_xy/common/constants.ts +++ b/src/plugins/chart_expressions/expression_xy/common/constants.ts @@ -9,6 +9,7 @@ export const XY_VIS = 'xyVis'; export const LAYERED_XY_VIS = 'layeredXyVis'; export const Y_CONFIG = 'yConfig'; +export const EXTENDED_Y_CONFIG = 'extendedYConfig'; export const MULTITABLE = 'lens_multitable'; export const DATA_LAYER = 'dataLayer'; export const EXTENDED_DATA_LAYER = 'extendedDataLayer'; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_reference_line_layer.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_reference_line_layer.ts index d015e355ce7f0..677f1bfe94d05 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_reference_line_layer.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_reference_line_layer.ts @@ -8,7 +8,7 @@ import { i18n } from '@kbn/i18n'; import type { Datatable, ExpressionFunctionDefinition } from '../../../../expressions/common'; -import { LayerTypes, EXTENDED_REFERENCE_LINE_LAYER, Y_CONFIG } from '../constants'; +import { LayerTypes, EXTENDED_REFERENCE_LINE_LAYER, EXTENDED_Y_CONFIG } from '../constants'; import { ExtendedReferenceLineLayerArgs, ExtendedReferenceLineLayerConfigResult } from '../types'; export const extendedReferenceLineLayerFunction: ExpressionFunctionDefinition< @@ -33,7 +33,7 @@ export const extendedReferenceLineLayerFunction: ExpressionFunctionDefinition< multi: true, }, yConfig: { - types: [Y_CONFIG], + types: [EXTENDED_Y_CONFIG], help: i18n.translate('expressionXY.extendedReferenceLineLayer.yConfig.help', { defaultMessage: 'Additional configuration for y axes', }), diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_y_axis_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_y_axis_config.ts new file mode 100644 index 0000000000000..253a5b4e63b8f --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_y_axis_config.ts @@ -0,0 +1,97 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import type { ExpressionFunctionDefinition } from '../../../../expressions/common'; +import { EXTENDED_Y_CONFIG, FillStyles, IconPositions, LineStyles, YAxisModes } from '../constants'; +import { ExtendedYConfig, ExtendedYConfigResult } from '../types'; + +export const extendedYAxisConfigFunction: ExpressionFunctionDefinition< + typeof EXTENDED_Y_CONFIG, + null, + ExtendedYConfig, + ExtendedYConfigResult +> = { + name: EXTENDED_Y_CONFIG, + aliases: [], + type: EXTENDED_Y_CONFIG, + help: i18n.translate('expressionXY.yConfig.help', { + defaultMessage: `Configure the behavior of a xy chart's y axis metric`, + }), + inputTypes: ['null'], + args: { + forAccessor: { + types: ['string'], + help: i18n.translate('expressionXY.yConfig.forAccessor.help', { + defaultMessage: 'The accessor this configuration is for', + }), + }, + axisMode: { + types: ['string'], + options: [...Object.values(YAxisModes)], + help: i18n.translate('expressionXY.yConfig.axisMode.help', { + defaultMessage: 'The axis mode of the metric', + }), + strict: true, + }, + color: { + types: ['string'], + help: i18n.translate('expressionXY.yConfig.color.help', { + defaultMessage: 'The color of the series', + }), + }, + lineStyle: { + types: ['string'], + options: [...Object.values(LineStyles)], + help: i18n.translate('expressionXY.yConfig.lineStyle.help', { + defaultMessage: 'The style of the reference line', + }), + strict: true, + }, + lineWidth: { + types: ['number'], + help: i18n.translate('expressionXY.yConfig.lineWidth.help', { + defaultMessage: 'The width of the reference line', + }), + }, + icon: { + types: ['string'], + help: i18n.translate('expressionXY.yConfig.icon.help', { + defaultMessage: 'An optional icon used for reference lines', + }), + }, + iconPosition: { + types: ['string'], + options: [...Object.values(IconPositions)], + help: i18n.translate('expressionXY.yConfig.iconPosition.help', { + defaultMessage: 'The placement of the icon for the reference line', + }), + strict: true, + }, + textVisibility: { + types: ['boolean'], + help: i18n.translate('expressionXY.yConfig.textVisibility.help', { + defaultMessage: 'Visibility of the label on the reference line', + }), + }, + fill: { + types: ['string'], + options: [...Object.values(FillStyles)], + help: i18n.translate('expressionXY.yConfig.fill.help', { + defaultMessage: 'Fill', + }), + strict: true, + }, + }, + fn(input, args) { + return { + type: EXTENDED_Y_CONFIG, + ...args, + }; + }, +}; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/index.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/index.ts index 4f9e7340046ab..ab1d570a07351 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/index.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/index.ts @@ -12,6 +12,7 @@ export * from './legend_config'; export * from './annotation_layer'; export * from './extended_annotation_layer'; export * from './y_axis_config'; +export * from './extended_y_axis_config'; export * from './data_layer'; export * from './extended_data_layer'; export * from './grid_lines_config'; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line_layer.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line_layer.ts index 0a93b31e8c623..04983881ff68b 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line_layer.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line_layer.ts @@ -8,7 +8,7 @@ import { i18n } from '@kbn/i18n'; import type { Datatable, ExpressionFunctionDefinition } from '../../../../expressions/common'; -import { LayerTypes, REFERENCE_LINE_LAYER, Y_CONFIG } from '../constants'; +import { EXTENDED_Y_CONFIG, LayerTypes, REFERENCE_LINE_LAYER } from '../constants'; import { ReferenceLineLayerArgs, ReferenceLineLayerConfigResult } from '../types'; export const referenceLineLayerFunction: ExpressionFunctionDefinition< @@ -33,7 +33,7 @@ export const referenceLineLayerFunction: ExpressionFunctionDefinition< multi: true, }, yConfig: { - types: [Y_CONFIG], + types: [EXTENDED_Y_CONFIG], help: i18n.translate('expressionXY.referenceLineLayer.yConfig.help', { defaultMessage: 'Additional configuration for y axes', }), diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/y_axis_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/y_axis_config.ts index d6432d1f373bd..e2bcff3ada522 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/y_axis_config.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/y_axis_config.ts @@ -8,7 +8,7 @@ import { i18n } from '@kbn/i18n'; import type { ExpressionFunctionDefinition } from '../../../../expressions/common'; -import { FillStyles, IconPositions, LineStyles, YAxisModes, Y_CONFIG } from '../constants'; +import { YAxisModes, Y_CONFIG } from '../constants'; import { YConfig, YConfigResult } from '../types'; export const yAxisConfigFunction: ExpressionFunctionDefinition< @@ -45,48 +45,6 @@ export const yAxisConfigFunction: ExpressionFunctionDefinition< defaultMessage: 'The color of the series', }), }, - lineStyle: { - types: ['string'], - options: [...Object.values(LineStyles)], - help: i18n.translate('expressionXY.yConfig.lineStyle.help', { - defaultMessage: 'The style of the reference line', - }), - strict: true, - }, - lineWidth: { - types: ['number'], - help: i18n.translate('expressionXY.yConfig.lineWidth.help', { - defaultMessage: 'The width of the reference line', - }), - }, - icon: { - types: ['string'], - help: i18n.translate('expressionXY.yConfig.icon.help', { - defaultMessage: 'An optional icon used for reference lines', - }), - }, - iconPosition: { - types: ['string'], - options: [...Object.values(IconPositions)], - help: i18n.translate('expressionXY.yConfig.iconPosition.help', { - defaultMessage: 'The placement of the icon for the reference line', - }), - strict: true, - }, - textVisibility: { - types: ['boolean'], - help: i18n.translate('expressionXY.yConfig.textVisibility.help', { - defaultMessage: 'Visibility of the label on the reference line', - }), - }, - fill: { - types: ['string'], - options: [...Object.values(FillStyles)], - help: i18n.translate('expressionXY.yConfig.fill.help', { - defaultMessage: 'Fill', - }), - strict: true, - }, }, fn(input, args) { return { diff --git a/src/plugins/chart_expressions/expression_xy/common/index.ts b/src/plugins/chart_expressions/expression_xy/common/index.ts index 50c3afc50b204..b6fb31b8daade 100755 --- a/src/plugins/chart_expressions/expression_xy/common/index.ts +++ b/src/plugins/chart_expressions/expression_xy/common/index.ts @@ -13,6 +13,7 @@ export { xyVisFunction, layeredXyVisFunction, yAxisConfigFunction, + extendedYAxisConfigFunction, legendConfigFunction, gridlinesConfigFunction, dataLayerFunction, @@ -46,17 +47,18 @@ export type { XYChartProps, LegendConfig, IconPosition, - YConfigResult, DataLayerArgs, LensMultiTable, ValueLabelMode, AxisExtentMode, FittingFunction, + ExtendedYConfig, AxisExtentConfig, LegendConfigResult, AxesSettingsConfig, AnnotationLayerArgs, XYLayerConfigResult, + ExtendedYConfigResult, GridlinesConfigResult, DataLayerConfigResult, TickLabelsConfigResult, diff --git a/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts b/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts index 199720be115ac..0b56352bd2c84 100644 --- a/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts +++ b/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts @@ -39,6 +39,7 @@ import { ANNOTATION_LAYER, EndValues, EXTENDED_ANNOTATION_LAYER, + EXTENDED_Y_CONFIG, } from '../constants'; export type EndValue = $Values; @@ -72,10 +73,7 @@ export interface AxisConfig { hide?: boolean; } -export interface YConfig { - forAccessor: string; - axisMode?: YAxisMode; - color?: string; +export interface ExtendedYConfig extends YConfig { icon?: string; lineWidth?: number; lineStyle?: LineStyle; @@ -84,6 +82,12 @@ export interface YConfig { textVisibility?: boolean; } +export interface YConfig { + forAccessor: string; + axisMode?: YAxisMode; + color?: string; +} + export interface DataLayerArgs { accessors: string[]; seriesType: SeriesType; @@ -270,13 +274,13 @@ export type ExtendedAnnotationLayerConfigResult = ExtendedAnnotationLayerArgs & export interface ReferenceLineLayerArgs { accessors: string[]; columnToLabel?: string; - yConfig?: YConfigResult[]; + yConfig?: ExtendedYConfigResult[]; } export interface ExtendedReferenceLineLayerArgs { accessors: string[]; columnToLabel?: string; - yConfig?: YConfigResult[]; + yConfig?: ExtendedYConfigResult[]; table?: Datatable; } @@ -328,6 +332,7 @@ export type ExtendedDataLayerConfigResult = Omit { ['yAccessorLeft', 'below'], ['yAccessorRight', 'above'], ['yAccessorRight', 'below'], - ] as Array<[string, YConfig['fill']]>)( + ] as Array<[string, ExtendedYConfig['fill']]>)( 'should render a RectAnnotation for a reference line with fill set: %s %s', (layerPrefix, fill) => { const axisMode = getAxisFromId(layerPrefix); @@ -134,7 +134,7 @@ describe('ReferenceLineAnnotations', () => { it.each([ ['xAccessor', 'above'], ['xAccessor', 'below'], - ] as Array<[string, YConfig['fill']]>)( + ] as Array<[string, ExtendedYConfig['fill']]>)( 'should render a RectAnnotation for a reference line with fill set: %s %s', (layerPrefix, fill) => { const wrapper = shallow( @@ -174,7 +174,7 @@ describe('ReferenceLineAnnotations', () => { ['yAccessorLeft', 'below', { y0: undefined, y1: 5 }, { y0: 5, y1: 10 }], ['yAccessorRight', 'above', { y0: 5, y1: 10 }, { y0: 10, y1: undefined }], ['yAccessorRight', 'below', { y0: undefined, y1: 5 }, { y0: 5, y1: 10 }], - ] as Array<[string, YConfig['fill'], YCoords, YCoords]>)( + ] as Array<[string, ExtendedYConfig['fill'], YCoords, YCoords]>)( 'should avoid overlap between two reference lines with fill in the same direction: 2 x %s %s', (layerPrefix, fill, coordsA, coordsB) => { const axisMode = getAxisFromId(layerPrefix); @@ -224,7 +224,7 @@ describe('ReferenceLineAnnotations', () => { it.each([ ['xAccessor', 'above', { x0: 1, x1: 2 }, { x0: 2, x1: undefined }], ['xAccessor', 'below', { x0: undefined, x1: 1 }, { x0: 1, x1: 2 }], - ] as Array<[string, YConfig['fill'], XCoords, XCoords]>)( + ] as Array<[string, ExtendedYConfig['fill'], XCoords, XCoords]>)( 'should avoid overlap between two reference lines with fill in the same direction: 2 x %s %s', (layerPrefix, fill, coordsA, coordsB) => { const wrapper = shallow( @@ -321,7 +321,7 @@ describe('ReferenceLineAnnotations', () => { it.each([ ['above', { y0: 5, y1: 10 }, { y0: 10, y1: undefined }], ['below', { y0: undefined, y1: 5 }, { y0: 5, y1: 10 }], - ] as Array<[YConfig['fill'], YCoords, YCoords]>)( + ] as Array<[ExtendedYConfig['fill'], YCoords, YCoords]>)( 'should be robust and works also for different axes when on same direction: 1x Left + 1x Right both %s', (fill, coordsA, coordsB) => { const wrapper = shallow( diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/annotations.tsx b/src/plugins/chart_expressions/expression_xy/public/helpers/annotations.tsx index 9050bdee4a365..5db98e8fb4d7f 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/annotations.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/annotations.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { Position } from '@elastic/charts'; import { EuiFlexGroup, EuiIcon, EuiIconProps, EuiText } from '@elastic/eui'; import classnames from 'classnames'; -import type { IconPosition, YAxisMode, YConfig } from '../../common/types'; +import type { IconPosition, YAxisMode, ExtendedYConfig } from '../../common/types'; import { getBaseIconPlacement } from '../components'; import { hasIcon } from './icon'; import { annotationsIconSet } from './annotations_icon_set'; @@ -20,7 +20,7 @@ export const LINES_MARKER_SIZE = 20; export const getLinesCausedPaddings = ( visualConfigs: Array< - Pick | undefined + Pick | undefined >, axesMap: Record<'left' | 'right', unknown> ) => { @@ -32,7 +32,7 @@ export const getLinesCausedPaddings = ( return; } const { axisMode, icon, iconPosition, textVisibility } = config; - if (axisMode && (hasIcon(icon) || textVisibility)) { + if (axisMode && textVisibility) { const placement = getBaseIconPlacement(iconPosition, axesMap, axisMode); paddings[placement] = Math.max( paddings[placement] || 0, diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/data_layers.tsx b/src/plugins/chart_expressions/expression_xy/public/helpers/data_layers.tsx index 925b086b02006..0d5d0519d3965 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/data_layers.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/data_layers.tsx @@ -49,6 +49,30 @@ type GetSeriesPropsFn = (config: { fillOpacity?: number; }) => SeriesSpec; +type GetSeriesNameFn = ( + data: XYChartSeriesIdentifier, + config: { + layer: CommonXYDataLayerConfigResult; + splitHint: SerializedFieldFormat | undefined; + splitFormatter: FieldFormat; + alreadyFormattedColumns: Record; + columnToLabelMap: Record; + } +) => SeriesName; + +type GetColorFn = ( + seriesIdentifier: XYChartSeriesIdentifier, + config: { + layer: CommonXYDataLayerConfigResult; + layerId: number; + accessor: string; + colorAssignments: ColorAssignments; + columnToLabelMap: Record; + paletteService: PaletteRegistry; + syncColors?: boolean; + } +) => string | null; + const isPrimitive = (value: unknown): boolean => value != null && typeof value !== 'object'; export const getFormattedTable = ( @@ -105,17 +129,6 @@ export const getAreAlreadyFormattedLayersInfo = ( {} ); -type GetSeriesNameFn = ( - data: XYChartSeriesIdentifier, - config: { - layer: CommonXYDataLayerConfigResult; - splitHint: SerializedFieldFormat | undefined; - splitFormatter: FieldFormat; - alreadyFormattedColumns: Record; - columnToLabelMap: Record; - } -) => SeriesName; - const getSeriesName: GetSeriesNameFn = ( data, { layer, splitHint, splitFormatter, alreadyFormattedColumns, columnToLabelMap } @@ -157,19 +170,6 @@ const getPointConfig = (xAccessor?: string, emphasizeFitting?: boolean) => ({ const getLineConfig = () => ({ visible: true, stroke: ColorVariant.Series, opacity: 1, dash: [] }); -type GetColorFn = ( - seriesIdentifier: XYChartSeriesIdentifier, - config: { - layer: CommonXYDataLayerConfigResult; - layerId: number; - accessor: string; - colorAssignments: ColorAssignments; - columnToLabelMap: Record; - paletteService: PaletteRegistry; - syncColors?: boolean; - } -) => string | null; - const getColor: GetColorFn = ( { yAccessor, seriesKeys }, { layer, layerId, accessor, colorAssignments, columnToLabelMap, paletteService, syncColors } @@ -264,7 +264,7 @@ export const getSeriesProps: GetSeriesPropsFn = ({ return { splitSeriesAccessors: layer.splitAccessor ? [layer.splitAccessor] : [], stackAccessors: isStacked ? [layer.xAccessor as string] : [], - id: `${layer.splitAccessor}-${accessor}`, + id: layer.splitAccessor ? `${layer.splitAccessor}-${accessor}` : `${accessor}`, xAccessor: layer.xAccessor || 'unifiedX', yAccessors: [accessor], data: rows, diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/state.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/state.ts index 269be74ad97bf..23a8ddfc49f13 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/state.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/state.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import type { CommonXYLayerConfigResult, SeriesType, YConfig } from '../../common'; +import type { CommonXYLayerConfigResult, SeriesType, ExtendedYConfig, YConfig } from '../../common'; import { getDataLayers, isAnnotationsLayer, isDataLayer } from './visualization'; export function isHorizontalSeries(seriesType: SeriesType) { @@ -29,8 +29,6 @@ export const getSeriesColor = (layer: CommonXYLayerConfigResult, accessor: strin if ((isDataLayer(layer) && layer.splitAccessor) || isAnnotationsLayer(layer)) { return null; } - - return ( - layer?.yConfig?.find((yConfig: YConfig) => yConfig.forAccessor === accessor)?.color || null - ); + const yConfig: Array | undefined = layer?.yConfig; + return yConfig?.find((yConf) => yConf.forAccessor === accessor)?.color || null; }; diff --git a/src/plugins/chart_expressions/expression_xy/public/plugin.ts b/src/plugins/chart_expressions/expression_xy/public/plugin.ts index 946a346a67789..f13688a2dd739 100755 --- a/src/plugins/chart_expressions/expression_xy/public/plugin.ts +++ b/src/plugins/chart_expressions/expression_xy/public/plugin.ts @@ -19,6 +19,7 @@ import { dataLayerFunction, extendedDataLayerFunction, yAxisConfigFunction, + extendedYAxisConfigFunction, legendConfigFunction, gridlinesConfigFunction, axisExtentConfigFunction, @@ -55,6 +56,7 @@ export class ExpressionXyPlugin { { expressions, charts }: SetupDeps ): ExpressionXyPluginSetup { expressions.registerFunction(yAxisConfigFunction); + expressions.registerFunction(extendedYAxisConfigFunction); expressions.registerFunction(legendConfigFunction); expressions.registerFunction(gridlinesConfigFunction); expressions.registerFunction(dataLayerFunction); diff --git a/src/plugins/chart_expressions/expression_xy/server/plugin.ts b/src/plugins/chart_expressions/expression_xy/server/plugin.ts index 71dad2f115b0a..e6e4228e56858 100755 --- a/src/plugins/chart_expressions/expression_xy/server/plugin.ts +++ b/src/plugins/chart_expressions/expression_xy/server/plugin.ts @@ -12,6 +12,7 @@ import { ExpressionXyPluginSetup, ExpressionXyPluginStart } from './types'; import { xyVisFunction, yAxisConfigFunction, + extendedYAxisConfigFunction, legendConfigFunction, gridlinesConfigFunction, dataLayerFunction, @@ -33,6 +34,7 @@ export class ExpressionXyPlugin { public setup(core: CoreSetup, { expressions }: SetupDeps) { expressions.registerFunction(yAxisConfigFunction); + expressions.registerFunction(extendedYAxisConfigFunction); expressions.registerFunction(legendConfigFunction); expressions.registerFunction(gridlinesConfigFunction); expressions.registerFunction(dataLayerFunction); diff --git a/x-pack/plugins/lens/public/index.ts b/x-pack/plugins/lens/public/index.ts index f36550c6d325a..a8be41b59930d 100644 --- a/x-pack/plugins/lens/public/index.ts +++ b/x-pack/plugins/lens/public/index.ts @@ -71,7 +71,7 @@ export type { } from './indexpattern_datasource/types'; export type { XYArgs, - YConfig, + ExtendedYConfig, XYRender, LayerType, YAxisMode, @@ -85,7 +85,7 @@ export type { XYChartProps, LegendConfig, IconPosition, - YConfigResult, + ExtendedYConfigResult, DataLayerArgs, LensMultiTable, ValueLabelMode, diff --git a/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx b/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx index 0d2a7d5ebf5db..c7439d28abb26 100644 --- a/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx @@ -10,7 +10,7 @@ import { i18n } from '@kbn/i18n'; import { layerTypes } from '../../common'; import type { YAxisMode, - YConfig, + ExtendedYConfig, } from '../../../../../src/plugins/chart_expressions/expression_xy/common'; import { Datatable } from '../../../../../src/plugins/expressions/public'; import type { DatasourcePublicAPI, FramePublicAPI, Visualization } from '../types'; @@ -37,7 +37,7 @@ export interface ReferenceLineBase { * * what groups are current defined in data layers * * what existing reference line are currently defined in reference layers */ -export function getGroupsToShow( +export function getGroupsToShow( referenceLayers: T[], state: XYState | undefined, datasourceLayers: Record, diff --git a/x-pack/plugins/lens/public/xy_visualization/state_helpers.ts b/x-pack/plugins/lens/public/xy_visualization/state_helpers.ts index a3b0bbbfaae84..bb10f28f07365 100644 --- a/x-pack/plugins/lens/public/xy_visualization/state_helpers.ts +++ b/x-pack/plugins/lens/public/xy_visualization/state_helpers.ts @@ -9,7 +9,7 @@ import { EuiIconType } from '@elastic/eui/src/components/icon/icon'; import type { FramePublicAPI, DatasourcePublicAPI } from '../types'; import type { SeriesType, - YConfig, + ExtendedYConfig, } from '../../../../../src/plugins/chart_expressions/expression_xy/common'; import { visualizationTypes, @@ -61,7 +61,8 @@ export const getSeriesColor = (layer: XYLayerConfig, accessor: string) => { return null; } return ( - layer?.yConfig?.find((yConfig: YConfig) => yConfig.forAccessor === accessor)?.color || null + layer?.yConfig?.find((yConfig: ExtendedYConfig) => yConfig.forAccessor === accessor)?.color || + null ); }; diff --git a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts index abc53c92fd854..84af182362a3f 100644 --- a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts +++ b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts @@ -20,7 +20,10 @@ import { import type { ValidXYDataLayerConfig } from './types'; import { OperationMetadata, DatasourcePublicAPI } from '../types'; import { getColumnToLabelMap } from './state_helpers'; -import type { YConfig } from '../../../../../src/plugins/chart_expressions/expression_xy/common'; +import type { + ExtendedYConfig, + YConfig, +} from '../../../../../src/plugins/chart_expressions/expression_xy/common'; import { hasIcon } from './xy_config_panel/shared/icon_select'; import { defaultReferenceLineColor } from './color_assignment'; import { getDefaultVisualValuesForLayer } from '../shared_components/datasource_default_values'; @@ -408,7 +411,7 @@ const referenceLineLayerToExpression = ( arguments: { yConfig: layer.yConfig ? layer.yConfig.map((yConfig) => - yConfigToExpression(yConfig, defaultReferenceLineColor) + extendedYConfigToExpression(yConfig, defaultReferenceLineColor) ) : [], accessors: layer.accessors, @@ -533,6 +536,23 @@ const yConfigToExpression = (yConfig: YConfig, defaultColor?: string): Ast => { { type: 'function', function: 'yConfig', + arguments: { + forAccessor: [yConfig.forAccessor], + axisMode: yConfig.axisMode ? [yConfig.axisMode] : [], + color: yConfig.color ? [yConfig.color] : defaultColor ? [defaultColor] : [], + }, + }, + ], + }; +}; + +const extendedYConfigToExpression = (yConfig: ExtendedYConfig, defaultColor?: string): Ast => { + return { + type: 'expression', + chain: [ + { + type: 'function', + function: 'extendedYConfig', arguments: { forAccessor: [yConfig.forAccessor], axisMode: yConfig.axisMode ? [yConfig.axisMode] : [], diff --git a/x-pack/plugins/lens/public/xy_visualization/types.ts b/x-pack/plugins/lens/public/xy_visualization/types.ts index 901584e785b27..0cd64f78103d2 100644 --- a/x-pack/plugins/lens/public/xy_visualization/types.ts +++ b/x-pack/plugins/lens/public/xy_visualization/types.ts @@ -28,6 +28,7 @@ import type { FittingFunction, LabelsOrientationConfig, EndValue, + ExtendedYConfig, YConfig, YScaleType, XScaleType, @@ -54,7 +55,7 @@ export interface XYDataLayerConfig { export interface XYReferenceLineLayerConfig { layerId: string; accessors: string[]; - yConfig?: YConfig[]; + yConfig?: ExtendedYConfig[]; palette?: PaletteOutput; layerType: 'referenceLine'; } diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx index 04b5661e75b3f..edc076ff257e4 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx @@ -32,7 +32,7 @@ import { FillStyle, SeriesType, YAxisMode, - YConfig, + ExtendedYConfig, } from '../../../../../src/plugins/chart_expressions/expression_xy/common'; import { State, visualizationTypes, XYSuggestion, XYLayerConfig, XYDataLayerConfig } from './types'; import { layerTypes } from '../../common'; @@ -393,7 +393,7 @@ export const getXyVisualization = ({ } const isReferenceLine = metrics.some((metric) => metric.agg === 'static_value'); const axisMode = axisPosition as YAxisMode; - const yConfig = metrics.map((metric, idx) => { + const yConfig = metrics.map((metric, idx) => { return { color: metric.color, forAccessor: metric.accessor ?? foundLayer.accessors[idx], diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/dimension_editor.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/dimension_editor.tsx index f288cf5841b28..b1ff679840c5e 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/dimension_editor.tsx @@ -14,7 +14,7 @@ import { State, XYState, XYDataLayerConfig } from '../types'; import { FormatFactory } from '../../../common'; import { YAxisMode, - YConfig, + ExtendedYConfig, } from '../../../../../../src/plugins/chart_expressions/expression_xy/common'; import { isHorizontalChart } from '../state_helpers'; import { ColorPicker } from './color_picker'; @@ -60,7 +60,7 @@ export function DimensionEditor( const axisMode = localYConfig?.axisMode || 'auto'; const setConfig = useCallback( - (yConfig: Partial | undefined) => { + (yConfig: Partial | undefined) => { if (yConfig == null) { return; } diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_panel.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_panel.tsx index fbb8920aec49b..80ae14786fca4 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_panel.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_panel.tsx @@ -14,7 +14,7 @@ import { State, XYState, XYReferenceLineLayerConfig } from '../types'; import { FormatFactory } from '../../../common'; import { FillStyle, - YConfig, + ExtendedYConfig, } from '../../../../../../src/plugins/chart_expressions/expression_xy/common'; import { ColorPicker } from './color_picker'; @@ -53,7 +53,7 @@ export const ReferenceLinePanel = ( ); const setConfig = useCallback( - (yConfig: Partial | undefined) => { + (yConfig: Partial | undefined) => { if (yConfig == null) { return; } @@ -103,7 +103,7 @@ export const ReferenceLinePanel = ( interface LabelConfigurationOptions { isHorizontal: boolean; - axisMode: YConfig['axisMode']; + axisMode: ExtendedYConfig['axisMode']; } function getFillPositionOptions({ isHorizontal, axisMode }: LabelConfigurationOptions) { @@ -149,8 +149,8 @@ export const FillSetting = ({ setConfig, isHorizontal, }: { - currentConfig?: YConfig; - setConfig: (yConfig: Partial | undefined) => void; + currentConfig?: ExtendedYConfig; + setConfig: (yConfig: Partial | undefined) => void; isHorizontal: boolean; }) => { return ( diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts index 775c989df2aec..c5ddad5afe0db 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts @@ -13,7 +13,7 @@ import { FieldBasedIndexPatternColumn, SeriesType, OperationType, - YConfig, + ExtendedYConfig, } from '../../../../../lens/public'; import { PersistableFilter } from '../../../../../lens/common'; @@ -71,7 +71,7 @@ export interface SeriesConfig { hasOperationType: boolean; palette?: PaletteOutput; yTitle?: string; - yConfig?: YConfig[]; + yConfig?: ExtendedYConfig[]; query?: { query: string; language: 'kuery' }; } From 5f661b0b07bfa18cb7c8bd0b4f371239900debab Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Fri, 8 Apr 2022 09:02:57 +0300 Subject: [PATCH 094/133] Fixed tests and snapshots. --- .../expression_xy/public/__mocks__/index.tsx | 2 +- .../components/reference_lines.test.tsx | 20 +++++++++---------- .../public/helpers/annotations.tsx | 3 ++- .../public/helpers/axes_configuration.ts | 6 ++++-- 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/public/__mocks__/index.tsx b/src/plugins/chart_expressions/expression_xy/public/__mocks__/index.tsx index ee4a14c476e65..cabd3c54cc9a3 100644 --- a/src/plugins/chart_expressions/expression_xy/public/__mocks__/index.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/__mocks__/index.tsx @@ -205,7 +205,7 @@ export function sampleArgsWithReferenceLine(value: number = 150) { type: 'referenceLineLayer', layerType: LayerTypes.REFERENCELINE, accessors: ['referenceLine-a'], - yConfig: [{ axisMode: 'left', forAccessor: 'referenceLine-a', type: 'yConfig' }], + yConfig: [{ axisMode: 'left', forAccessor: 'referenceLine-a', type: 'extendedYConfig' }], table: data, }, ], diff --git a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.test.tsx b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.test.tsx index 6883b30d6bdaa..6caf46eaa2ac4 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.test.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.test.tsx @@ -108,7 +108,7 @@ describe('ReferenceLineAnnotations', () => { axisMode, lineStyle: 'solid', fill, - type: 'yConfig', + type: 'extendedYConfig', }, ])} /> @@ -145,7 +145,7 @@ describe('ReferenceLineAnnotations', () => { forAccessor: `${layerPrefix}FirstId`, axisMode: 'bottom', lineStyle: 'solid', - type: 'yConfig', + type: 'extendedYConfig', fill, }, ])} @@ -186,14 +186,14 @@ describe('ReferenceLineAnnotations', () => { forAccessor: `${layerPrefix}FirstId`, axisMode, lineStyle: 'solid', - type: 'yConfig', + type: 'extendedYConfig', fill, }, { forAccessor: `${layerPrefix}SecondId`, axisMode, lineStyle: 'solid', - type: 'yConfig', + type: 'extendedYConfig', fill, }, ])} @@ -235,14 +235,14 @@ describe('ReferenceLineAnnotations', () => { forAccessor: `${layerPrefix}FirstId`, axisMode: 'bottom', lineStyle: 'solid', - type: 'yConfig', + type: 'extendedYConfig', fill, }, { forAccessor: `${layerPrefix}SecondId`, axisMode: 'bottom', lineStyle: 'solid', - type: 'yConfig', + type: 'extendedYConfig', fill, }, ])} @@ -284,14 +284,14 @@ describe('ReferenceLineAnnotations', () => { axisMode, lineStyle: 'solid', fill: 'above', - type: 'yConfig', + type: 'extendedYConfig', }, { forAccessor: `${layerPrefix}SecondId`, axisMode, lineStyle: 'solid', fill: 'below', - type: 'yConfig', + type: 'extendedYConfig', }, ])} /> @@ -333,14 +333,14 @@ describe('ReferenceLineAnnotations', () => { axisMode: 'left', lineStyle: 'solid', fill, - type: 'yConfig', + type: 'extendedYConfig', }, { forAccessor: `yAccessorRightSecondId`, axisMode: 'right', lineStyle: 'solid', fill, - type: 'yConfig', + type: 'extendedYConfig', }, ])} /> diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/annotations.tsx b/src/plugins/chart_expressions/expression_xy/public/helpers/annotations.tsx index 5db98e8fb4d7f..9dd67b38b20a9 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/annotations.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/annotations.tsx @@ -32,7 +32,7 @@ export const getLinesCausedPaddings = ( return; } const { axisMode, icon, iconPosition, textVisibility } = config; - if (axisMode && textVisibility) { + if (axisMode && (hasIcon(icon) || textVisibility)) { const placement = getBaseIconPlacement(iconPosition, axesMap, axisMode); paddings[placement] = Math.max( paddings[placement] || 0, @@ -48,6 +48,7 @@ export const getLinesCausedPaddings = ( paddings[placement] = LINES_MARKER_SIZE; } }); + return paddings; }; diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/axes_configuration.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/axes_configuration.ts index b625d76ba2ad0..c5a529e7181f7 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/axes_configuration.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/axes_configuration.ts @@ -11,6 +11,8 @@ import { AxisExtentConfig, CommonXYDataLayerConfigResult, CommonXYReferenceLineLayerConfigResult, + ExtendedYConfig, + YConfig, } from '../../common'; import type { IFieldFormat, @@ -59,9 +61,9 @@ export function groupAxesByType( layers.forEach((layer, index) => { const { table } = layer; layer.accessors.forEach((accessor) => { + const yConfig: Array | undefined = layer.yConfig; const mode = - layer.yConfig?.find((yAxisConfig) => yAxisConfig.forAccessor === accessor)?.axisMode || - 'auto'; + yConfig?.find((yAxisConfig) => yAxisConfig.forAccessor === accessor)?.axisMode || 'auto'; let formatter: SerializedFieldFormat = table.columns?.find((column) => column.id === accessor) ?.meta?.params || { id: 'number' }; if ( From 0b15a325b35963cc4042648d5532b0c8ea1ceeed Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Fri, 8 Apr 2022 11:45:39 +0300 Subject: [PATCH 095/133] Icons at annotations and reference lines are strict. --- .../expression_xy/common/constants.ts | 18 ++ .../extended_y_axis_config.ts | 11 +- .../expression_xy/common/index.ts | 3 + .../common/types/expression_functions.ts | 4 +- .../common/types/expression_renderers.ts | 9 + .../public/components/annotations.tsx | 9 +- .../public/components/xy_chart.test.tsx | 2 +- .../public/helpers/annotations.tsx | 28 ++- .../public/helpers/annotations_icon_set.tsx | 101 ---------- .../expression_xy/public/helpers/icon.ts | 101 ++++++++++ .../expression_xy/public/helpers/index.ts | 1 - .../event_annotation/common/constants.ts | 24 +++ src/plugins/event_annotation/common/index.ts | 2 +- .../common/manual_event_annotation/index.ts | 6 +- src/plugins/event_annotation/common/types.ts | 6 +- .../public/xy_visualization/visualization.tsx | 2 +- .../annotations_panel.tsx | 187 ++++++++++++++++++ .../annotations_config_panel/icon_set.ts | 5 +- .../annotations_config_panel/index.tsx | 181 +---------------- .../xy_config_panel/dimension_editor.tsx | 2 +- .../reference_line_config_panel/icon_set.ts | 67 +++++++ .../reference_line_config_panel/index.tsx | 8 + .../reference_line_panel.tsx | 29 +-- .../xy_config_panel/shared/icon_select.tsx | 81 ++------ .../shared/marker_decoration_settings.tsx | 38 ++-- 25 files changed, 521 insertions(+), 404 deletions(-) delete mode 100644 src/plugins/chart_expressions/expression_xy/public/helpers/annotations_icon_set.tsx create mode 100644 src/plugins/event_annotation/common/constants.ts create mode 100644 x-pack/plugins/lens/public/xy_visualization/xy_config_panel/annotations_config_panel/annotations_panel.tsx create mode 100644 x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_config_panel/icon_set.ts create mode 100644 x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_config_panel/index.tsx rename x-pack/plugins/lens/public/xy_visualization/xy_config_panel/{ => reference_line_config_panel}/reference_line_panel.tsx (85%) diff --git a/src/plugins/chart_expressions/expression_xy/common/constants.ts b/src/plugins/chart_expressions/expression_xy/common/constants.ts index 6652f025a67e5..6856c8e95d545 100644 --- a/src/plugins/chart_expressions/expression_xy/common/constants.ts +++ b/src/plugins/chart_expressions/expression_xy/common/constants.ts @@ -114,3 +114,21 @@ export const ValueLabelModes = { INSIDE: 'inside', OUTSIDE: 'outside', } as const; + +export const AvailableReferenceLineIcons = { + EMPTY: 'empty', + ASTERISK: 'asterisk', + ALERT: 'alert', + BELL: 'bell', + BOLT: 'bolt', + BUG: 'bug', + CIRCLE: 'circle', + EDITOR_COMMENT: 'editorComment', + FLAG: 'flag', + HEART: 'heart', + MAP_MARKER: 'mapMarker', + PIN_FILLED: 'pinFilled', + STAR_EMPTY: 'starEmpty', + TAG: 'tag', + TRIANGLE: 'triangle', +} as const; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_y_axis_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_y_axis_config.ts index 253a5b4e63b8f..08e8cbf576cf5 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_y_axis_config.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_y_axis_config.ts @@ -8,7 +8,14 @@ import { i18n } from '@kbn/i18n'; import type { ExpressionFunctionDefinition } from '../../../../expressions/common'; -import { EXTENDED_Y_CONFIG, FillStyles, IconPositions, LineStyles, YAxisModes } from '../constants'; +import { + AvailableReferenceLineIcons, + EXTENDED_Y_CONFIG, + FillStyles, + IconPositions, + LineStyles, + YAxisModes, +} from '../constants'; import { ExtendedYConfig, ExtendedYConfigResult } from '../types'; export const extendedYAxisConfigFunction: ExpressionFunctionDefinition< @@ -64,6 +71,8 @@ export const extendedYAxisConfigFunction: ExpressionFunctionDefinition< help: i18n.translate('expressionXY.yConfig.icon.help', { defaultMessage: 'An optional icon used for reference lines', }), + options: [...Object.values(AvailableReferenceLineIcons)], + strict: true, }, iconPosition: { types: ['string'], diff --git a/src/plugins/chart_expressions/expression_xy/common/index.ts b/src/plugins/chart_expressions/expression_xy/common/index.ts index b6fb31b8daade..626832640c676 100755 --- a/src/plugins/chart_expressions/expression_xy/common/index.ts +++ b/src/plugins/chart_expressions/expression_xy/common/index.ts @@ -54,6 +54,7 @@ export type { FittingFunction, ExtendedYConfig, AxisExtentConfig, + CollectiveConfig, LegendConfigResult, AxesSettingsConfig, AnnotationLayerArgs, @@ -66,8 +67,10 @@ export type { ReferenceLineLayerArgs, LabelsOrientationConfig, CommonXYLayerConfigResult, + AvailableReferenceLineIcon, XYExtendedLayerConfigResult, AnnotationLayerConfigResult, + ExtendedAnnotationLayerArgs, ExtendedDataLayerConfigResult, LabelsOrientationConfigResult, CommonXYDataLayerConfigResult, diff --git a/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts b/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts index 0b56352bd2c84..da276e0b34279 100644 --- a/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts +++ b/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts @@ -40,6 +40,7 @@ import { EndValues, EXTENDED_ANNOTATION_LAYER, EXTENDED_Y_CONFIG, + AvailableReferenceLineIcons, } from '../constants'; export type EndValue = $Values; @@ -55,6 +56,7 @@ export type IconPosition = $Values; export type ValueLabelMode = $Values; export type AxisExtentMode = $Values; export type FittingFunction = $Values; +export type AvailableReferenceLineIcon = $Values; export interface AxesSettingsConfig { x: boolean; @@ -74,7 +76,7 @@ export interface AxisConfig { } export interface ExtendedYConfig extends YConfig { - icon?: string; + icon?: AvailableReferenceLineIcon; lineWidth?: number; lineStyle?: LineStyle; fill?: FillStyle; diff --git a/src/plugins/chart_expressions/expression_xy/common/types/expression_renderers.ts b/src/plugins/chart_expressions/expression_xy/common/types/expression_renderers.ts index e8201b1c5bfa7..04d7fb2a446d3 100644 --- a/src/plugins/chart_expressions/expression_xy/common/types/expression_renderers.ts +++ b/src/plugins/chart_expressions/expression_xy/common/types/expression_renderers.ts @@ -6,6 +6,8 @@ * Side Public License, v 1. */ +import { AnnotationTooltipFormatter } from '@elastic/charts'; +import { AvailableAnnotationIcon, EventAnnotationArgs } from '../../../../event_annotation/common'; import { XY_VIS_RENDERER } from '../constants'; import { XYProps } from './expression_functions'; @@ -18,3 +20,10 @@ export interface XYRender { as: typeof XY_VIS_RENDERER; value: XYChartProps; } + +export interface CollectiveConfig extends Omit { + roundedTimestamp: number; + axisMode: 'bottom'; + icon?: AvailableAnnotationIcon | string; + customTooltipDetails?: AnnotationTooltipFormatter | undefined; +} diff --git a/src/plugins/chart_expressions/expression_xy/public/components/annotations.tsx b/src/plugins/chart_expressions/expression_xy/public/components/annotations.tsx index 70803f8c507c0..50a6ea555c542 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/annotations.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/annotations.tsx @@ -26,7 +26,8 @@ import type { AnnotationLayerArgs, ExtendedAnnotationLayerArgs, CommonXYAnnotationLayerConfigResult, -} from '../../common/types'; + CollectiveConfig, +} from '../../common'; import { AnnotationIcon, hasIcon, Marker, MarkerBody } from '../helpers'; import { mapVerticalToHorizontalPlacement, LINES_MARKER_SIZE } from '../helpers'; @@ -47,12 +48,6 @@ export interface AnnotationsProps { isBarChart?: boolean; } -interface CollectiveConfig extends EventAnnotationArgs { - roundedTimestamp: number; - axisMode: 'bottom'; - customTooltipDetails?: AnnotationTooltipFormatter | undefined; -} - const groupVisibleConfigsByInterval = ( layers: Array, minInterval?: number, diff --git a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx index 9c42a4e6c270a..6da5e5e92b30d 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx @@ -2634,7 +2634,7 @@ describe('XYChart component', () => { annotations: [ { ...sampleStyledAnnotation, - icon: 'square', + icon: 'asterisk', color: 'blue', lineStyle: 'dotted', lineWidth: 10, diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/annotations.tsx b/src/plugins/chart_expressions/expression_xy/public/helpers/annotations.tsx index 9dd67b38b20a9..4458771836b6b 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/annotations.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/annotations.tsx @@ -9,19 +9,27 @@ import React from 'react'; import { Position } from '@elastic/charts'; import { EuiFlexGroup, EuiIcon, EuiIconProps, EuiText } from '@elastic/eui'; import classnames from 'classnames'; -import type { IconPosition, YAxisMode, ExtendedYConfig } from '../../common/types'; +import type { + IconPosition, + YAxisMode, + ExtendedYConfig, + CollectiveConfig, +} from '../../common/types'; import { getBaseIconPlacement } from '../components'; -import { hasIcon } from './icon'; -import { annotationsIconSet } from './annotations_icon_set'; +import { hasIcon, iconSet } from './icon'; export const LINES_MARKER_SIZE = 20; -// Note: it does not take into consideration whether the reference line is in view or not +type PartialExtendedYConfig = Pick< + ExtendedYConfig, + 'axisMode' | 'icon' | 'iconPosition' | 'textVisibility' +>; + +type PartialCollectiveConfig = Pick; +// Note: it does not take into consideration whether the reference line is in view or not export const getLinesCausedPaddings = ( - visualConfigs: Array< - Pick | undefined - >, + visualConfigs: Array, axesMap: Record<'left' | 'right', unknown> ) => { // collect all paddings for the 4 axis: if any text is detected double it. @@ -31,7 +39,9 @@ export const getLinesCausedPaddings = ( if (!config) { return; } - const { axisMode, icon, iconPosition, textVisibility } = config; + const { axisMode, icon, textVisibility } = config; + const iconPosition = (config as PartialExtendedYConfig).iconPosition ?? undefined; + if (axisMode && (hasIcon(icon) || textVisibility)) { const placement = getBaseIconPlacement(iconPosition, axesMap, axisMode); paddings[placement] = Math.max( @@ -139,7 +149,7 @@ export const AnnotationIcon = ({ if (isNumericalString(type)) { return ; } - const iconConfig = annotationsIconSet.find((i) => i.value === type); + const iconConfig = iconSet.find((i) => i.value === type); if (!iconConfig) { return null; } diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/annotations_icon_set.tsx b/src/plugins/chart_expressions/expression_xy/public/helpers/annotations_icon_set.tsx deleted file mode 100644 index 99b4648e4d556..0000000000000 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/annotations_icon_set.tsx +++ /dev/null @@ -1,101 +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 { i18n } from '@kbn/i18n'; -import { TriangleIcon, CircleIcon } from '../icons'; - -export const annotationsIconSet = [ - { - value: 'asterisk', - label: i18n.translate('expressionXY.xyChart.iconSelect.asteriskIconLabel', { - defaultMessage: 'Asterisk', - }), - }, - { - value: 'alert', - label: i18n.translate('expressionXY.xyChart.iconSelect.alertIconLabel', { - defaultMessage: 'Alert', - }), - }, - { - value: 'bell', - label: i18n.translate('expressionXY.xyChart.iconSelect.bellIconLabel', { - defaultMessage: 'Bell', - }), - }, - { - value: 'bolt', - label: i18n.translate('expressionXY.xyChart.iconSelect.boltIconLabel', { - defaultMessage: 'Bolt', - }), - }, - { - value: 'bug', - label: i18n.translate('expressionXY.xyChart.iconSelect.bugIconLabel', { - defaultMessage: 'Bug', - }), - }, - { - value: 'circle', - label: i18n.translate('expressionXY.xyChart.iconSelect.circleIconLabel', { - defaultMessage: 'Circle', - }), - icon: CircleIcon, - canFill: true, - }, - - { - value: 'editorComment', - label: i18n.translate('expressionXY.xyChart.iconSelect.commentIconLabel', { - defaultMessage: 'Comment', - }), - }, - { - value: 'flag', - label: i18n.translate('expressionXY.xyChart.iconSelect.flagIconLabel', { - defaultMessage: 'Flag', - }), - }, - { - value: 'heart', - label: i18n.translate('expressionXY.xyChart.iconSelect.heartLabel', { - defaultMessage: 'Heart', - }), - }, - { - value: 'mapMarker', - label: i18n.translate('expressionXY.xyChart.iconSelect.mapMarkerLabel', { - defaultMessage: 'Map Marker', - }), - }, - { - value: 'pinFilled', - label: i18n.translate('expressionXY.xyChart.iconSelect.mapPinLabel', { - defaultMessage: 'Map Pin', - }), - }, - { - value: 'starEmpty', - label: i18n.translate('expressionXY.xyChart.iconSelect.starLabel', { defaultMessage: 'Star' }), - }, - { - value: 'tag', - label: i18n.translate('expressionXY.xyChart.iconSelect.tagIconLabel', { - defaultMessage: 'Tag', - }), - }, - { - value: 'triangle', - label: i18n.translate('expressionXY.xyChart.iconSelect.triangleIconLabel', { - defaultMessage: 'Triangle', - }), - icon: TriangleIcon, - shouldRotate: true, - canFill: true, - }, -]; diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/icon.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/icon.ts index 57e285a07232f..8b4113b3ada11 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/icon.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/icon.ts @@ -6,6 +6,107 @@ * Side Public License, v 1. */ +import { i18n } from '@kbn/i18n'; +import { TriangleIcon, CircleIcon } from '../icons'; +import { AvailableReferenceLineIcons } from '../../common/constants'; + export function hasIcon(icon: string | undefined): icon is string { return icon != null && icon !== 'empty'; } + +export const iconSet = [ + { + value: AvailableReferenceLineIcons.EMPTY, + label: i18n.translate('expressionXY.xyChart.iconSelect.noIconLabel', { + defaultMessage: 'None', + }), + }, + { + value: AvailableReferenceLineIcons.ASTERISK, + label: i18n.translate('expressionXY.xyChart.iconSelect.asteriskIconLabel', { + defaultMessage: 'Asterisk', + }), + }, + { + value: AvailableReferenceLineIcons.ALERT, + label: i18n.translate('expressionXY.xyChart.iconSelect.alertIconLabel', { + defaultMessage: 'Alert', + }), + }, + { + value: AvailableReferenceLineIcons.BELL, + label: i18n.translate('expressionXY.xyChart.iconSelect.bellIconLabel', { + defaultMessage: 'Bell', + }), + }, + { + value: AvailableReferenceLineIcons.BOLT, + label: i18n.translate('expressionXY.xyChart.iconSelect.boltIconLabel', { + defaultMessage: 'Bolt', + }), + }, + { + value: AvailableReferenceLineIcons.BUG, + label: i18n.translate('expressionXY.xyChart.iconSelect.bugIconLabel', { + defaultMessage: 'Bug', + }), + }, + { + value: AvailableReferenceLineIcons.CIRCLE, + label: i18n.translate('expressionXY.xyChart.iconSelect.circleIconLabel', { + defaultMessage: 'Circle', + }), + icon: CircleIcon, + canFill: true, + }, + + { + value: AvailableReferenceLineIcons.EDITOR_COMMENT, + label: i18n.translate('expressionXY.xyChart.iconSelect.commentIconLabel', { + defaultMessage: 'Comment', + }), + }, + { + value: AvailableReferenceLineIcons.FLAG, + label: i18n.translate('expressionXY.xyChart.iconSelect.flagIconLabel', { + defaultMessage: 'Flag', + }), + }, + { + value: AvailableReferenceLineIcons.HEART, + label: i18n.translate('expressionXY.xyChart.iconSelect.heartLabel', { + defaultMessage: 'Heart', + }), + }, + { + value: AvailableReferenceLineIcons.MAP_MARKER, + label: i18n.translate('expressionXY.xyChart.iconSelect.mapMarkerLabel', { + defaultMessage: 'Map Marker', + }), + }, + { + value: AvailableReferenceLineIcons.PIN_FILLED, + label: i18n.translate('expressionXY.xyChart.iconSelect.mapPinLabel', { + defaultMessage: 'Map Pin', + }), + }, + { + value: AvailableReferenceLineIcons.STAR_EMPTY, + label: i18n.translate('expressionXY.xyChart.iconSelect.starLabel', { defaultMessage: 'Star' }), + }, + { + value: AvailableReferenceLineIcons.TAG, + label: i18n.translate('expressionXY.xyChart.iconSelect.tagIconLabel', { + defaultMessage: 'Tag', + }), + }, + { + value: AvailableReferenceLineIcons.TRIANGLE, + label: i18n.translate('expressionXY.xyChart.iconSelect.triangleIconLabel', { + defaultMessage: 'Triangle', + }), + icon: TriangleIcon, + shouldRotate: true, + canFill: true, + }, +]; diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/index.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/index.ts index 24304132500ec..38aa6257b5cd3 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/index.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/index.ts @@ -15,6 +15,5 @@ export * from './axes_configuration'; export * from './reference_lines'; export * from './icon'; export * from './color_assignment'; -export * from './annotations_icon_set'; export * from './annotations'; export * from './data_layers'; diff --git a/src/plugins/event_annotation/common/constants.ts b/src/plugins/event_annotation/common/constants.ts new file mode 100644 index 0000000000000..3338450b64ce5 --- /dev/null +++ b/src/plugins/event_annotation/common/constants.ts @@ -0,0 +1,24 @@ +/* + * 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 const AvailableAnnotationIcons = { + ASTERISK: 'asterisk', + ALERT: 'alert', + BELL: 'bell', + BOLT: 'bolt', + BUG: 'bug', + CIRCLE: 'circle', + EDITOR_COMMENT: 'editorComment', + FLAG: 'flag', + HEART: 'heart', + MAP_MARKER: 'mapMarker', + PIN_FILLED: 'pinFilled', + STAR_EMPTY: 'starEmpty', + TAG: 'tag', + TRIANGLE: 'triangle', +} as const; diff --git a/src/plugins/event_annotation/common/index.ts b/src/plugins/event_annotation/common/index.ts index 332fa19150aad..f3421582d01b8 100644 --- a/src/plugins/event_annotation/common/index.ts +++ b/src/plugins/event_annotation/common/index.ts @@ -10,4 +10,4 @@ export type { EventAnnotationArgs, EventAnnotationOutput } from './manual_event_ export { manualEventAnnotation } from './manual_event_annotation'; export { eventAnnotationGroup } from './event_annotation_group'; export type { EventAnnotationGroupArgs } from './event_annotation_group'; -export type { EventAnnotationConfig } from './types'; +export type { EventAnnotationConfig, AvailableAnnotationIcon } from './types'; diff --git a/src/plugins/event_annotation/common/manual_event_annotation/index.ts b/src/plugins/event_annotation/common/manual_event_annotation/index.ts index 108df93b34180..62a735759471a 100644 --- a/src/plugins/event_annotation/common/manual_event_annotation/index.ts +++ b/src/plugins/event_annotation/common/manual_event_annotation/index.ts @@ -9,6 +9,8 @@ import type { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; import { i18n } from '@kbn/i18n'; import type { EventAnnotationArgs, EventAnnotationOutput } from './types'; +import { AvailableAnnotationIcons } from '../constants'; + export const manualEventAnnotation: ExpressionFunctionDefinition< 'manual_event_annotation', null, @@ -59,6 +61,8 @@ export const manualEventAnnotation: ExpressionFunctionDefinition< help: i18n.translate('eventAnnotation.manualAnnotation.args.icon', { defaultMessage: 'An optional icon used for annotation lines', }), + options: [...Object.values(AvailableAnnotationIcons)], + strict: true, }, textVisibility: { types: ['boolean'], @@ -73,7 +77,7 @@ export const manualEventAnnotation: ExpressionFunctionDefinition< }), }, }, - fn: function fn(input: unknown, args: EventAnnotationArgs) { + fn(input: unknown, args: EventAnnotationArgs) { return { type: 'manual_event_annotation', ...args, diff --git a/src/plugins/event_annotation/common/types.ts b/src/plugins/event_annotation/common/types.ts index 95275804d1d1f..6345472742047 100644 --- a/src/plugins/event_annotation/common/types.ts +++ b/src/plugins/event_annotation/common/types.ts @@ -6,14 +6,18 @@ * Side Public License, v 1. */ +import { $Values } from '@kbn/utility-types'; +import { AvailableAnnotationIcons } from './constants'; + export type LineStyle = 'solid' | 'dashed' | 'dotted'; export type AnnotationType = 'manual'; export type KeyType = 'point_in_time'; +export type AvailableAnnotationIcon = $Values; export interface StyleProps { label: string; color?: string; - icon?: string; + icon?: AvailableAnnotationIcon; lineWidth?: number; lineStyle?: LineStyle; textVisibility?: boolean; diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx index edc076ff257e4..b566e878773c4 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx @@ -74,7 +74,7 @@ import { } from './visualization_helpers'; import { groupAxesByType } from './axes_configuration'; import { XYState } from './types'; -import { ReferenceLinePanel } from './xy_config_panel/reference_line_panel'; +import { ReferenceLinePanel } from './xy_config_panel/reference_line_config_panel'; import { AnnotationsPanel } from './xy_config_panel/annotations_config_panel'; import { DimensionTrigger } from '../shared_components/dimension_trigger'; import { defaultAnnotationLabel } from './annotations/helpers'; diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/annotations_config_panel/annotations_panel.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/annotations_config_panel/annotations_panel.tsx new file mode 100644 index 0000000000000..b683548cd2517 --- /dev/null +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/annotations_config_panel/annotations_panel.tsx @@ -0,0 +1,187 @@ +/* + * 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, { useCallback } from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiDatePicker, EuiFormRow, EuiSwitch, EuiSwitchEvent } from '@elastic/eui'; +import type { PaletteRegistry } from 'src/plugins/charts/public'; +import moment from 'moment'; +import { EventAnnotationConfig } from 'src/plugins/event_annotation/common/types'; +import type { VisualizationDimensionEditorProps } from '../../../types'; +import { State, XYState, XYAnnotationLayerConfig } from '../../types'; +import { FormatFactory } from '../../../../common'; +import { DimensionEditorSection, NameInput, useDebouncedValue } from '../../../shared_components'; +import { isHorizontalChart } from '../../state_helpers'; +import { defaultAnnotationLabel } from '../../annotations/helpers'; +import { ColorPicker } from '../color_picker'; +import { IconSelectSetting, TextDecorationSetting } from '../shared/marker_decoration_settings'; +import { LineStyleSettings } from '../shared/line_style_settings'; +import { updateLayer } from '..'; +import { annotationsIconSet } from './icon_set'; + +export const AnnotationsPanel = ( + props: VisualizationDimensionEditorProps & { + formatFactory: FormatFactory; + paletteService: PaletteRegistry; + } +) => { + const { state, setState, layerId, accessor } = props; + const isHorizontal = isHorizontalChart(state.layers); + + const { inputValue: localState, handleInputChange: setLocalState } = useDebouncedValue({ + value: state, + onChange: setState, + }); + + const index = localState.layers.findIndex((l) => l.layerId === layerId); + const localLayer = localState.layers.find( + (l) => l.layerId === layerId + ) as XYAnnotationLayerConfig; + + const currentAnnotations = localLayer.annotations?.find((c) => c.id === accessor); + + const setAnnotations = useCallback( + (annotations: Partial | undefined) => { + if (annotations == null) { + return; + } + const newConfigs = [...(localLayer.annotations || [])]; + const existingIndex = newConfigs.findIndex((c) => c.id === accessor); + if (existingIndex !== -1) { + newConfigs[existingIndex] = { ...newConfigs[existingIndex], ...annotations }; + } else { + return; // that should never happen because annotations are created before annotations panel is opened + } + setLocalState(updateLayer(localState, { ...localLayer, annotations: newConfigs }, index)); + }, + [accessor, index, localState, localLayer, setLocalState] + ); + + return ( + <> + + { + if (date) { + setAnnotations({ + key: { + ...(currentAnnotations?.key || { type: 'point_in_time' }), + timestamp: date.toISOString(), + }, + }); + } + }} + label={i18n.translate('xpack.lens.xyChart.annotationDate', { + defaultMessage: 'Annotation date', + })} + /> + + + { + setAnnotations({ label: value }); + }} + /> + + + + + setAnnotations({ isHidden: ev.target.checked })} + /> + + + ); +}; + +const ConfigPanelDatePicker = ({ + value, + label, + onChange, +}: { + value: moment.Moment; + label: string; + onChange: (val: moment.Moment | null) => void; +}) => { + return ( + + + + ); +}; + +const ConfigPanelHideSwitch = ({ + value, + onChange, +}: { + value: boolean; + onChange: (event: EuiSwitchEvent) => void; +}) => { + return ( + + + + ); +}; diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/annotations_config_panel/icon_set.ts b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/annotations_config_panel/icon_set.ts index 87813ec12913e..70e121ee0a288 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/annotations_config_panel/icon_set.ts +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/annotations_config_panel/icon_set.ts @@ -4,10 +4,13 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ + import { i18n } from '@kbn/i18n'; +import { AvailableAnnotationIcon } from '../../../../../../../src/plugins/event_annotation/common'; import { IconTriangle, IconCircle } from '../../../assets/annotation_icons'; +import { IconSet } from '../shared/icon_select'; -export const annotationsIconSet = [ +export const annotationsIconSet: IconSet = [ { value: 'asterisk', label: i18n.translate('xpack.lens.xyChart.iconSelect.asteriskIconLabel', { diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/annotations_config_panel/index.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/annotations_config_panel/index.tsx index b683548cd2517..bd63354936703 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/annotations_config_panel/index.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/annotations_config_panel/index.tsx @@ -5,183 +5,4 @@ * 2.0. */ -import React, { useCallback } from 'react'; -import { i18n } from '@kbn/i18n'; -import { EuiDatePicker, EuiFormRow, EuiSwitch, EuiSwitchEvent } from '@elastic/eui'; -import type { PaletteRegistry } from 'src/plugins/charts/public'; -import moment from 'moment'; -import { EventAnnotationConfig } from 'src/plugins/event_annotation/common/types'; -import type { VisualizationDimensionEditorProps } from '../../../types'; -import { State, XYState, XYAnnotationLayerConfig } from '../../types'; -import { FormatFactory } from '../../../../common'; -import { DimensionEditorSection, NameInput, useDebouncedValue } from '../../../shared_components'; -import { isHorizontalChart } from '../../state_helpers'; -import { defaultAnnotationLabel } from '../../annotations/helpers'; -import { ColorPicker } from '../color_picker'; -import { IconSelectSetting, TextDecorationSetting } from '../shared/marker_decoration_settings'; -import { LineStyleSettings } from '../shared/line_style_settings'; -import { updateLayer } from '..'; -import { annotationsIconSet } from './icon_set'; - -export const AnnotationsPanel = ( - props: VisualizationDimensionEditorProps & { - formatFactory: FormatFactory; - paletteService: PaletteRegistry; - } -) => { - const { state, setState, layerId, accessor } = props; - const isHorizontal = isHorizontalChart(state.layers); - - const { inputValue: localState, handleInputChange: setLocalState } = useDebouncedValue({ - value: state, - onChange: setState, - }); - - const index = localState.layers.findIndex((l) => l.layerId === layerId); - const localLayer = localState.layers.find( - (l) => l.layerId === layerId - ) as XYAnnotationLayerConfig; - - const currentAnnotations = localLayer.annotations?.find((c) => c.id === accessor); - - const setAnnotations = useCallback( - (annotations: Partial | undefined) => { - if (annotations == null) { - return; - } - const newConfigs = [...(localLayer.annotations || [])]; - const existingIndex = newConfigs.findIndex((c) => c.id === accessor); - if (existingIndex !== -1) { - newConfigs[existingIndex] = { ...newConfigs[existingIndex], ...annotations }; - } else { - return; // that should never happen because annotations are created before annotations panel is opened - } - setLocalState(updateLayer(localState, { ...localLayer, annotations: newConfigs }, index)); - }, - [accessor, index, localState, localLayer, setLocalState] - ); - - return ( - <> - - { - if (date) { - setAnnotations({ - key: { - ...(currentAnnotations?.key || { type: 'point_in_time' }), - timestamp: date.toISOString(), - }, - }); - } - }} - label={i18n.translate('xpack.lens.xyChart.annotationDate', { - defaultMessage: 'Annotation date', - })} - /> - - - { - setAnnotations({ label: value }); - }} - /> - - - - - setAnnotations({ isHidden: ev.target.checked })} - /> - - - ); -}; - -const ConfigPanelDatePicker = ({ - value, - label, - onChange, -}: { - value: moment.Moment; - label: string; - onChange: (val: moment.Moment | null) => void; -}) => { - return ( - - - - ); -}; - -const ConfigPanelHideSwitch = ({ - value, - onChange, -}: { - value: boolean; - onChange: (event: EuiSwitchEvent) => void; -}) => { - return ( - - - - ); -}; +export { AnnotationsPanel } from './annotations_panel'; diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/dimension_editor.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/dimension_editor.tsx index b1ff679840c5e..7bb0b00ffce32 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/dimension_editor.tsx @@ -20,7 +20,7 @@ import { isHorizontalChart } from '../state_helpers'; import { ColorPicker } from './color_picker'; import { PalettePicker, useDebouncedValue } from '../../shared_components'; import { isAnnotationsLayer, isReferenceLayer } from '../visualization_helpers'; -import { ReferenceLinePanel } from './reference_line_panel'; +import { ReferenceLinePanel } from './reference_line_config_panel'; import { AnnotationsPanel } from './annotations_config_panel'; type UnwrapArray = T extends Array ? P : T; diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_config_panel/icon_set.ts b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_config_panel/icon_set.ts new file mode 100644 index 0000000000000..e22ea13f802da --- /dev/null +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_config_panel/icon_set.ts @@ -0,0 +1,67 @@ +/* + * 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 { AvailableReferenceLineIcon } from '../../../../../../../src/plugins/chart_expressions/expression_xy/common'; +import { IconSet } from '../shared/icon_select'; + +export const referenceLineIconsSet: IconSet = [ + { + value: 'empty', + label: i18n.translate('xpack.lens.xyChart.iconSelect.noIconLabel', { + defaultMessage: 'None', + }), + }, + { + value: 'asterisk', + label: i18n.translate('xpack.lens.xyChart.iconSelect.asteriskIconLabel', { + defaultMessage: 'Asterisk', + }), + }, + { + value: 'bell', + label: i18n.translate('xpack.lens.xyChart.iconSelect.bellIconLabel', { + defaultMessage: 'Bell', + }), + }, + { + value: 'bolt', + label: i18n.translate('xpack.lens.xyChart.iconSelect.boltIconLabel', { + defaultMessage: 'Bolt', + }), + }, + { + value: 'bug', + label: i18n.translate('xpack.lens.xyChart.iconSelect.bugIconLabel', { + defaultMessage: 'Bug', + }), + }, + { + value: 'editorComment', + label: i18n.translate('xpack.lens.xyChart.iconSelect.commentIconLabel', { + defaultMessage: 'Comment', + }), + }, + { + value: 'alert', + label: i18n.translate('xpack.lens.xyChart.iconSelect.alertIconLabel', { + defaultMessage: 'Alert', + }), + }, + { + value: 'flag', + label: i18n.translate('xpack.lens.xyChart.iconSelect.flagIconLabel', { + defaultMessage: 'Flag', + }), + }, + { + value: 'tag', + label: i18n.translate('xpack.lens.xyChart.iconSelect.tagIconLabel', { + defaultMessage: 'Tag', + }), + }, +]; diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_config_panel/index.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_config_panel/index.tsx new file mode 100644 index 0000000000000..4297f7d35cd6c --- /dev/null +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_config_panel/index.tsx @@ -0,0 +1,8 @@ +/* + * 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. + */ + +export { ReferenceLinePanel } from './reference_line_panel'; diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_panel.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_config_panel/reference_line_panel.tsx similarity index 85% rename from x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_panel.tsx rename to x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_config_panel/reference_line_panel.tsx index 80ae14786fca4..28e65c017591f 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_panel.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_config_panel/reference_line_panel.tsx @@ -9,25 +9,26 @@ import React, { useCallback } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiButtonGroup, EuiFormRow } from '@elastic/eui'; import type { PaletteRegistry } from 'src/plugins/charts/public'; -import type { VisualizationDimensionEditorProps } from '../../types'; -import { State, XYState, XYReferenceLineLayerConfig } from '../types'; -import { FormatFactory } from '../../../common'; +import type { VisualizationDimensionEditorProps } from '../../../types'; +import { State, XYState, XYReferenceLineLayerConfig } from '../../types'; +import { FormatFactory } from '../../../../common'; import { FillStyle, ExtendedYConfig, -} from '../../../../../../src/plugins/chart_expressions/expression_xy/common'; +} from '../../../../../../../src/plugins/chart_expressions/expression_xy/common'; -import { ColorPicker } from './color_picker'; -import { updateLayer } from '.'; -import { useDebouncedValue } from '../../shared_components'; -import { idPrefix } from './dimension_editor'; -import { isHorizontalChart } from '../state_helpers'; +import { ColorPicker } from '../color_picker'; +import { updateLayer } from '..'; +import { useDebouncedValue } from '../../../shared_components'; +import { idPrefix } from '../dimension_editor'; +import { isHorizontalChart } from '../../state_helpers'; import { IconSelectSetting, MarkerDecorationPosition, TextDecorationSetting, -} from './shared/marker_decoration_settings'; -import { LineStyleSettings } from './shared/line_style_settings'; +} from '../shared/marker_decoration_settings'; +import { LineStyleSettings } from '../shared/line_style_settings'; +import { referenceLineIconsSet } from './icon_set'; export const ReferenceLinePanel = ( props: VisualizationDimensionEditorProps & { @@ -77,7 +78,11 @@ export const ReferenceLinePanel = ( return ( <> - + ; - -export const euiIconsSet = [ - { - value: 'empty', - label: i18n.translate('xpack.lens.xyChart.iconSelect.noIconLabel', { - defaultMessage: 'None', - }), - }, - { - value: 'asterisk', - label: i18n.translate('xpack.lens.xyChart.iconSelect.asteriskIconLabel', { - defaultMessage: 'Asterisk', - }), - }, - { - value: 'bell', - label: i18n.translate('xpack.lens.xyChart.iconSelect.bellIconLabel', { - defaultMessage: 'Bell', - }), - }, - { - value: 'bolt', - label: i18n.translate('xpack.lens.xyChart.iconSelect.boltIconLabel', { - defaultMessage: 'Bolt', - }), - }, - { - value: 'bug', - label: i18n.translate('xpack.lens.xyChart.iconSelect.bugIconLabel', { - defaultMessage: 'Bug', - }), - }, - { - value: 'editorComment', - label: i18n.translate('xpack.lens.xyChart.iconSelect.commentIconLabel', { - defaultMessage: 'Comment', - }), - }, - { - value: 'alert', - label: i18n.translate('xpack.lens.xyChart.iconSelect.alertIconLabel', { - defaultMessage: 'Alert', - }), - }, - { - value: 'flag', - label: i18n.translate('xpack.lens.xyChart.iconSelect.flagIconLabel', { - defaultMessage: 'Flag', - }), - }, - { - value: 'tag', - label: i18n.translate('xpack.lens.xyChart.iconSelect.tagIconLabel', { - defaultMessage: 'Tag', - }), - }, -]; +export type IconSet = Array<{ + value: T; + label: string; + icon?: T | IconType; + shouldRotate?: boolean; + canFill?: boolean; +}>; const IconView = (props: { value?: string; label: string; icon?: IconType }) => { if (!props.value) return null; @@ -84,15 +33,15 @@ const IconView = (props: { value?: string; label: string; icon?: IconType }) => ); }; -export const IconSelect = ({ +export function IconSelect({ value, onChange, - customIconSet = euiIconsSet, + customIconSet, }: { - value?: string; - onChange: (newIcon: string) => void; - customIconSet?: IconSet; -}) => { + value?: Icon; + onChange: (newIcon: Icon) => void; + customIconSet: IconSet; +}) { const selectedIcon = customIconSet.find((option) => value === option.value) || customIconSet.find((option) => option.value === 'empty')!; @@ -115,4 +64,4 @@ export const IconSelect = ({ } /> ); -}; +} diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/shared/marker_decoration_settings.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/shared/marker_decoration_settings.tsx index 26723abc55fad..41a92e1076f27 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/shared/marker_decoration_settings.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/shared/marker_decoration_settings.tsx @@ -74,22 +74,22 @@ function getIconPositionOptions({ isHorizontal, axisMode }: LabelConfigurationOp ]; } -interface MarkerDecorationConfig { +export interface MarkerDecorationConfig { axisMode?: YAxisMode; - icon?: string; + icon?: T; iconPosition?: IconPosition; textVisibility?: boolean; } -export const TextDecorationSetting = ({ +export function TextDecorationSetting({ currentConfig, setConfig, customIconSet, }: { - currentConfig?: MarkerDecorationConfig; - setConfig: (config: MarkerDecorationConfig) => void; - customIconSet?: IconSet; -}) => { + currentConfig?: MarkerDecorationConfig; + setConfig: (config: MarkerDecorationConfig) => void; + customIconSet?: IconSet; +}) { return ( ); -}; +} -export const IconSelectSetting = ({ +export function IconSelectSetting({ currentConfig, setConfig, customIconSet, }: { - currentConfig?: MarkerDecorationConfig; - setConfig: (config: MarkerDecorationConfig) => void; - customIconSet?: IconSet; -}) => { + currentConfig?: MarkerDecorationConfig; + setConfig: (config: MarkerDecorationConfig) => void; + customIconSet: IconSet; +}) { return ( ); -}; +} -export const MarkerDecorationPosition = ({ +export function MarkerDecorationPosition({ currentConfig, setConfig, isHorizontal, }: { - currentConfig?: MarkerDecorationConfig; - setConfig: (config: MarkerDecorationConfig) => void; + currentConfig?: MarkerDecorationConfig; + setConfig: (config: MarkerDecorationConfig) => void; isHorizontal: boolean; -}) => { +}) { return ( <> {hasIcon(currentConfig?.icon) || currentConfig?.textVisibility ? ( @@ -213,4 +213,4 @@ export const MarkerDecorationPosition = ({ ) : null} ); -}; +} From c17026f01a51cd30064f94d7fce71fd223220db0 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Tue, 12 Apr 2022 15:48:28 +0300 Subject: [PATCH 096/133] axis extent validation added. --- .../axis_extent_config.ts | 21 +++++++- .../common/expression_functions/xy_vis.ts | 32 ++++++++++- .../public/components/xy_chart.tsx | 53 ++++++++----------- .../public/helpers/axes_configuration.ts | 14 ----- 4 files changed, 72 insertions(+), 48 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/axis_extent_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/axis_extent_config.ts index c5cf89a4663c9..b12f6a5afdea9 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/axis_extent_config.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/axis_extent_config.ts @@ -11,6 +11,13 @@ import type { ExpressionFunctionDefinition } from '../../../../expressions/commo import { AxisExtentConfig, AxisExtentConfigResult } from '../types'; import { AxisExtentModes, AXIS_EXTENT_CONFIG } from '../constants'; +const errors = { + upperBoundLowerOrEqualToLowerBoundError: () => + i18n.translate('expressionXY.reusable.function.axisExtentConfig.errors.emptyUpperBound', { + defaultMessage: 'Upper bound should be greater than lower bound, if custom mode is enabled.', + }), +}; + export const axisExtentConfigFunction: ExpressionFunctionDefinition< typeof AXIS_EXTENT_CONFIG, null, @@ -27,10 +34,12 @@ export const axisExtentConfigFunction: ExpressionFunctionDefinition< args: { mode: { types: ['string'], - options: [...Object.values(AxisExtentModes)], help: i18n.translate('expressionXY.axisExtentConfig.extentMode.help', { defaultMessage: 'The extent mode', }), + options: [...Object.values(AxisExtentModes)], + strict: true, + default: AxisExtentModes.FULL, }, lowerBound: { types: ['number'], @@ -46,6 +55,16 @@ export const axisExtentConfigFunction: ExpressionFunctionDefinition< }, }, fn(input, args) { + if (args.mode === AxisExtentModes.CUSTOM) { + if ( + args.lowerBound !== undefined && + args.upperBound !== undefined && + args.lowerBound >= args.upperBound + ) { + throw new Error(errors.upperBoundLowerOrEqualToLowerBoundError()); + } + } + return { type: AXIS_EXTENT_CONFIG, ...args, diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts index a50d0973b763d..14e49ba4b92af 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts @@ -8,7 +8,7 @@ import { i18n } from '@kbn/i18n'; import type { ExpressionFunctionDefinition, Datatable } from '../../../../expressions'; -import { XYArgs, XYLayerConfigResult, XYRender } from '../types'; +import { AxisExtentConfigResult, XYArgs, XYLayerConfigResult, XYRender } from '../types'; import { XY_VIS, DATA_LAYER, @@ -26,10 +26,32 @@ import { EndValues, ANNOTATION_LAYER, LayerTypes, + AxisExtentModes, } from '../constants'; import { Dimension, prepareLogTable } from '../../../../visualizations/common/utils'; import { getLayerDimensions } from '../utils'; +const errors = { + extendBoundsAreInvalidError: () => + i18n.translate('expressionXY.reusable.function.xyVis.errors.extendBoundsAreInvalidError', { + defaultMessage: + 'For area and bar modes, and custom extent mode, the lower bound should be less or greater than 0 and the upper bound - be greater or equal than 0', + }), +}; + +const validateExtent = (extent: AxisExtentConfigResult, hasBarOrArea: boolean) => { + const isValidLowerBound = + extent.lowerBound === undefined || (extent.lowerBound !== undefined && extent.lowerBound <= 0); + const isValidUpperBound = + extent.upperBound === undefined || (extent.upperBound !== undefined && extent.upperBound >= 0); + + const areValidBounds = isValidLowerBound && isValidUpperBound; + + if (hasBarOrArea && extent.mode === AxisExtentModes.CUSTOM && !areValidBounds) { + throw new Error(errors.extendBoundsAreInvalidError()); + } +}; + export const xyVisFunction: ExpressionFunctionDefinition< typeof XY_VIS, Datatable, @@ -216,6 +238,14 @@ export const xyVisFunction: ExpressionFunctionDefinition< handlers.inspectorAdapters.tables.logDatatable('default', logTable); } + const hasBarOrArea = + dataLayers.filter( + ({ seriesType }) => seriesType.includes('bar') || seriesType.includes('area') + ).length > 0; + + validateExtent(args.yLeftExtent, hasBarOrArea); + validateExtent(args.yRightExtent, hasBarOrArea); + return { type: 'render', as: XY_VIS_RENDERER, diff --git a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx index 89b9c271445d5..f671cf75dc439 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx @@ -26,9 +26,7 @@ import { AxisStyle, } from '@elastic/charts'; import { IconType } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { PaletteRegistry, SeriesLayer } from '@kbn/coloring'; -import type { Datatable, DatatableRow, DatatableColumn } from '../../../../expressions/public'; +import { PaletteRegistry } from '@kbn/coloring'; import { RenderMode } from '../../../../expressions/common'; import { EmptyPlaceholder } from '../../../../../plugins/charts/public'; import type { FilterEvent, BrushEvent, FormatFactory } from '../types'; @@ -53,7 +51,6 @@ import { isDataLayer, getAxesConfiguration, GroupsConfiguration, - validateExtent, computeOverallDataDomain, getLinesCausedPaddings, } from '../helpers'; @@ -314,36 +311,28 @@ export function XYChart({ let min: number = NaN; let max: number = NaN; - if (extent.mode === 'custom') { - const { inclusiveZeroError, boundaryError } = validateExtent(hasBarOrArea, extent); - if (!inclusiveZeroError && !boundaryError) { - min = extent.lowerBound ?? NaN; - max = extent.upperBound ?? NaN; - } - } else { - const axisHasReferenceLine = referenceLineLayers.some(({ yConfig }) => - yConfig?.some(({ axisMode }) => axisMode === axis.groupId) + const axisHasReferenceLine = referenceLineLayers.some(({ yConfig }) => + yConfig?.some(({ axisMode }) => axisMode === axis.groupId) + ); + if (!fit && axisHasReferenceLine) { + // Remove this once the chart will support automatic annotation fit for other type of charts + const { min: computedMin, max: computedMax } = computeOverallDataDomain( + layers, + axis.series.map(({ accessor }) => accessor) ); - if (!fit && axisHasReferenceLine) { - // Remove this once the chart will support automatic annotation fit for other type of charts - const { min: computedMin, max: computedMax } = computeOverallDataDomain( - layers, - axis.series.map(({ accessor }) => accessor) - ); - if (computedMin != null && computedMax != null) { - max = Math.max(computedMax, max || 0); - min = Math.min(computedMin, min || 0); - } - for (const { yConfig, table } of referenceLineLayers) { - for (const { axisMode, forAccessor } of yConfig || []) { - if (axis.groupId === axisMode) { - for (const row of table.rows) { - const value = row[forAccessor]; - // keep the 0 in view - max = Math.max(value, max || 0, 0); - min = Math.min(value, min || 0, 0); - } + if (computedMin != null && computedMax != null) { + max = Math.max(computedMax, max || 0); + min = Math.min(computedMin, min || 0); + } + for (const { yConfig, table } of referenceLineLayers) { + for (const { axisMode, forAccessor } of yConfig || []) { + if (axis.groupId === axisMode) { + for (const row of table.rows) { + const value = row[forAccessor]; + // keep the 0 in view + max = Math.max(value, max || 0, 0); + min = Math.min(value, min || 0, 0); } } } diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/axes_configuration.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/axes_configuration.ts index c5a529e7181f7..fdc12609307c1 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/axes_configuration.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/axes_configuration.ts @@ -143,17 +143,3 @@ export function getAxesConfiguration( return axisGroups; } - -export function validateExtent(hasBarOrArea: boolean, extent?: AxisExtentConfig) { - const inclusiveZeroError = - extent && - hasBarOrArea && - ((extent.lowerBound !== undefined && extent.lowerBound > 0) || - (extent.upperBound !== undefined && extent.upperBound) < 0); - const boundaryError = - extent && - extent.lowerBound !== undefined && - extent.upperBound !== undefined && - extent.upperBound <= extent.lowerBound; - return { inclusiveZeroError, boundaryError }; -} From 17ea44f2e6d2f4a471ca2fdb46614a7730ec3d28 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Tue, 12 Apr 2022 17:09:17 +0300 Subject: [PATCH 097/133] Added checks to the legend config. --- .../expression_functions/legend_config.ts | 56 ++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/legend_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/legend_config.ts index ba65c8aee161f..0ef5381c161d0 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/legend_config.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/legend_config.ts @@ -8,10 +8,43 @@ import { HorizontalAlignment, Position, VerticalAlignment } from '@elastic/charts'; import { i18n } from '@kbn/i18n'; -import type { ExpressionFunctionDefinition } from '../../../../../../src/plugins/expressions/common'; +import type { ExpressionFunctionDefinition } from '../../../../expressions/common'; import { LEGEND_CONFIG } from '../constants'; import { LegendConfig, LegendConfigResult } from '../types'; +const errors = { + positionUsageWithIsInsideError: () => + i18n.translate( + 'expressionXY.reusable.function.legendConfig.errors.positionUsageWithIsInsideError', + { + defaultMessage: + '`position` argument is not applied if `isInside = true`. Please, use `horizontalAlignment` and `verticalAlignment` arguments instead.', + } + ), + alignmentUsageWithFalsyIsInsideError: () => + i18n.translate( + 'expressionXY.reusable.function.legendConfig.errors.alignmentUsageWithFalsyIsInsideError', + { + defaultMessage: + '`horizontalAlignment` and `verticalAlignment` arguments are not applied if `isInside = false`. Please, use the `position` argument instead.', + } + ), + floatingColumnsWithFalsyIsInsideError: () => + i18n.translate( + 'expressionXY.reusable.function.legendConfig.errors.floatingColumnsWithFalsyIsInsideError', + { + defaultMessage: '`floatingColumns` arguments are not applied if `isInside = false`.', + } + ), + legendSizeWithFalsyIsInsideError: () => + i18n.translate( + 'expressionXY.reusable.function.legendConfig.errors.legendSizeWithFalsyIsInsideError', + { + defaultMessage: '`legendSize` argument is not applied if `isInside = false`.', + } + ), +}; + export const legendConfigFunction: ExpressionFunctionDefinition< typeof LEGEND_CONFIG, null, @@ -31,6 +64,7 @@ export const legendConfigFunction: ExpressionFunctionDefinition< help: i18n.translate('expressionXY.legendConfig.isVisible.help', { defaultMessage: 'Specifies whether or not the legend is visible.', }), + default: true, }, position: { types: ['string'], @@ -97,6 +131,26 @@ export const legendConfigFunction: ExpressionFunctionDefinition< }, }, fn(input, args) { + if (args.isInside) { + if (args.position) { + throw new Error(errors.positionUsageWithIsInsideError()); + } + + if (args.legendSize !== undefined) { + throw new Error(errors.legendSizeWithFalsyIsInsideError()); + } + } + + if (!args.isInside) { + if (args.verticalAlignment || args.horizontalAlignment) { + throw new Error(errors.alignmentUsageWithFalsyIsInsideError()); + } + + if (args.floatingColumns !== undefined) { + throw new Error(errors.floatingColumnsWithFalsyIsInsideError()); + } + } + return { type: LEGEND_CONFIG, ...args, From 31114673f5a2b4ab7401de378bc6754a6ea0930d Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Tue, 12 Apr 2022 18:36:06 +0300 Subject: [PATCH 098/133] fillOpacity usage validation is added. --- .../common/expression_functions/xy_vis.ts | 18 ++++++++++++------ .../public/helpers/fitting_functions.ts | 5 +++-- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts index 14e49ba4b92af..59b66ffc918db 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts @@ -37,6 +37,10 @@ const errors = { defaultMessage: 'For area and bar modes, and custom extent mode, the lower bound should be less or greater than 0 and the upper bound - be greater or equal than 0', }), + notUsedFillOpacityError: () => + i18n.translate('expressionXY.reusable.function.xyVis.errors.notUsedFillOpacityError', { + defaultMessage: '`fillOpacity` argument is applicable only for area charts.', + }), }; const validateExtent = (extent: AxisExtentConfigResult, hasBarOrArea: boolean) => { @@ -238,13 +242,15 @@ export const xyVisFunction: ExpressionFunctionDefinition< handlers.inspectorAdapters.tables.logDatatable('default', logTable); } - const hasBarOrArea = - dataLayers.filter( - ({ seriesType }) => seriesType.includes('bar') || seriesType.includes('area') - ).length > 0; + const hasBar = dataLayers.filter(({ seriesType }) => seriesType.includes('bar')).length > 0; + const hasArea = dataLayers.filter(({ seriesType }) => seriesType.includes('area')).length > 0; + + validateExtent(args.yLeftExtent, hasBar || hasArea); + validateExtent(args.yRightExtent, hasBar || hasArea); - validateExtent(args.yLeftExtent, hasBarOrArea); - validateExtent(args.yRightExtent, hasBarOrArea); + if (!hasArea && args.fillOpacity !== undefined) { + throw new Error(errors.notUsedFillOpacityError()); + } return { type: 'render', diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/fitting_functions.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/fitting_functions.ts index 43d5ad9b4c19f..4c26caf59d8d3 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/fitting_functions.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/fitting_functions.ts @@ -8,6 +8,7 @@ import { Fit } from '@elastic/charts'; import { EndValue, FittingFunction } from '../../common'; +import { EndValues } from '../../common/constants'; export function getFitEnum(fittingFunction?: FittingFunction | EndValue) { if (fittingFunction) { @@ -17,10 +18,10 @@ export function getFitEnum(fittingFunction?: FittingFunction | EndValue) { } export function getEndValue(endValue?: EndValue) { - if (endValue === 'Nearest') { + if (endValue === EndValues.NEAREST) { return Fit[endValue]; } - if (endValue === 'Zero') { + if (endValue === EndValues.ZERO) { return 0; } return undefined; From bd282ebd2bc5e471d50d8ee9d6ab98ea04fca37e Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Tue, 12 Apr 2022 19:38:15 +0300 Subject: [PATCH 099/133] Fixed valueLabels argument options. Removed not used. Added validation for usage. --- .../expression_xy/common/constants.ts | 3 +-- .../common/expression_functions/xy_vis.ts | 15 +++++++++++++++ .../public/components/data_layers.tsx | 4 ++-- .../expression_xy/public/components/xy_chart.tsx | 6 ++++-- 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/common/constants.ts b/src/plugins/chart_expressions/expression_xy/common/constants.ts index 6856c8e95d545..e12d5b9c4abda 100644 --- a/src/plugins/chart_expressions/expression_xy/common/constants.ts +++ b/src/plugins/chart_expressions/expression_xy/common/constants.ts @@ -111,8 +111,7 @@ export const XYCurveTypes = { export const ValueLabelModes = { HIDE: 'hide', - INSIDE: 'inside', - OUTSIDE: 'outside', + SHOW: 'show', } as const; export const AvailableReferenceLineIcons = { diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts index 59b66ffc918db..087deff314230 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts @@ -41,6 +41,11 @@ const errors = { i18n.translate('expressionXY.reusable.function.xyVis.errors.notUsedFillOpacityError', { defaultMessage: '`fillOpacity` argument is applicable only for area charts.', }), + valueLabelsForNotBarsOrHistogramBarsChartsError: () => + i18n.translate('expressionXY.reusable.function.xyVis.errors.notUsedFillOpacityError', { + defaultMessage: + '`valueLabels` argument is applicable only for bar charts, which are not histograms.', + }), }; const validateExtent = (extent: AxisExtentConfigResult, hasBarOrArea: boolean) => { @@ -136,6 +141,7 @@ export const xyVisFunction: ExpressionFunctionDefinition< defaultMessage: 'Value labels mode', }), strict: true, + default: ValueLabelModes.HIDE, }, tickLabelsVisibilitySettings: { types: [TICK_LABELS_CONFIG], @@ -243,6 +249,7 @@ export const xyVisFunction: ExpressionFunctionDefinition< } const hasBar = dataLayers.filter(({ seriesType }) => seriesType.includes('bar')).length > 0; + const hasArea = dataLayers.filter(({ seriesType }) => seriesType.includes('area')).length > 0; validateExtent(args.yLeftExtent, hasBar || hasArea); @@ -252,6 +259,14 @@ export const xyVisFunction: ExpressionFunctionDefinition< throw new Error(errors.notUsedFillOpacityError()); } + const hasNotHistogramBars = + dataLayers.filter(({ seriesType, isHistogram }) => seriesType.includes('bar') && !isHistogram) + .length > 0; + + if ((!hasBar || !hasNotHistogramBars) && args.valueLabels !== ValueLabelModes.HIDE) { + throw new Error(errors.valueLabelsForNotBarsOrHistogramBarsChartsError()); + } + return { type: 'render', as: XY_VIS_RENDERER, diff --git a/src/plugins/chart_expressions/expression_xy/public/components/data_layers.tsx b/src/plugins/chart_expressions/expression_xy/public/components/data_layers.tsx index 94b19f31a83e3..fd2c9506894d3 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/data_layers.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/data_layers.tsx @@ -24,7 +24,7 @@ import { ValueLabelMode, XYCurveType, } from '../../common'; -import { SeriesTypes } from '../../common/constants'; +import { SeriesTypes, ValueLabelModes } from '../../common/constants'; import { getColorAssignments, getFitOptions, @@ -157,7 +157,7 @@ export const DataLayers: FC = ({ // * when rotating the chart, the formatter is not correctly picked // * in some scenarios value labels are not strings, and this breaks the elastic-chart lib valueFormatter: (d: unknown) => yAxis?.formatter?.convert(d) || '', - showValueLabel: shouldShowValueLabels && valueLabels !== 'hide', + showValueLabel: shouldShowValueLabels && valueLabels !== ValueLabelModes.HIDE, isValueContainedInElement: false, isAlternatingValueLabel: false, overflowConstraints: [ diff --git a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx index f671cf75dc439..38d9ae2cf8683 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx @@ -62,7 +62,7 @@ import { CommonXYDataLayerConfigResult, CommonXYLayerConfigResult } from '../../ import './xy_chart.scss'; import { Annotations, getAnnotationsGroupedByInterval } from './annotations'; -import { SeriesTypes } from '../../common/constants'; +import { SeriesTypes, ValueLabelModes } from '../../common/constants'; import { DataLayers } from './data_layers'; declare global { @@ -349,7 +349,9 @@ export function XYChart({ !isHistogramViz; const valueLabelsStyling = - shouldShowValueLabels && valueLabels !== 'hide' && getValueLabelsStyling(shouldRotate); + shouldShowValueLabels && + valueLabels !== ValueLabelModes.HIDE && + getValueLabelsStyling(shouldRotate); const clickHandler: ElementClickListener = ([[geometry, series]]) => { // for xyChart series is always XYChartSeriesIdentifier and geometry is always type of GeometryValue From d35c9eb05610b07e3d6c2758170310430d84c0f5 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Tue, 12 Apr 2022 19:58:19 +0300 Subject: [PATCH 100/133] Removed not used tests and imports. --- .../public/components/xy_chart.test.tsx | 28 ----------- .../public/components/xy_chart.tsx | 47 ++++++++++--------- .../public/helpers/axes_configuration.ts | 1 - 3 files changed, 25 insertions(+), 51 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx index 6da5e5e92b30d..17ca946b500fb 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx @@ -550,34 +550,6 @@ describe('XYChart component', () => { }); }); - test('it does not allow positive lower bound for bar', () => { - const component = shallow( - - ); - expect(component.find(Axis).find('[id="left"]').prop('domain')).toEqual({ - fit: false, - min: NaN, - max: NaN, - }); - }); - test('it does include referenceLine values when in full extent mode', () => { const { args: refArgs } = sampleArgsWithReferenceLine(); diff --git a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx index 38d9ae2cf8683..ead11a82ce86b 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx @@ -310,35 +310,38 @@ export function XYChart({ const fit = !hasBarOrArea && extent.mode === 'dataBounds'; let min: number = NaN; let max: number = NaN; - - const axisHasReferenceLine = referenceLineLayers.some(({ yConfig }) => - yConfig?.some(({ axisMode }) => axisMode === axis.groupId) - ); - if (!fit && axisHasReferenceLine) { - // Remove this once the chart will support automatic annotation fit for other type of charts - const { min: computedMin, max: computedMax } = computeOverallDataDomain( - layers, - axis.series.map(({ accessor }) => accessor) + if (extent.mode === 'custom') { + min = extent.lowerBound ?? NaN; + max = extent.upperBound ?? NaN; + } else { + const axisHasReferenceLine = referenceLineLayers.some(({ yConfig }) => + yConfig?.some(({ axisMode }) => axisMode === axis.groupId) ); + if (!fit && axisHasReferenceLine) { + // Remove this once the chart will support automatic annotation fit for other type of charts + const { min: computedMin, max: computedMax } = computeOverallDataDomain( + layers, + axis.series.map(({ accessor }) => accessor) + ); - if (computedMin != null && computedMax != null) { - max = Math.max(computedMax, max || 0); - min = Math.min(computedMin, min || 0); - } - for (const { yConfig, table } of referenceLineLayers) { - for (const { axisMode, forAccessor } of yConfig || []) { - if (axis.groupId === axisMode) { - for (const row of table.rows) { - const value = row[forAccessor]; - // keep the 0 in view - max = Math.max(value, max || 0, 0); - min = Math.min(value, min || 0, 0); + if (computedMin != null && computedMax != null) { + max = Math.max(computedMax, max || 0); + min = Math.min(computedMin, min || 0); + } + for (const { yConfig, table } of referenceLineLayers) { + for (const { axisMode, forAccessor } of yConfig || []) { + if (axis.groupId === axisMode) { + for (const row of table.rows) { + const value = row[forAccessor]; + // keep the 0 in view + max = Math.max(value, max || 0, 0); + min = Math.min(value, min || 0, 0); + } } } } } } - return { fit, min, max }; }; diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/axes_configuration.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/axes_configuration.ts index fdc12609307c1..fa76c0e3f2994 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/axes_configuration.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/axes_configuration.ts @@ -8,7 +8,6 @@ import { FormatFactory } from '../types'; import { - AxisExtentConfig, CommonXYDataLayerConfigResult, CommonXYReferenceLineLayerConfigResult, ExtendedYConfig, From 2fb11f42d9152cf92d520e7ebe315e81e4f58d6e Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Wed, 13 Apr 2022 15:37:40 +0300 Subject: [PATCH 101/133] Fixed valueLabels and added migrations. --- .../common/expression_functions/xy_vis.ts | 29 +++++++++++++---- x-pack/plugins/lens/common/types.ts | 3 +- .../legend_settings_popover.tsx | 2 +- .../legend_size_settings.tsx | 2 +- .../value_labels_settings.test.tsx | 2 +- .../value_labels_settings.tsx | 2 +- .../xy_visualization/to_expression.test.ts | 4 +-- .../public/xy_visualization/to_expression.ts | 21 ++++++++----- .../make_lens_embeddable_factory.ts | 15 +++++++-- .../server/migrations/common_migrations.ts | 31 +++++++++++++++---- .../migrations/saved_object_migrations.ts | 28 +++++++++++++++-- .../plugins/lens/server/migrations/types.ts | 14 +++++++-- 12 files changed, 118 insertions(+), 35 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts index 087deff314230..2d8a09691868a 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts @@ -8,7 +8,13 @@ import { i18n } from '@kbn/i18n'; import type { ExpressionFunctionDefinition, Datatable } from '../../../../expressions'; -import { AxisExtentConfigResult, XYArgs, XYLayerConfigResult, XYRender } from '../types'; +import { + AxisExtentConfigResult, + DataLayerConfigResult, + XYArgs, + XYLayerConfigResult, + XYRender, +} from '../types'; import { XY_VIS, DATA_LAYER, @@ -46,9 +52,17 @@ const errors = { defaultMessage: '`valueLabels` argument is applicable only for bar charts, which are not histograms.', }), + dataBoundsForNotLineChartError: () => + i18n.translate('expressionXY.reusable.function.xyVis.errors.dataBoundsForNotLineChartError', { + defaultMessage: 'Only line charts can be fit to the data bounds', + }), }; -const validateExtent = (extent: AxisExtentConfigResult, hasBarOrArea: boolean) => { +const validateExtent = ( + extent: AxisExtentConfigResult, + hasBarOrArea: boolean, + dataLayers: DataLayerConfigResult[] +) => { const isValidLowerBound = extent.lowerBound === undefined || (extent.lowerBound !== undefined && extent.lowerBound <= 0); const isValidUpperBound = @@ -59,6 +73,11 @@ const validateExtent = (extent: AxisExtentConfigResult, hasBarOrArea: boolean) = if (hasBarOrArea && extent.mode === AxisExtentModes.CUSTOM && !areValidBounds) { throw new Error(errors.extendBoundsAreInvalidError()); } + + const lineSeries = dataLayers.filter(({ seriesType }) => seriesType.includes('line')); + if (!lineSeries.length && extent.mode === AxisExtentModes.DATA_BOUNDS) { + throw new Error(errors.dataBoundsForNotLineChartError()); + } }; export const xyVisFunction: ExpressionFunctionDefinition< @@ -244,16 +263,14 @@ export const xyVisFunction: ExpressionFunctionDefinition< }, []); const logTable = prepareLogTable(data, layerDimensions, true); - handlers.inspectorAdapters.tables.logDatatable('default', logTable); } const hasBar = dataLayers.filter(({ seriesType }) => seriesType.includes('bar')).length > 0; - const hasArea = dataLayers.filter(({ seriesType }) => seriesType.includes('area')).length > 0; - validateExtent(args.yLeftExtent, hasBar || hasArea); - validateExtent(args.yRightExtent, hasBar || hasArea); + validateExtent(args.yLeftExtent, hasBar || hasArea, dataLayers); + validateExtent(args.yRightExtent, hasBar || hasArea, dataLayers); if (!hasArea && args.fillOpacity !== undefined) { throw new Error(errors.notUsedFillOpacityError()); diff --git a/x-pack/plugins/lens/common/types.ts b/x-pack/plugins/lens/common/types.ts index 6922deaebd681..ee60de11dda99 100644 --- a/x-pack/plugins/lens/common/types.ts +++ b/x-pack/plugins/lens/common/types.ts @@ -60,8 +60,7 @@ export type CustomPaletteParamsConfig = CustomPaletteParams & { export type LayerType = typeof layerTypes[keyof typeof layerTypes]; -// Shared by XY Chart and Heatmap as for now -export type ValueLabelConfig = 'hide' | 'inside' | 'outside'; +export type ValueLabelConfig = 'hide' | 'show'; export type PieChartType = $Values; export type CategoryDisplayType = $Values; diff --git a/x-pack/plugins/lens/public/shared_components/legend_settings_popover.tsx b/x-pack/plugins/lens/public/shared_components/legend_settings_popover.tsx index 481c38815d43d..d808f737f94d3 100644 --- a/x-pack/plugins/lens/public/shared_components/legend_settings_popover.tsx +++ b/x-pack/plugins/lens/public/shared_components/legend_settings_popover.tsx @@ -232,7 +232,7 @@ export const LegendSettingsPopover: React.FunctionComponent {location && ( { }); it('should render the passed value if given', () => { - const component = shallow(); + const component = shallow(); expect( component.find('[data-test-subj="lens-value-labels-visibility-btn"]').prop('idSelected') ).toEqual(`value_labels_inside`); diff --git a/x-pack/plugins/lens/public/shared_components/value_labels_settings.tsx b/x-pack/plugins/lens/public/shared_components/value_labels_settings.tsx index 64d9f5475379a..f5378a2e3ba01 100644 --- a/x-pack/plugins/lens/public/shared_components/value_labels_settings.tsx +++ b/x-pack/plugins/lens/public/shared_components/value_labels_settings.tsx @@ -26,7 +26,7 @@ const valueLabelsOptions: Array<{ }, { id: `value_labels_inside`, - value: 'inside', + value: 'show', label: i18n.translate('xpack.lens.shared.valueLabelsVisibility.inside', { defaultMessage: 'Show', }), diff --git a/x-pack/plugins/lens/public/xy_visualization/to_expression.test.ts b/x-pack/plugins/lens/public/xy_visualization/to_expression.test.ts index f4ba3a7e4dcf4..847918c7b9e21 100644 --- a/x-pack/plugins/lens/public/xy_visualization/to_expression.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/to_expression.test.ts @@ -347,7 +347,7 @@ describe('#toExpression', () => { const expression = xyVisualization.toExpression( { legend: { position: Position.Bottom, isVisible: true }, - valueLabels: 'inside', + valueLabels: 'show', preferredSeriesType: 'bar', layers: [ { @@ -371,7 +371,7 @@ describe('#toExpression', () => { const expression = xyVisualization.toExpression( { legend: { position: Position.Bottom, isVisible: true }, - valueLabels: 'inside', + valueLabels: 'show', preferredSeriesType: 'bar', layers: [ { diff --git a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts index ac649807c428b..13d8644fc74e6 100644 --- a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts +++ b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts @@ -221,15 +221,20 @@ export const buildExpression = ( showSingleSeries: state.legend.showSingleSeries ? [state.legend.showSingleSeries] : [], - position: [state.legend.position], + position: !state.legend.isInside ? [state.legend.position] : [], isInside: state.legend.isInside ? [state.legend.isInside] : [], - legendSize: state.legend.legendSize ? [state.legend.legendSize] : [], - horizontalAlignment: state.legend.horizontalAlignment - ? [state.legend.horizontalAlignment] - : [], - verticalAlignment: state.legend.verticalAlignment - ? [state.legend.verticalAlignment] - : [], + legendSize: + !state.legend.isInside && state.legend.legendSize + ? [state.legend.legendSize] + : [], + horizontalAlignment: + state.legend.horizontalAlignment && state.legend.isInside + ? [state.legend.horizontalAlignment] + : [], + verticalAlignment: + state.legend.verticalAlignment && state.legend.isInside + ? [state.legend.verticalAlignment] + : [], // ensure that even if the user types more than 5 columns // we will only show 5 floatingColumns: state.legend.floatingColumns 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 7b17dc9977e6d..473f20c0a9120 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 @@ -14,6 +14,7 @@ import { import { DOC_TYPE } from '../../common'; import { commonEnhanceTableRowHeight, + commonFixValueLabelsInXY, commonMakeReversePaletteAsCustom, commonRemoveTimezoneDateHistogramParam, commonRenameFilterReferences, @@ -34,6 +35,7 @@ import { VisState716, VisState810, VisStatePre715, + XYVisualizationStatePre820, } from '../migrations/types'; import { extract, inject } from '../../common/embeddable_factory'; @@ -94,10 +96,19 @@ export const makeLensEmbeddableFactory = } as unknown as SerializableRecord; }, '8.2.0': (state) => { - const lensState = state as unknown as { attributes: LensDocShape810 }; + const lensState = state as unknown as { + attributes: LensDocShape810; + }; let migratedLensState = commonSetLastValueShowArrayValues(lensState.attributes); - migratedLensState = commonEnhanceTableRowHeight(migratedLensState); + migratedLensState = commonEnhanceTableRowHeight( + migratedLensState as LensDocShape810 + ); migratedLensState = commonSetIncludeEmptyRowsDateHistogram(migratedLensState); + if (migratedLensState.visualizationType !== 'lnsXY') { + migratedLensState = commonFixValueLabelsInXY( + migratedLensState as LensDocShape810 + ); + } 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 0a76b0a5e6b45..77ae161987f05 100644 --- a/x-pack/plugins/lens/server/migrations/common_migrations.ts +++ b/x-pack/plugins/lens/server/migrations/common_migrations.ts @@ -27,6 +27,9 @@ import { VisState820, CustomVisualizationMigrations, LensDocShape810, + LensDocShape820, + XYVisualizationStatePre820, + XYVisualizationState820, } from './types'; import { DOCUMENT_FIELD_NAME, layerTypes } from '../../common'; import { LensDocShape } from './saved_object_migrations'; @@ -194,9 +197,7 @@ export const commonRenameFilterReferences = (attributes: LensDocShape715): LensD return newAttributes as LensDocShape810; }; -export const commonSetLastValueShowArrayValues = ( - attributes: LensDocShape810 -): LensDocShape810 => { +export const commonSetLastValueShowArrayValues = (attributes: LensDocShape810): LensDocShape810 => { const newAttributes = cloneDeep(attributes); for (const layer of Object.values(newAttributes.state.datasourceStates.indexpattern.layers)) { for (const column of Object.values(layer.columns)) { @@ -215,19 +216,19 @@ export const commonEnhanceTableRowHeight = ( attributes: LensDocShape810 ): LensDocShape810 => { if (attributes.visualizationType !== 'lnsDatatable') { - return attributes; + return attributes as LensDocShape810; } const visState810 = attributes.state.visualization as VisState810; const newAttributes = cloneDeep(attributes); const vizState = newAttributes.state.visualization as VisState820; vizState.rowHeight = visState810.fitRowToContent ? 'auto' : 'single'; vizState.rowHeightLines = visState810.fitRowToContent ? 2 : 1; - return newAttributes; + return newAttributes as LensDocShape810; }; export const commonSetIncludeEmptyRowsDateHistogram = ( attributes: LensDocShape810 -): LensDocShape810 => { +): LensDocShape810 => { const newAttributes = cloneDeep(attributes); for (const layer of Object.values(newAttributes.state.datasourceStates.indexpattern.layers)) { for (const column of Object.values(layer.columns)) { @@ -328,3 +329,21 @@ export const fixLensTopValuesCustomFormatting = (attributes: LensDocShape810): L ); return newAttributes as LensDocShape810; }; + +export const commonFixValueLabelsInXY = ( + attributes: LensDocShape820 +): LensDocShape820 => { + const newAttributes: LensDocShape820 = cloneDeep(attributes); + const { visualization } = newAttributes.state; + const { valueLabels } = visualization; + return { + ...newAttributes, + state: { + ...newAttributes.state, + visualization: { + ...visualization, + valueLabels: valueLabels && valueLabels !== 'hide' ? 'show' : valueLabels, + }, + }, + }; +}; 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 6fb8044baf8ea..b26c3e4895afb 100644 --- a/x-pack/plugins/lens/server/migrations/saved_object_migrations.ts +++ b/x-pack/plugins/lens/server/migrations/saved_object_migrations.ts @@ -15,7 +15,7 @@ import { } from 'src/core/server'; import { Filter } from '@kbn/es-query'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { Query } from 'src/plugins/data/public'; +import { Query } from '../../../../../src/plugins/data/public'; import { mergeSavedObjectMigrationMaps } from '../../../../../src/core/server'; import { MigrateFunctionsObject } from '../../../../../src/plugins/kibana_utils/common'; import { PersistableFilter } from '../../common'; @@ -30,6 +30,11 @@ import { VisState716, CustomVisualizationMigrations, LensDocShape810, + LensDocShape820, + XYVisualizationStatePre820, + XYVisualizationState820, + VisState810, + VisState820, } from './types'; import { commonRenameOperationsForFormula, @@ -44,6 +49,7 @@ import { commonSetLastValueShowArrayValues, commonEnhanceTableRowHeight, commonSetIncludeEmptyRowsDateHistogram, + commonFixValueLabelsInXY, } from './common_migrations'; interface LensDocShapePre710 { @@ -474,7 +480,10 @@ const setLastValueShowArrayValues: SavedObjectMigrationFn = (doc) => { +const enhanceTableRowHeight: SavedObjectMigrationFn< + LensDocShape810, + LensDocShape810 +> = (doc) => { const newDoc = cloneDeep(doc); return { ...newDoc, attributes: commonEnhanceTableRowHeight(newDoc.attributes) }; }; @@ -485,6 +494,18 @@ const setIncludeEmptyRowsDateHistogram: SavedObjectMigrationFn, + LensDocShape820 +> = (doc) => { + if (doc.attributes.visualizationType !== 'lnsXY') { + return doc; + } + + const newDoc = cloneDeep(doc); + return { ...newDoc, attributes: commonFixValueLabelsInXY(newDoc.attributes) }; +}; + const lensMigrations: SavedObjectMigrationMap = { '7.7.0': removeInvalidAccessors, // The order of these migrations matter, since the timefield migration relies on the aggConfigs @@ -502,7 +523,8 @@ const lensMigrations: SavedObjectMigrationMap = { '8.2.0': flow( setLastValueShowArrayValues, setIncludeEmptyRowsDateHistogram, - enhanceTableRowHeight + enhanceTableRowHeight, + fixValueLabelsInXY ), }; diff --git a/x-pack/plugins/lens/server/migrations/types.ts b/x-pack/plugins/lens/server/migrations/types.ts index de4143e3d00c5..c757ed2e1ec02 100644 --- a/x-pack/plugins/lens/server/migrations/types.ts +++ b/x-pack/plugins/lens/server/migrations/types.ts @@ -10,7 +10,7 @@ import { Filter } from '@kbn/es-query'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { Query } from 'src/plugins/data/public'; import type { MigrateFunctionsObject } from 'src/plugins/kibana_utils/common'; -import type { LayerType, PersistableFilter } from '../../common'; +import type { LayerType, PersistableFilter, ValueLabelConfig } from '../../common'; export type CustomVisualizationMigrations = Record MigrateFunctionsObject>; @@ -204,7 +204,7 @@ export type LensDocShape810 = Omit< 'filters' | 'state' > & { filters: Filter[]; - state: Omit & { + state: Omit['state'], 'datasourceStates'> & { datasourceStates: { indexpattern: Omit & { layers: Record< @@ -258,3 +258,13 @@ export interface VisState820 { rowHeight: 'auto' | 'single' | 'custom'; rowHeightLines: number; } + +export type LensDocShape820 = LensDocShape810; + +export interface XYVisualizationStatePre820 { + valueLabels: 'hide' | 'inside' | 'outside'; +} + +export interface XYVisualizationState820 { + valueLabels: ValueLabelConfig; +} From 54e1b66d3257b2524cfa8e60183d3edb0da98fbb Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Wed, 13 Apr 2022 16:17:58 +0300 Subject: [PATCH 102/133] Fixed type checks. --- .../lens/public/heatmap_visualization/toolbar_component.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/lens/public/heatmap_visualization/toolbar_component.tsx b/x-pack/plugins/lens/public/heatmap_visualization/toolbar_component.tsx index ae8f9c32d4358..c543208b4cb9f 100644 --- a/x-pack/plugins/lens/public/heatmap_visualization/toolbar_component.tsx +++ b/x-pack/plugins/lens/public/heatmap_visualization/toolbar_component.tsx @@ -62,11 +62,11 @@ export const HeatmapToolbar = memo( buttonDataTestSubj="lnsVisualOptionsButton" > { setState({ ...state, - gridConfig: { ...state.gridConfig, isCellLabelVisible: newMode === 'inside' }, + gridConfig: { ...state.gridConfig, isCellLabelVisible: newMode === 'show' }, }); }} /> From e4a40dc3763f587363b2a5b33ce2bb8cd1274b2a Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Wed, 13 Apr 2022 17:50:52 +0300 Subject: [PATCH 103/133] Added test for the migrations. --- .../saved_object_migrations.test.ts | 49 +++++++++++++++++++ 1 file changed, 49 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 7c6e8345b71de..d1972c87b58d2 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 @@ -24,6 +24,7 @@ import { } from './types'; import { layerTypes, MetricState } from '../../common'; import { Filter } from '@kbn/es-query'; +import { XYState } from '../../public'; describe('Lens migrations', () => { const migrations = getAllMigrations({}, {}); @@ -2113,4 +2114,52 @@ describe('Lens migrations', () => { expect(visState.size).toBe('s'); }); }); + + describe('8.3.0 valueLabels in XY', () => { + const context = { log: { warning: () => {} } } as unknown as SavedObjectMigrationContext; + const example = { + type: 'lens', + id: 'mocked-saved-object-id', + attributes: { + savedObjectId: '1', + title: 'MyRenamedOps', + description: '', + visualizationType: 'lnsXY', + state: { + visualization: { + valueLabels: 'inside', + }, + }, + }, + } as unknown as SavedObjectUnsanitizedDoc; + + it('migrates valueLabels from `inside` to `show`', () => { + const result = migrations['8.3.0'](example, context) as ReturnType< + SavedObjectMigrationFn + >; + const visState = result.attributes.state.visualization as XYState; + expect(visState.valueLabels).toBe('show'); + }); + + it("doesn't migrate valueLabels with `hide` value", () => { + const result = migrations['8.3.0']( + { + ...example, + attributes: { + ...example.attributes, + state: { + ...example.attributes.state, + visualization: { + ...(example.attributes.state.visualization as Record), + valueLabels: 'hide', + }, + }, + }, + }, + context + ) as ReturnType>; + const visState = result.attributes.state.visualization as XYState; + expect(visState.valueLabels).toBe('hide'); + }); + }); }); From 05f854f3fdff78a717c7a8cea6f2c12e107c1f04 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Wed, 13 Apr 2022 17:51:10 +0300 Subject: [PATCH 104/133] Fixed imports. --- .../lens/server/migrations/saved_object_migrations.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 d1972c87b58d2..0f2a32b091054 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 @@ -21,10 +21,10 @@ import { VisStatePre715, VisState810, VisState820, + VisState830, } from './types'; import { layerTypes, MetricState } from '../../common'; import { Filter } from '@kbn/es-query'; -import { XYState } from '../../public'; describe('Lens migrations', () => { const migrations = getAllMigrations({}, {}); @@ -2137,7 +2137,7 @@ describe('Lens migrations', () => { const result = migrations['8.3.0'](example, context) as ReturnType< SavedObjectMigrationFn >; - const visState = result.attributes.state.visualization as XYState; + const visState = result.attributes.state.visualization as VisState830; expect(visState.valueLabels).toBe('show'); }); @@ -2158,7 +2158,7 @@ describe('Lens migrations', () => { }, context ) as ReturnType>; - const visState = result.attributes.state.visualization as XYState; + const visState = result.attributes.state.visualization as VisState830; expect(visState.valueLabels).toBe('hide'); }); }); From e50bc1fc1b250913015dc831f545068068bbbab2 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Wed, 13 Apr 2022 18:07:05 +0300 Subject: [PATCH 105/133] Fixed types --- x-pack/plugins/lens/public/xy_visualization/visualization.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx index 6abb5f5b58f72..9e4ba04a072d8 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx @@ -86,7 +86,7 @@ type ConvertActiveDataFn = ( const updateFrame = ( state: State | undefined, - frame: FramePublicAPI, + frame: Pick, convertActiveData?: ConvertActiveDataFn ) => { if (!frame) { From 2f74299ac3c355d1f4a8d0638435bfd48c340508 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Thu, 14 Apr 2022 10:40:29 +0300 Subject: [PATCH 106/133] Fixed i18n checks. # Conflicts: # src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx --- .../common/expression_functions/xy_vis.ts | 11 +++++++---- .../expression_xy/public/components/xy_chart.tsx | 4 ++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts index 2d8a09691868a..f67c5e9cc8d41 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts @@ -48,10 +48,13 @@ const errors = { defaultMessage: '`fillOpacity` argument is applicable only for area charts.', }), valueLabelsForNotBarsOrHistogramBarsChartsError: () => - i18n.translate('expressionXY.reusable.function.xyVis.errors.notUsedFillOpacityError', { - defaultMessage: - '`valueLabels` argument is applicable only for bar charts, which are not histograms.', - }), + i18n.translate( + 'expressionXY.reusable.function.xyVis.errors.valueLabelsForNotBarsOrHistogramBarsChartsError', + { + defaultMessage: + '`valueLabels` argument is applicable only for bar charts, which are not histograms.', + } + ), dataBoundsForNotLineChartError: () => i18n.translate('expressionXY.reusable.function.xyVis.errors.dataBoundsForNotLineChartError', { defaultMessage: 'Only line charts can be fit to the data bounds', diff --git a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx index ead11a82ce86b..721eb25e42fcf 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx @@ -59,12 +59,12 @@ import { getLegendAction } from './legend_action'; import { ReferenceLineAnnotations, computeChartMargins } from './reference_lines'; import { visualizationDefinitions } from '../definitions'; import { CommonXYDataLayerConfigResult, CommonXYLayerConfigResult } from '../../common/types'; - -import './xy_chart.scss'; import { Annotations, getAnnotationsGroupedByInterval } from './annotations'; import { SeriesTypes, ValueLabelModes } from '../../common/constants'; import { DataLayers } from './data_layers'; +import './xy_chart.scss'; + declare global { interface Window { /** From 02ba3944fb10c00854c9ee7771ba2eabcc60084c Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Mon, 18 Apr 2022 12:10:58 +0300 Subject: [PATCH 107/133] Fixed imports and types. --- .../common/expression_functions/data_layer.test.ts | 4 ++-- .../common/expression_functions/y_axis_config.ts | 2 +- .../expression_xy/public/components/xy_chart.tsx | 3 +-- .../xy_config_panel/visual_options_popover/index.tsx | 1 - 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer.test.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer.test.ts index 21feab885d877..518690d47bfcb 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer.test.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer.test.ts @@ -7,10 +7,10 @@ */ import { DataLayerArgs } from '../types'; -import { dataLayerConfigFunction } from '.'; import { createMockExecutionContext } from '@kbn/expressions-plugin/common/mocks'; -import { mockPaletteOutput } from '../__mocks__'; +import { mockPaletteOutput, sampleArgs } from '../__mocks__'; import { LayerTypes } from '../constants'; +import { dataLayerFunction } from './data_layer'; describe('dataLayerConfig', () => { test('produces the correct arguments', () => { diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/y_axis_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/y_axis_config.ts index aaae5b4cf4620..1d9087dc264ec 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/y_axis_config.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/y_axis_config.ts @@ -8,7 +8,7 @@ import { i18n } from '@kbn/i18n'; import type { ExpressionFunctionDefinition } from '@kbn/expressions-plugin/common'; -import { FillStyles, IconPositions, LineStyles, YAxisModes, Y_CONFIG } from '../constants'; +import { YAxisModes, Y_CONFIG } from '../constants'; import { YConfig, YConfigResult } from '../types'; export const yAxisConfigFunction: ExpressionFunctionDefinition< diff --git a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx index 4b6ff8eee6f40..744566c263d09 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx @@ -28,7 +28,7 @@ import { import { IconType } from '@elastic/eui'; import { PaletteRegistry } from '@kbn/coloring'; import { RenderMode } from '@kbn/expressions-plugin/common'; -import { EmptyPlaceholder } from '@kbn/charts-plugin/common'; +import { EmptyPlaceholder } from '@kbn/charts-plugin/public'; import { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public'; import { ChartsPluginSetup, ChartsPluginStart, useActiveCursor } from '@kbn/charts-plugin/public'; import { MULTILAYER_TIME_AXIS_STYLE } from '@kbn/charts-plugin/common'; @@ -521,7 +521,6 @@ export function XYChart({ shouldRotate ), }, - markSizeRatio: args.markSizeRatio ?? 1, }} baseTheme={chartBaseTheme} tooltip={{ diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/index.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/index.tsx index 634f2c121f3d9..ba8a246043bf2 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/index.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/index.tsx @@ -7,7 +7,6 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; -import { ValidLayer } from '@kbn/expression-xy-plugin/common'; import { ToolbarPopover, TooltipWrapper, ValueLabelsSettings } from '../../../shared_components'; import { MissingValuesOptions } from './missing_values_option'; import { LineCurveOption } from './line_curve_option'; From dfccec63bf48ea8b090597923069b737b4ec3c13 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Mon, 18 Apr 2022 10:04:20 +0000 Subject: [PATCH 108/133] [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' --- .../expression_functions/extended_annotation_layer.ts | 2 +- .../common/expression_functions/extended_data_layer.ts | 2 +- .../expression_functions/extended_reference_line_layer.ts | 2 +- .../common/expression_functions/extended_y_axis_config.ts | 2 +- .../common/expression_functions/layered_xy_vis.ts | 2 +- .../expression_xy/common/types/expression_renderers.ts | 2 +- .../expression_xy/common/utils/log_datatables.ts | 4 ++-- .../expression_xy/public/components/data_layers.tsx | 6 +++--- .../expression_xy/public/helpers/color_assignment.test.ts | 2 +- .../expression_xy/public/helpers/data_layers.tsx | 6 +++--- .../xy_config_panel/annotations_config_panel/icon_set.ts | 2 +- .../xy_config_panel/reference_line_config_panel/icon_set.ts | 2 +- .../xy_visualization/xy_config_panel/shared/icon_select.tsx | 2 +- 13 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_annotation_layer.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_annotation_layer.ts index 0962cc472ab26..cce2000e0485e 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_annotation_layer.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_annotation_layer.ts @@ -7,7 +7,7 @@ */ import { i18n } from '@kbn/i18n'; -import type { Datatable, ExpressionFunctionDefinition } from '../../../../expressions/common'; +import type { Datatable, ExpressionFunctionDefinition } from '@kbn/expressions-plugin/common'; import { LayerTypes, EXTENDED_ANNOTATION_LAYER } from '../constants'; import { ExtendedAnnotationLayerArgs, ExtendedAnnotationLayerConfigResult } from '../types'; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_data_layer.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_data_layer.ts index a6edc20db60dd..edb174a7ab1b9 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_data_layer.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_data_layer.ts @@ -7,7 +7,7 @@ */ import { i18n } from '@kbn/i18n'; -import type { Datatable, ExpressionFunctionDefinition } from '../../../../expressions/common'; +import type { Datatable, ExpressionFunctionDefinition } from '@kbn/expressions-plugin/common'; import { ExtendedDataLayerArgs, ExtendedDataLayerConfigResult } from '../types'; import { EXTENDED_DATA_LAYER, diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_reference_line_layer.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_reference_line_layer.ts index 677f1bfe94d05..affef45fbc2c8 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_reference_line_layer.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_reference_line_layer.ts @@ -7,7 +7,7 @@ */ import { i18n } from '@kbn/i18n'; -import type { Datatable, ExpressionFunctionDefinition } from '../../../../expressions/common'; +import type { Datatable, ExpressionFunctionDefinition } from '@kbn/expressions-plugin/common'; import { LayerTypes, EXTENDED_REFERENCE_LINE_LAYER, EXTENDED_Y_CONFIG } from '../constants'; import { ExtendedReferenceLineLayerArgs, ExtendedReferenceLineLayerConfigResult } from '../types'; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_y_axis_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_y_axis_config.ts index 08e8cbf576cf5..05de3608a2299 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_y_axis_config.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_y_axis_config.ts @@ -7,7 +7,7 @@ */ import { i18n } from '@kbn/i18n'; -import type { ExpressionFunctionDefinition } from '../../../../expressions/common'; +import type { ExpressionFunctionDefinition } from '@kbn/expressions-plugin/common'; import { AvailableReferenceLineIcons, EXTENDED_Y_CONFIG, diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts index 29bcd7e05cec6..b680b5492efa3 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts @@ -7,7 +7,7 @@ */ import { i18n } from '@kbn/i18n'; -import type { ExpressionFunctionDefinition, Datatable } from '../../../../expressions'; +import type { ExpressionFunctionDefinition, Datatable } from '@kbn/expressions-plugin'; import { LayeredXYArgs, XYExtendedLayerConfigResult, XYRender } from '../types'; import { XYCurveTypes, diff --git a/src/plugins/chart_expressions/expression_xy/common/types/expression_renderers.ts b/src/plugins/chart_expressions/expression_xy/common/types/expression_renderers.ts index 04d7fb2a446d3..4da90dbb994ba 100644 --- a/src/plugins/chart_expressions/expression_xy/common/types/expression_renderers.ts +++ b/src/plugins/chart_expressions/expression_xy/common/types/expression_renderers.ts @@ -7,7 +7,7 @@ */ import { AnnotationTooltipFormatter } from '@elastic/charts'; -import { AvailableAnnotationIcon, EventAnnotationArgs } from '../../../../event_annotation/common'; +import { AvailableAnnotationIcon, EventAnnotationArgs } from '@kbn/event-annotation-plugin/common'; import { XY_VIS_RENDERER } from '../constants'; import { XYProps } from './expression_functions'; diff --git a/src/plugins/chart_expressions/expression_xy/common/utils/log_datatables.ts b/src/plugins/chart_expressions/expression_xy/common/utils/log_datatables.ts index 64a1ab0ef0a2f..88d194d4646b5 100644 --- a/src/plugins/chart_expressions/expression_xy/common/utils/log_datatables.ts +++ b/src/plugins/chart_expressions/expression_xy/common/utils/log_datatables.ts @@ -6,8 +6,8 @@ * Side Public License, v 1. */ -import { ExecutionContext } from '../../../../expressions'; -import { Dimension, prepareLogTable } from '../../../../visualizations/common/utils'; +import { ExecutionContext } from '@kbn/expressions-plugin'; +import { Dimension, prepareLogTable } from '@kbn/visualizations-plugin/common/utils'; import { LayerTypes } from '../constants'; import { strings } from '../i18n'; import { diff --git a/src/plugins/chart_expressions/expression_xy/public/components/data_layers.tsx b/src/plugins/chart_expressions/expression_xy/public/components/data_layers.tsx index fd2c9506894d3..836a6944dd3bd 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/data_layers.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/data_layers.tsx @@ -14,9 +14,9 @@ import { } from '@elastic/charts'; import React, { FC } from 'react'; import { i18n } from '@kbn/i18n'; -import { PaletteRegistry } from '../../../../charts/public'; -import { FormatFactory } from '../../../../field_formats/common'; -import { Datatable } from '../../../../expressions'; +import { PaletteRegistry } from '@kbn/charts-plugin/public'; +import { FormatFactory } from '@kbn/field-formats-plugin/common'; +import { Datatable } from '@kbn/expressions-plugin'; import { CommonXYDataLayerConfigResult, EndValue, diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.test.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.test.ts index 78ec008c5e561..704406b857331 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.test.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.test.ts @@ -10,7 +10,7 @@ import { getColorAssignments } from './color_assignment'; import type { DataLayerConfigResult } from '../../common'; import type { FormatFactory } from '../types'; import { LayerTypes } from '../../common/constants'; -import { Datatable } from '../../../../expressions'; +import { Datatable } from '@kbn/expressions-plugin'; describe('color_assignment', () => { const tables: Record = { diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/data_layers.tsx b/src/plugins/chart_expressions/expression_xy/public/helpers/data_layers.tsx index 0d5d0519d3965..7c6f9be734d6d 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/data_layers.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/data_layers.tsx @@ -21,11 +21,11 @@ import { FieldFormat, FieldFormatParams, SerializedFieldFormat, -} from 'src/plugins/field_formats/common'; -import { Datatable, DatatableRow } from '../../../../expressions'; +} from '@kbn/field-formats-plugin/common'; +import { Datatable, DatatableRow } from '@kbn/expressions-plugin'; +import { PaletteRegistry, SeriesLayer } from '@kbn/charts-plugin/public'; import { CommonXYDataLayerConfigResult, XScaleType } from '../../common'; import { FormatFactory } from '../types'; -import { PaletteRegistry, SeriesLayer } from '../../../../charts/public'; import { getSeriesColor } from './state'; import { ColorAssignments } from './color_assignment'; import { GroupsConfiguration } from './axes_configuration'; diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/annotations_config_panel/icon_set.ts b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/annotations_config_panel/icon_set.ts index 70e121ee0a288..32721285a4477 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/annotations_config_panel/icon_set.ts +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/annotations_config_panel/icon_set.ts @@ -6,7 +6,7 @@ */ import { i18n } from '@kbn/i18n'; -import { AvailableAnnotationIcon } from '../../../../../../../src/plugins/event_annotation/common'; +import { AvailableAnnotationIcon } from '@kbn/event-annotation-plugin/common'; import { IconTriangle, IconCircle } from '../../../assets/annotation_icons'; import { IconSet } from '../shared/icon_select'; diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_config_panel/icon_set.ts b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_config_panel/icon_set.ts index e22ea13f802da..eda5d06cd3ef1 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_config_panel/icon_set.ts +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_config_panel/icon_set.ts @@ -6,7 +6,7 @@ */ import { i18n } from '@kbn/i18n'; -import { AvailableReferenceLineIcon } from '../../../../../../../src/plugins/chart_expressions/expression_xy/common'; +import { AvailableReferenceLineIcon } from '@kbn/expression-xy-plugin/common'; import { IconSet } from '../shared/icon_select'; export const referenceLineIconsSet: IconSet = [ diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/shared/icon_select.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/shared/icon_select.tsx index 75899d46399b6..bcb6a3a60b9e6 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/shared/icon_select.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/shared/icon_select.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { EuiComboBox, EuiFlexGroup, EuiFlexItem, EuiIcon, IconType } from '@elastic/eui'; -import { AvailableReferenceLineIcon } from '../../../../../../../src/plugins/chart_expressions/expression_xy/common'; +import { AvailableReferenceLineIcon } from '@kbn/expression-xy-plugin/common'; export function hasIcon(icon: string | undefined): icon is string { return icon != null && icon !== 'empty'; From 4d5eea148f006881cca92ad74f00333359ba36ee Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Tue, 19 Apr 2022 10:35:09 +0300 Subject: [PATCH 109/133] Added `showTooltip`. --- .../expression_xy/common/__mocks__/index.ts | 1 + .../common/expression_functions/layered_xy_vis.ts | 8 ++++++++ .../expression_xy/common/expression_functions/xy_vis.ts | 8 ++++++++ .../expression_xy/common/types/expression_functions.ts | 3 +++ .../expression_xy/public/components/xy_chart.test.tsx | 3 +++ .../expression_xy/public/components/xy_chart.tsx | 2 ++ 6 files changed, 25 insertions(+) diff --git a/src/plugins/chart_expressions/expression_xy/common/__mocks__/index.ts b/src/plugins/chart_expressions/expression_xy/common/__mocks__/index.ts index 9be697c951b50..33f35c4980ac3 100644 --- a/src/plugins/chart_expressions/expression_xy/common/__mocks__/index.ts +++ b/src/plugins/chart_expressions/expression_xy/common/__mocks__/index.ts @@ -67,6 +67,7 @@ export const createArgsWithLayers = ( xTitle: '', yTitle: '', yRightTitle: '', + showTooltip: true, legend: { type: 'legendConfig', isVisible: false, diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts index b680b5492efa3..e9ddb20110d93 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts @@ -171,6 +171,13 @@ export const layeredXyVisFunction: ExpressionFunctionDefinition< }), required: false, }, + showTooltip: { + types: ['boolean'], + default: true, + help: i18n.translate('expressionXY.layeredXyVis.showTooltip.help', { + defaultMessage: 'Show tooltip', + }), + }, }, fn(data, args, handlers) { const layers = (args.layers ?? []).filter( @@ -185,6 +192,7 @@ export const layeredXyVisFunction: ExpressionFunctionDefinition< value: { args: { ...args, + showTooltip: args.showTooltip ?? false, layers, ariaLabel: args.ariaLabel ?? diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts index bce212538437d..338f84caf28d7 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts @@ -243,6 +243,13 @@ export const xyVisFunction: ExpressionFunctionDefinition< defaultMessage: 'Specifies the aria label of the xy chart', }), }, + showTooltip: { + types: ['boolean'], + default: true, + help: i18n.translate('expressionXY.xyVis.showTooltip.help', { + defaultMessage: 'Show tooltip', + }), + }, }, fn(data, args, handlers) { const { dataLayers = [], referenceLineLayers = [], annotationLayers = [], ...restArgs } = args; @@ -293,6 +300,7 @@ export const xyVisFunction: ExpressionFunctionDefinition< value: { args: { ...restArgs, + showTooltip: args.showTooltip ?? false, layers, ariaLabel: args.ariaLabel ?? diff --git a/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts b/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts index cd4add85dd421..724315a0cb428 100644 --- a/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts +++ b/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts @@ -202,6 +202,7 @@ export interface XYArgs { hideEndzones?: boolean; valuesInLegend?: boolean; ariaLabel?: string; + showTooltip?: boolean; } export interface LayeredXYArgs { @@ -225,6 +226,7 @@ export interface LayeredXYArgs { hideEndzones?: boolean; valuesInLegend?: boolean; ariaLabel?: string; + showTooltip?: boolean; } export interface XYProps { @@ -248,6 +250,7 @@ export interface XYProps { hideEndzones?: boolean; valuesInLegend?: boolean; ariaLabel?: string; + showTooltip: boolean; } export interface AnnotationLayerArgs { diff --git a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx index ba486eb338b03..402144ab8a0ed 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx @@ -2064,6 +2064,7 @@ describe('XYChart component', () => { }; const args: XYProps = { + showTooltip: true, xTitle: '', yTitle: '', yRightTitle: '', @@ -2151,6 +2152,7 @@ describe('XYChart component', () => { }; const args: XYProps = { + showTooltip: true, xTitle: '', yTitle: '', yRightTitle: '', @@ -2222,6 +2224,7 @@ describe('XYChart component', () => { }; const args: XYProps = { + showTooltip: true, xTitle: '', yTitle: '', yRightTitle: '', diff --git a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx index 744566c263d09..ca2aeacba1aed 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx @@ -24,6 +24,7 @@ import { DisplayValueStyle, RecursivePartial, AxisStyle, + TooltipType, } from '@elastic/charts'; import { IconType } from '@elastic/eui'; import { PaletteRegistry } from '@kbn/coloring'; @@ -526,6 +527,7 @@ export function XYChart({ tooltip={{ boundary: document.getElementById('app-fixed-viewport') ?? undefined, headerFormatter: (d) => safeXAccessorLabelRenderer(d.value), + type: args.showTooltip ? TooltipType.VerticalCursor : TooltipType.None, }} allowBrushingLastHistogramBin={isTimeViz} rotation={shouldRotate ? 90 : 0} From acabb8f6af6d42be9b13fb65d9bf07e79d01aac4 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Tue, 19 Apr 2022 10:52:01 +0300 Subject: [PATCH 110/133] Update src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_annotation_layer.ts Co-authored-by: Marta Bondyra --- .../common/expression_functions/extended_annotation_layer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_annotation_layer.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_annotation_layer.ts index cce2000e0485e..b255c3586d0ba 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_annotation_layer.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_annotation_layer.ts @@ -34,7 +34,7 @@ export function extendedAnnotationLayerFunction(): ExpressionFunctionDefinition< annotations: { types: ['manual_event_annotation'], help: i18n.translate('expressionXY.extendedAnnotationLayer.annotations.help', { - defaultMessage: 'Annotationss', + defaultMessage: 'Annotations', }), multi: true, }, From 435f89afe4e9d120fc0add194042f9bc237af7f7 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Tue, 19 Apr 2022 11:15:19 +0300 Subject: [PATCH 111/133] Removed extra extends. --- x-pack/plugins/lens/server/migrations/types.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/lens/server/migrations/types.ts b/x-pack/plugins/lens/server/migrations/types.ts index 9022e7baac5f4..53804a6bbcfe0 100644 --- a/x-pack/plugins/lens/server/migrations/types.ts +++ b/x-pack/plugins/lens/server/migrations/types.ts @@ -269,5 +269,5 @@ export interface XYVisualizationState830 extends VisState820 { valueLabels: ValueLabelConfig; } -export type VisStatePre830 = XYVisualizationStatePre830 & VisState820; -export type VisState830 = XYVisualizationState830 & VisState820; +export type VisStatePre830 = XYVisualizationStatePre830; +export type VisState830 = XYVisualizationState830; From eec1c42c86c53a888ad5199ab5d809ba393f67eb Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Tue, 19 Apr 2022 12:49:43 +0300 Subject: [PATCH 112/133] Update src/plugins/chart_expressions/expression_xy/common/expression_functions/annotation_layer.ts Co-authored-by: Marta Bondyra --- .../common/expression_functions/annotation_layer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/annotation_layer.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/annotation_layer.ts index 2dd0fbcfdb834..be3ad824ce262 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/annotation_layer.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/annotation_layer.ts @@ -36,7 +36,7 @@ export function annotationLayerFunction(): ExpressionFunctionDefinition< annotations: { types: ['manual_event_annotation'], help: i18n.translate('expressionXY.annotationLayer.annotations.help', { - defaultMessage: 'Annotationss', + defaultMessage: 'Annotations', }), multi: true, }, From 471546e620782022561e66d0de91449740f20018 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Tue, 19 Apr 2022 13:31:03 +0300 Subject: [PATCH 113/133] Added guard. --- .../expression_xy/public/helpers/annotations.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/annotations.tsx b/src/plugins/chart_expressions/expression_xy/public/helpers/annotations.tsx index 4458771836b6b..d6746cafc0296 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/annotations.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/annotations.tsx @@ -27,6 +27,11 @@ type PartialExtendedYConfig = Pick< type PartialCollectiveConfig = Pick; +const isExtendedYConfig = ( + config: PartialExtendedYConfig | PartialCollectiveConfig | undefined +): config is PartialExtendedYConfig => + (config as PartialExtendedYConfig)?.iconPosition ? true : false; + // Note: it does not take into consideration whether the reference line is in view or not export const getLinesCausedPaddings = ( visualConfigs: Array, @@ -40,7 +45,7 @@ export const getLinesCausedPaddings = ( return; } const { axisMode, icon, textVisibility } = config; - const iconPosition = (config as PartialExtendedYConfig).iconPosition ?? undefined; + const iconPosition = isExtendedYConfig(config) ? config.iconPosition : undefined; if (axisMode && (hasIcon(icon) || textVisibility)) { const placement = getBaseIconPlacement(iconPosition, axesMap, axisMode); From b52c1b5dfa60398d67f58fb97283f4eb8cc71bf3 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Tue, 19 Apr 2022 13:33:57 +0300 Subject: [PATCH 114/133] Fixed the code duplication. --- .../lens/public/xy_visualization/reference_line_helpers.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx b/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx index d2b8d944ae4ab..ccb066f49efc2 100644 --- a/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx @@ -312,8 +312,6 @@ export const getReferenceSupportedLayer = ( }, ]; - const layers = state?.layers || []; - const referenceLineGroups = getGroupsRelatedToData( referenceLineGroupIds, state, @@ -321,6 +319,7 @@ export const getReferenceSupportedLayer = ( frame?.activeData ); + const layers = state?.layers || []; const dataLayers = getDataLayers(layers); const filledDataLayers = dataLayers.filter( @@ -337,7 +336,7 @@ export const getReferenceSupportedLayer = ( groupId: id, columnId: generateId(), dataType: 'number', - label: getAxisName(label, { isHorizontal: isHorizontalChart(state?.layers || []) }), + label: getAxisName(label, { isHorizontal: isHorizontalChart(layers) }), staticValue: getStaticValue( dataLayers, label, From 844c7b1c305fc1160e13cfe44f6732b0d7daf474 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Tue, 19 Apr 2022 14:12:52 +0300 Subject: [PATCH 115/133] Removed table from the annotation layer. --- .../expression_xy/common/constants.ts | 1 - .../expression_functions/annotation_layer.ts | 5 +- .../extended_annotation_layer.ts | 57 ------------------- .../common/expression_functions/index.ts | 1 - .../expression_functions/layered_xy_vis.ts | 4 +- .../expression_xy/common/index.ts | 4 -- .../common/types/expression_functions.ts | 26 ++------- .../public/components/annotations.tsx | 3 +- .../public/components/xy_chart.test.tsx | 15 ++--- .../public/components/xy_chart.tsx | 6 +- .../public/helpers/interval.test.ts | 30 ++++++---- .../expression_xy/public/helpers/layers.ts | 4 +- .../expression_xy/public/plugin.ts | 2 - .../expression_xy/server/plugin.ts | 2 - .../public/xy_visualization/to_expression.ts | 12 +--- 15 files changed, 42 insertions(+), 130 deletions(-) delete mode 100644 src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_annotation_layer.ts diff --git a/src/plugins/chart_expressions/expression_xy/common/constants.ts b/src/plugins/chart_expressions/expression_xy/common/constants.ts index e12d5b9c4abda..a75b3b97f6a43 100644 --- a/src/plugins/chart_expressions/expression_xy/common/constants.ts +++ b/src/plugins/chart_expressions/expression_xy/common/constants.ts @@ -21,7 +21,6 @@ export const TICK_LABELS_CONFIG = 'tickLabelsConfig'; export const AXIS_EXTENT_CONFIG = 'axisExtentConfig'; export const REFERENCE_LINE_LAYER = 'referenceLineLayer'; export const EXTENDED_REFERENCE_LINE_LAYER = 'extendedReferenceLineLayer'; -export const EXTENDED_ANNOTATION_LAYER = 'extendedAnnotationLayer'; export const LABELS_ORIENTATION_CONFIG = 'labelsOrientationConfig'; export const AXIS_TITLES_VISIBILITY_CONFIG = 'axisTitlesVisibilityConfig'; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/annotation_layer.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/annotation_layer.ts index be3ad824ce262..6df2f9a4037a1 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/annotation_layer.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/annotation_layer.ts @@ -9,13 +9,13 @@ import { i18n } from '@kbn/i18n'; import type { Datatable, ExpressionFunctionDefinition } from '@kbn/expressions-plugin/common'; import { LayerTypes, ANNOTATION_LAYER } from '../constants'; -import { AnnotationLayerArgs, AnnotationLayerConfigResult } from '../types'; +import { AnnotationLayerArgs, CommonXYAnnotationLayerConfigResult } from '../types'; export function annotationLayerFunction(): ExpressionFunctionDefinition< typeof ANNOTATION_LAYER, Datatable, AnnotationLayerArgs, - AnnotationLayerConfigResult + CommonXYAnnotationLayerConfigResult > { return { name: ANNOTATION_LAYER, @@ -47,7 +47,6 @@ export function annotationLayerFunction(): ExpressionFunctionDefinition< ...args, annotations: args.annotations ?? [], layerType: LayerTypes.ANNOTATIONS, - table: input, }; }, }; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_annotation_layer.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_annotation_layer.ts deleted file mode 100644 index b255c3586d0ba..0000000000000 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_annotation_layer.ts +++ /dev/null @@ -1,57 +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 { i18n } from '@kbn/i18n'; -import type { Datatable, ExpressionFunctionDefinition } from '@kbn/expressions-plugin/common'; -import { LayerTypes, EXTENDED_ANNOTATION_LAYER } from '../constants'; -import { ExtendedAnnotationLayerArgs, ExtendedAnnotationLayerConfigResult } from '../types'; - -export function extendedAnnotationLayerFunction(): ExpressionFunctionDefinition< - typeof EXTENDED_ANNOTATION_LAYER, - Datatable, - ExtendedAnnotationLayerArgs, - ExtendedAnnotationLayerConfigResult -> { - return { - name: EXTENDED_ANNOTATION_LAYER, - aliases: [], - type: EXTENDED_ANNOTATION_LAYER, - inputTypes: ['datatable'], - help: i18n.translate('expressionXY.extendedAnnotationLayer.help', { - defaultMessage: `Configure an annotation layer in the xy chart`, - }), - args: { - hide: { - types: ['boolean'], - default: false, - help: 'Show details', - }, - annotations: { - types: ['manual_event_annotation'], - help: i18n.translate('expressionXY.extendedAnnotationLayer.annotations.help', { - defaultMessage: 'Annotations', - }), - multi: true, - }, - table: { - types: ['datatable'], - help: i18n.translate('expressionXY.extendedAnnotationLayer.table.help', { - defaultMessage: 'Table', - }), - }, - }, - fn: (input, args) => { - return { - type: EXTENDED_ANNOTATION_LAYER, - ...args, - layerType: LayerTypes.ANNOTATIONS, - table: args.table ?? input, - }; - }, - }; -} diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/index.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/index.ts index ab1d570a07351..fb709f430f2ea 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/index.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/index.ts @@ -10,7 +10,6 @@ export * from './xy_vis'; export * from './layered_xy_vis'; export * from './legend_config'; export * from './annotation_layer'; -export * from './extended_annotation_layer'; export * from './y_axis_config'; export * from './extended_y_axis_config'; export * from './data_layer'; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts index b680b5492efa3..ec75648c85a9b 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts @@ -24,7 +24,7 @@ import { EXTENDED_REFERENCE_LINE_LAYER, LAYERED_XY_VIS, EndValues, - EXTENDED_ANNOTATION_LAYER, + ANNOTATION_LAYER, } from '../constants'; import { logDatatables } from '../utils'; @@ -130,7 +130,7 @@ export const layeredXyVisFunction: ExpressionFunctionDefinition< }), }, layers: { - types: [EXTENDED_DATA_LAYER, EXTENDED_REFERENCE_LINE_LAYER, EXTENDED_ANNOTATION_LAYER], + types: [EXTENDED_DATA_LAYER, EXTENDED_REFERENCE_LINE_LAYER, ANNOTATION_LAYER], help: i18n.translate('expressionXY.layeredXyVis.layers.help', { defaultMessage: 'Layers of visual series', }), diff --git a/src/plugins/chart_expressions/expression_xy/common/index.ts b/src/plugins/chart_expressions/expression_xy/common/index.ts index 626832640c676..2fbb6d5ef6c87 100755 --- a/src/plugins/chart_expressions/expression_xy/common/index.ts +++ b/src/plugins/chart_expressions/expression_xy/common/index.ts @@ -18,7 +18,6 @@ export { gridlinesConfigFunction, dataLayerFunction, annotationLayerFunction, - extendedAnnotationLayerFunction, extendedDataLayerFunction, axisExtentConfigFunction, tickLabelsConfigFunction, @@ -69,14 +68,11 @@ export type { CommonXYLayerConfigResult, AvailableReferenceLineIcon, XYExtendedLayerConfigResult, - AnnotationLayerConfigResult, - ExtendedAnnotationLayerArgs, ExtendedDataLayerConfigResult, LabelsOrientationConfigResult, CommonXYDataLayerConfigResult, ReferenceLineLayerConfigResult, AxisTitlesVisibilityConfigResult, - ExtendedAnnotationLayerConfigResult, CommonXYAnnotationLayerConfigResult, ExtendedReferenceLineLayerConfigResult, CommonXYReferenceLineLayerConfigResult, diff --git a/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts b/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts index cd4add85dd421..ee875082302c2 100644 --- a/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts +++ b/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts @@ -38,7 +38,6 @@ import { EXTENDED_REFERENCE_LINE_LAYER, ANNOTATION_LAYER, EndValues, - EXTENDED_ANNOTATION_LAYER, EXTENDED_Y_CONFIG, AvailableReferenceLineIcons, } from '../constants'; @@ -191,7 +190,7 @@ export interface XYArgs { valueLabels: ValueLabelMode; dataLayers: DataLayerConfigResult[]; referenceLineLayers: ReferenceLineLayerConfigResult[]; - annotationLayers: AnnotationLayerConfigResult[]; + annotationLayers: CommonXYAnnotationLayerConfigResult[]; fittingFunction?: FittingFunction; axisTitlesVisibilitySettings?: AxisTitlesVisibilityConfigResult; tickLabelsVisibilitySettings?: TickLabelsConfigResult; @@ -255,22 +254,9 @@ export interface AnnotationLayerArgs { hide?: boolean; } -export interface ExtendedAnnotationLayerArgs { - annotations: EventAnnotationOutput[]; - hide?: boolean; - table?: Datatable; -} - -export type AnnotationLayerConfigResult = AnnotationLayerArgs & { +export type CommonXYAnnotationLayerConfigResult = AnnotationLayerArgs & { type: typeof ANNOTATION_LAYER; layerType: typeof LayerTypes.ANNOTATIONS; - table: Datatable; -}; - -export type ExtendedAnnotationLayerConfigResult = ExtendedAnnotationLayerArgs & { - type: typeof EXTENDED_ANNOTATION_LAYER; - layerType: typeof LayerTypes.ANNOTATIONS; - table: Datatable; }; export interface ReferenceLineLayerArgs { @@ -291,12 +277,12 @@ export type XYLayerArgs = DataLayerArgs | ReferenceLineLayerArgs | AnnotationLay export type XYLayerConfigResult = | DataLayerConfigResult | ReferenceLineLayerConfigResult - | AnnotationLayerConfigResult; + | CommonXYAnnotationLayerConfigResult; export type XYExtendedLayerConfigResult = | ExtendedDataLayerConfigResult | ExtendedReferenceLineLayerConfigResult - | ExtendedAnnotationLayerConfigResult; + | CommonXYAnnotationLayerConfigResult; export interface LensMultiTable { type: typeof MULTITABLE; @@ -354,7 +340,3 @@ export type CommonXYDataLayerConfigResult = DataLayerConfigResult | ExtendedData export type CommonXYReferenceLineLayerConfigResult = | ReferenceLineLayerConfigResult | ExtendedReferenceLineLayerConfigResult; - -export type CommonXYAnnotationLayerConfigResult = - | AnnotationLayerConfigResult - | ExtendedAnnotationLayerConfigResult; diff --git a/src/plugins/chart_expressions/expression_xy/public/components/annotations.tsx b/src/plugins/chart_expressions/expression_xy/public/components/annotations.tsx index eeff4e349aba6..93eab02b5d0c7 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/annotations.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/annotations.tsx @@ -24,7 +24,6 @@ import type { FieldFormat } from '@kbn/field-formats-plugin/common'; import { defaultAnnotationColor } from '@kbn/event-annotation-plugin/public'; import type { AnnotationLayerArgs, - ExtendedAnnotationLayerArgs, CommonXYAnnotationLayerConfigResult, CollectiveConfig, } from '../../common'; @@ -49,7 +48,7 @@ export interface AnnotationsProps { } const groupVisibleConfigsByInterval = ( - layers: Array, + layers: AnnotationLayerArgs[], minInterval?: number, firstTimestamp?: number ) => { diff --git a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx index ba486eb338b03..1ff10c87266dd 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx @@ -30,7 +30,7 @@ import { Datatable } from '@kbn/expressions-plugin/common'; import { EmptyPlaceholder } from '@kbn/charts-plugin/public'; import { eventAnnotationServiceMock } from '@kbn/event-annotation-plugin/public/mocks'; import { EventAnnotationOutput } from '@kbn/event-annotation-plugin/common'; -import { AnnotationLayerConfigResult, DataLayerConfigResult } from '../../common'; +import { CommonXYAnnotationLayerConfigResult, DataLayerConfigResult } from '../../common'; import { LayerTypes } from '../../common/constants'; import { XyEndzones } from './x_domain'; import { @@ -214,8 +214,8 @@ describe('XYChart component', () => { args={{ ...multiLayerArgs, layers: [ - { ...multiLayerArgs.layers[0], table: table1 }, - { ...multiLayerArgs.layers[1], table: table2 }, + { ...(multiLayerArgs.layers[0] as DataLayerConfigResult), table: table1 }, + { ...(multiLayerArgs.layers[1] as DataLayerConfigResult), table: table2 }, ], }} /> @@ -2520,7 +2520,7 @@ describe('XYChart component', () => { lineStyle: 'dashed', lineWidth: 3, }; - const sampleAnnotationLayers: AnnotationLayerConfigResult[] = [ + const sampleAnnotationLayers: CommonXYAnnotationLayerConfigResult[] = [ { type: 'annotationLayer', layerType: LayerTypes.ANNOTATIONS, @@ -2531,7 +2531,6 @@ describe('XYChart component', () => { type: 'manual_event_annotation', }, ], - table: sampleArgs().data, }, ]; @@ -2551,7 +2550,7 @@ describe('XYChart component', () => { }); test('should render simplified annotation when hide is true', () => { const { args } = sampleArgsWithAnnotation(); - (args.layers[0] as AnnotationLayerConfigResult).hide = true; + (args.layers[0] as CommonXYAnnotationLayerConfigResult).hide = true; const component = mount(); expect(component.find('LineAnnotation')).toMatchSnapshot(); }); @@ -2561,7 +2560,6 @@ describe('XYChart component', () => { { type: 'annotationLayer', layerType: LayerTypes.ANNOTATIONS, - table: sampleArgs().data, annotations: [ sampleStyledAnnotation, { ...sampleStyledAnnotation, time: '2022-03-18T08:25:00.020Z', label: 'Event 2' }, @@ -2597,12 +2595,10 @@ describe('XYChart component', () => { type: 'annotationLayer', layerType: LayerTypes.ANNOTATIONS, annotations: [sampleStyledAnnotation], - table: sampleArgs().data, }, { type: 'annotationLayer', layerType: LayerTypes.ANNOTATIONS, - table: sampleArgs().data, annotations: [ { ...sampleStyledAnnotation, @@ -2628,7 +2624,6 @@ describe('XYChart component', () => { { type: 'annotationLayer', layerType: LayerTypes.ANNOTATIONS, - table: sampleArgs().data, annotations: [ sampleStyledAnnotation, { ...sampleStyledAnnotation, time: '2022-03-18T08:30:00.020Z', label: 'Event 2' }, diff --git a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx index 744566c263d09..7acd53fea9c03 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx @@ -154,7 +154,7 @@ export function XYChart({ ); const handleCursorUpdate = useActiveCursor(chartsActiveCursorService, chartRef, { - datatables: layers.map(({ table }) => table), + datatables: filteredLayers.map(({ table }) => table), }); if (filteredLayers.length === 0) { @@ -229,7 +229,9 @@ export function XYChart({ axisSeries .map( (series) => - layers[series.layer].table.columns.find((column) => column.id === series.accessor)?.name + filteredLayers[series.layer].table.columns.find( + (column) => column.id === series.accessor + )?.name ) .filter((name) => Boolean(name))[0] ); diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/interval.test.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/interval.test.ts index 4bdd595db80c6..7234e921789a4 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/interval.test.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/interval.test.ts @@ -12,29 +12,30 @@ import { calculateMinInterval } from './interval'; describe('calculateMinInterval', () => { let xyProps: XYChartProps; - + let layer: DataLayerConfigResult; beforeEach(() => { const { layers, ...restArgs } = sampleArgs().args; xyProps = { args: { ...restArgs, layers } }; - - (xyProps.args.layers[0] as DataLayerConfigResult).xScaleType = 'time'; + layer = xyProps.args.layers[0] as DataLayerConfigResult; + layer.xScaleType = 'time'; }); it('should use first valid layer and determine interval', async () => { - xyProps.args.layers[0].table.columns[2].meta.source = 'esaggs'; - xyProps.args.layers[0].table.columns[2].meta.sourceParams = { + layer.table.columns[2].meta.source = 'esaggs'; + layer.table.columns[2].meta.sourceParams = { type: 'date_histogram', params: { used_interval: '5m', }, }; + xyProps.args.layers[0] = layer; const result = await calculateMinInterval(xyProps); expect(result).toEqual(5 * 60 * 1000); }); it('should return interval of number histogram if available on first x axis columns', async () => { - (xyProps.args.layers[0] as DataLayerConfigResult).xScaleType = 'linear'; - xyProps.args.layers[0].table.columns[2].meta = { + layer.xScaleType = 'linear'; + layer.table.columns[2].meta = { source: 'esaggs', type: 'number', field: 'someField', @@ -46,19 +47,22 @@ describe('calculateMinInterval', () => { }, }, }; + xyProps.args.layers[0] = layer; const result = await calculateMinInterval(xyProps); expect(result).toEqual(5); }); it('should return undefined if data table is empty', async () => { - xyProps.args.layers[0].table.rows = []; - xyProps.args.layers[0].table.columns[2].meta.source = 'esaggs'; - xyProps.args.layers[0].table.columns[2].meta.sourceParams = { + layer.table.rows = []; + layer.table.columns[2].meta.source = 'esaggs'; + layer.table.columns[2].meta.sourceParams = { type: 'date_histogram', params: { used_interval: '5m', }, }; + + xyProps.args.layers[0] = layer; const result = await calculateMinInterval(xyProps); expect(result).toEqual(undefined); }); @@ -69,13 +73,15 @@ describe('calculateMinInterval', () => { }); it('should return undefined if date column is not found', async () => { - xyProps.args.layers[0].table.columns.splice(2, 1); + layer.table.columns.splice(2, 1); + xyProps.args.layers[0] = layer; const result = await calculateMinInterval(xyProps); expect(result).toEqual(undefined); }); it('should return undefined if x axis is not a date', async () => { - (xyProps.args.layers[0] as DataLayerConfigResult).xScaleType = 'ordinal'; + layer.xScaleType = 'ordinal'; + xyProps.args.layers[0] = layer; xyProps.args.layers[0].table.columns.splice(2, 1); const result = await calculateMinInterval(xyProps); expect(result).toEqual(undefined); diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/layers.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/layers.ts index ce12eed0c87a8..4e11c7e52543d 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/layers.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/layers.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import { Datatable } from '@kbn/expressions-plugin/common'; import { CommonXYDataLayerConfigResult, CommonXYLayerConfigResult, @@ -16,7 +17,7 @@ import { isDataLayer, isReferenceLayer } from './visualization'; export function getFilteredLayers(layers: CommonXYLayerConfigResult[]) { return layers.filter( (layer): layer is CommonXYReferenceLineLayerConfigResult | CommonXYDataLayerConfigResult => { - const { table } = layer; + let table: Datatable | undefined; let accessors: string[] = []; let xAccessor: undefined | string | number; let splitAccessor: undefined | string | number; @@ -27,6 +28,7 @@ export function getFilteredLayers(layers: CommonXYLayerConfigResult[]) { } if (isDataLayer(layer) || isReferenceLayer(layer)) { + table = layer.table; accessors = layer.accessors; } diff --git a/src/plugins/chart_expressions/expression_xy/public/plugin.ts b/src/plugins/chart_expressions/expression_xy/public/plugin.ts index 2cda4e13ca97f..61b24c4293dca 100755 --- a/src/plugins/chart_expressions/expression_xy/public/plugin.ts +++ b/src/plugins/chart_expressions/expression_xy/public/plugin.ts @@ -28,7 +28,6 @@ import { referenceLineLayerFunction, extendedReferenceLineLayerFunction, annotationLayerFunction, - extendedAnnotationLayerFunction, labelsOrientationConfigFunction, axisTitlesVisibilityConfigFunction, } from '../common'; @@ -64,7 +63,6 @@ export class ExpressionXyPlugin { expressions.registerFunction(axisExtentConfigFunction); expressions.registerFunction(tickLabelsConfigFunction); expressions.registerFunction(annotationLayerFunction); - expressions.registerFunction(extendedAnnotationLayerFunction); expressions.registerFunction(labelsOrientationConfigFunction); expressions.registerFunction(referenceLineLayerFunction); expressions.registerFunction(extendedReferenceLineLayerFunction); diff --git a/src/plugins/chart_expressions/expression_xy/server/plugin.ts b/src/plugins/chart_expressions/expression_xy/server/plugin.ts index dd54685fd50d2..19bb3dc6cc624 100755 --- a/src/plugins/chart_expressions/expression_xy/server/plugin.ts +++ b/src/plugins/chart_expressions/expression_xy/server/plugin.ts @@ -25,7 +25,6 @@ import { extendedDataLayerFunction, extendedReferenceLineLayerFunction, layeredXyVisFunction, - extendedAnnotationLayerFunction, } from '../common'; import { SetupDeps } from './types'; @@ -42,7 +41,6 @@ export class ExpressionXyPlugin expressions.registerFunction(axisExtentConfigFunction); expressions.registerFunction(tickLabelsConfigFunction); expressions.registerFunction(annotationLayerFunction); - expressions.registerFunction(extendedAnnotationLayerFunction); expressions.registerFunction(labelsOrientationConfigFunction); expressions.registerFunction(referenceLineLayerFunction); expressions.registerFunction(extendedReferenceLineLayerFunction); diff --git a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts index 5a645082d11b3..0451140e1be19 100644 --- a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts +++ b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts @@ -381,11 +381,7 @@ export const buildExpression = ( ) ), ...validAnnotationsLayers.map((layer) => - annotationLayerToExpression( - layer, - eventAnnotationService, - datasourceExpressionsByLayers[layer.layerId] - ) + annotationLayerToExpression(layer, eventAnnotationService) ), ], }, @@ -427,18 +423,16 @@ const referenceLineLayerToExpression = ( const annotationLayerToExpression = ( layer: XYAnnotationLayerConfig, - eventAnnotationService: EventAnnotationServiceType, - datasourceExpression: Ast + eventAnnotationService: EventAnnotationServiceType ): Ast => { return { type: 'expression', chain: [ { type: 'function', - function: 'extendedAnnotationLayer', + function: 'annotationLayer', arguments: { hide: [Boolean(layer.hide)], - ...(datasourceExpression ? { table: [buildTableExpression(datasourceExpression)] } : {}), annotations: layer.annotations ? layer.annotations.map( (ann): Ast => From 612ce22167d8d1765316877ec2a93988f8ce02f5 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Tue, 19 Apr 2022 15:11:07 +0300 Subject: [PATCH 116/133] Changed the `convertActiveDataFromIndexesToLayers` location. --- .../reference_line_helpers.tsx | 39 ------------------ .../public/xy_visualization/visualization.tsx | 2 +- .../visualization_helpers.tsx | 40 +++++++++++++++++++ .../xy_config_panel/index.tsx | 6 +-- 4 files changed, 43 insertions(+), 44 deletions(-) diff --git a/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx b/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx index ccb066f49efc2..5ddb1fcc043e7 100644 --- a/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx @@ -254,45 +254,6 @@ function computeStaticValueForGroup( } } -/** - * Converts hashmap of tables, stored by layers' indexes - * (created at `layeredXyVis` expression function), to hashmap of tables, stored by layers' ids. Before, - * layers, passed to `xy` expression function contained layerIds. But it is impossible to continue using - * this approach any more, as far as the idea of multitable is going to be deprecated. - * @param activeData hashmap of tables, containing requested data. - * @param layers array of data visualization configuration. Each layer has its own table at the `activeData`. - * @returns new hashmap of tables, where all the tables are mapped by layerId. - */ -export const convertActiveDataFromIndexesToLayers = ( - activeData: Record | undefined, - layers: XYState['layers'] = [] -): Record | undefined => { - if (!activeData) { - return activeData; - } - - const indexesToLayerIds = layers.reduce>( - (layersWithIndexes, { layerId }, index) => - layerId ? { ...layersWithIndexes, [index]: layerId } : layersWithIndexes, - {} - ); - - const convertedActiveData = Object.entries(activeData).reduce< - Record - >((dataByLayerIds, [layerIndex, dataPerLayer]) => { - // if layer index doesn't exist at the map of layer index, it means, that is - // a layerId and should be mapped without conveting from index to layerId. - const index = Number(layerIndex); - const layerId = isNaN(index) ? layerIndex : indexesToLayerIds[index] ?? layerIndex; - return { - ...dataByLayerIds, - [layerId]: dataPerLayer, - }; - }, {}); - - return Object.keys(convertedActiveData).length ? convertedActiveData : undefined; -}; - export const getReferenceSupportedLayer = ( state?: XYState, frame?: Pick diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx index 93bee71a24e40..fc63030163544 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx @@ -41,7 +41,6 @@ import { toExpression, toPreviewExpression, getSortedAccessors } from './to_expr import { getAccessorColorConfig, getColorAssignments } from './color_assignment'; import { getColumnToLabelMap } from './state_helpers'; import { - convertActiveDataFromIndexesToLayers, getGroupsAvailableInData, getReferenceConfiguration, getReferenceSupportedLayer, @@ -55,6 +54,7 @@ import { } from './annotations/helpers'; import { checkXAccessorCompatibility, + convertActiveDataFromIndexesToLayers, defaultSeriesType, getAxisName, getDataLayers, diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization_helpers.tsx b/x-pack/plugins/lens/public/xy_visualization/visualization_helpers.tsx index d390d081258a5..87ba94408f075 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization_helpers.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/visualization_helpers.tsx @@ -8,6 +8,7 @@ import { i18n } from '@kbn/i18n'; import { uniq } from 'lodash'; import { SeriesType } from '@kbn/expression-xy-plugin/common'; +import { Datatable } from '@kbn/expressions-plugin/common'; import { DatasourceLayers, OperationMetadata, VisualizationType } from '../types'; import { State, @@ -337,3 +338,42 @@ export const isNumericMetric = (op: OperationMetadata) => export const isNumericDynamicMetric = (op: OperationMetadata) => isNumericMetric(op) && !op.isStaticValue; export const isBucketed = (op: OperationMetadata) => op.isBucketed; + +/** + * Converts hashmap of tables, stored by layers' indexes + * (created at `layeredXyVis` expression function), to hashmap of tables, stored by layers' ids. Before, + * layers, passed to `xy` expression function contained layerIds. But it is impossible to continue using + * this approach any more, as far as the idea of multitable is going to be deprecated. + * @param activeData hashmap of tables, containing requested data. + * @param layers array of data visualization configuration. Each layer has its own table at the `activeData`. + * @returns new hashmap of tables, where all the tables are mapped by layerId. + */ +export const convertActiveDataFromIndexesToLayers = ( + activeData: Record | undefined, + layers: XYState['layers'] = [] +): Record | undefined => { + if (!activeData) { + return activeData; + } + + const indexesToLayerIds = layers.reduce>( + (layersWithIndexes, { layerId }, index) => + layerId ? { ...layersWithIndexes, [index]: layerId } : layersWithIndexes, + {} + ); + + const convertedActiveData = Object.entries(activeData).reduce< + Record + >((dataByLayerIds, [layerIndex, dataPerLayer]) => { + // if layer index doesn't exist at the map of layer index, it means, that is + // a layerId and should be mapped without conveting from index to layerId. + const index = Number(layerIndex); + const layerId = isNaN(index) ? layerIndex : indexesToLayerIds[index] ?? layerIndex; + return { + ...dataByLayerIds, + [layerId]: dataPerLayer, + }; + }, {}); + + return Object.keys(convertedActiveData).length ? convertedActiveData : undefined; +}; diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/index.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/index.tsx index f4350beec5389..b61f4694f8a91 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/index.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/index.tsx @@ -21,7 +21,6 @@ import { getScaleType } from '../to_expression'; import { TooltipWrapper } from '../../shared_components'; import { getDefaultVisualValuesForLayer } from '../../shared_components/datasource_default_values'; import { getDataLayers } from '../visualization_helpers'; -import { convertActiveDataFromIndexesToLayers } from '../reference_line_helpers'; type UnwrapArray = T extends Array ? P : T; type AxesSettingsConfigKeys = keyof AxesSettingsConfig; @@ -119,9 +118,8 @@ export const XyToolbar = memo(function XyToolbar( const dataLayers = getDataLayers(state?.layers); const shouldRotate = state?.layers.length ? isHorizontalChart(state.layers) : false; - const activeData = convertActiveDataFromIndexesToLayers(frame.activeData, state.layers); - const axisGroups = getAxesConfiguration(dataLayers, shouldRotate, activeData); - const dataBounds = getDataBounds(activeData, axisGroups); + const axisGroups = getAxesConfiguration(dataLayers, shouldRotate, frame.activeData); + const dataBounds = getDataBounds(frame.activeData, axisGroups); const tickLabelsVisibilitySettings = { x: state?.tickLabelsVisibilitySettings?.x ?? true, From 9d63b48d4119858bff11f592451dd54925fead9c Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Tue, 19 Apr 2022 15:55:39 +0300 Subject: [PATCH 117/133] Added tests for convertActiveDataFromIndexesToLayers --- .../visualization_helpers.test.tsx | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 x-pack/plugins/lens/public/xy_visualization/visualization_helpers.test.tsx diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization_helpers.test.tsx b/x-pack/plugins/lens/public/xy_visualization/visualization_helpers.test.tsx new file mode 100644 index 0000000000000..a57a4397e4366 --- /dev/null +++ b/x-pack/plugins/lens/public/xy_visualization/visualization_helpers.test.tsx @@ -0,0 +1,75 @@ +/* + * 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 { Datatable } from '@kbn/expressions-plugin/common'; +import { XYDataLayerConfig, XYState } from './types'; +import { convertActiveDataFromIndexesToLayers } from './visualization_helpers'; + +const generateDatatable = (columnName: string): Datatable => ({ + type: 'datatable', + columns: [{ id: columnName, name: columnName, meta: { type: 'number' } }], + rows: [], +}); + +describe('#convertActiveDataFromIndexesToLayers', () => { + const partialLayer: Omit = { + layerType: 'data', + accessors: [], + seriesType: 'area', + }; + + const datatable1: Datatable = generateDatatable('first'); + const datatable2: Datatable = generateDatatable('second'); + const datatable3: Datatable = generateDatatable('third'); + const datatable4: Datatable = generateDatatable('fourth'); + const datatable5: Datatable = generateDatatable('fifth'); + + const activeData = { + 0: datatable1, + 1: datatable2, + 2: datatable3, + 3: datatable4, + }; + + const layers: XYState['layers'] = [ + { layerId: 'id1', ...partialLayer }, + { layerId: 'id2', ...partialLayer }, + { layerId: 'id3', ...partialLayer }, + { layerId: 'id4', ...partialLayer }, + ]; + + it('should convert activeData indexes to layerIds', () => { + const result = convertActiveDataFromIndexesToLayers(activeData, layers); + expect(result).toStrictEqual({ + id1: datatable1, + id2: datatable2, + id3: datatable3, + id4: datatable4, + }); + }); + + it('should not remap layerIds from activeData', () => { + const result = convertActiveDataFromIndexesToLayers({ ...activeData, id0: datatable5 }, layers); + expect(result).toStrictEqual({ + id1: datatable1, + id2: datatable2, + id3: datatable3, + id4: datatable4, + id0: datatable5, + }); + }); + + it('should return undefined if activeData is empty', () => { + const result = convertActiveDataFromIndexesToLayers({}, layers); + expect(result).toBeUndefined(); + }); + + it('should skip if no activeData is passed', () => { + const result = convertActiveDataFromIndexesToLayers(undefined, []); + expect(result).toBeUndefined(); + }); +}); From a947ff70c960c87d069dcdecee0ac0728d0da2c1 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Tue, 19 Apr 2022 17:28:32 +0300 Subject: [PATCH 118/133] Reduced the bundle size a little bit. --- .../expression_functions/common_args_xy.ts | 122 ++++++++++++++ .../expression_functions/layered_xy_vis.ts | 140 +--------------- .../common/expression_functions/xy_vis.ts | 154 +----------------- .../expression_xy/common/i18n/index.tsx | 88 ++++++++++ .../expression_xy/common/index.ts | 18 -- .../expression_xy/public/plugin.ts | 2 +- .../expression_xy/server/plugin.ts | 2 +- 7 files changed, 223 insertions(+), 303 deletions(-) create mode 100644 src/plugins/chart_expressions/expression_xy/common/expression_functions/common_args_xy.ts diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_args_xy.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_args_xy.ts new file mode 100644 index 0000000000000..1713e8927918e --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_args_xy.ts @@ -0,0 +1,122 @@ +/* + * 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, ExpressionFunctionDefinition } from '@kbn/expressions-plugin/common'; +import { + AXIS_EXTENT_CONFIG, + AXIS_TITLES_VISIBILITY_CONFIG, + EndValues, + FittingFunctions, + GRID_LINES_CONFIG, + LABELS_ORIENTATION_CONFIG, + LEGEND_CONFIG, + TICK_LABELS_CONFIG, + ValueLabelModes, + XYCurveTypes, +} from '../constants'; +import { strings } from '../i18n'; +import { LayeredXYArgs, XYArgs, XYRender } from '../types'; + +type XYFnArgs = ExpressionFunctionDefinition< + string, + Datatable, + XYArgs | LayeredXYArgs, + XYRender +>['args']; + +export const commonArgsXY: Omit< + XYFnArgs, + 'dataLayers' | 'referenceLineLayers' | 'annotationLayers' | 'layers' +> = { + xTitle: { + types: ['string'], + help: strings.getXTitleHelp(), + }, + yTitle: { + types: ['string'], + help: strings.getYTitleHelp(), + }, + yRightTitle: { + types: ['string'], + help: strings.getYRightTitleHelp(), + }, + yLeftExtent: { + types: [AXIS_EXTENT_CONFIG], + help: strings.getYLeftExtentHelp(), + }, + yRightExtent: { + types: [AXIS_EXTENT_CONFIG], + help: strings.getYRightExtentHelp(), + }, + legend: { + types: [LEGEND_CONFIG], + help: strings.getLegendHelp(), + }, + fittingFunction: { + types: ['string'], + options: [...Object.values(FittingFunctions)], + help: strings.getFittingFunctionHelp(), + strict: true, + }, + endValue: { + types: ['string'], + options: [...Object.values(EndValues)], + help: strings.getEndValueHelp(), + }, + emphasizeFitting: { + types: ['boolean'], + default: false, + help: '', + }, + valueLabels: { + types: ['string'], + options: [...Object.values(ValueLabelModes)], + help: strings.getValueLabelsHelp(), + strict: true, + }, + tickLabelsVisibilitySettings: { + types: [TICK_LABELS_CONFIG], + help: strings.getTickLabelsVisibilitySettingsHelp(), + }, + labelsOrientation: { + types: [LABELS_ORIENTATION_CONFIG], + help: strings.getLabelsOrientationHelp(), + }, + gridlinesVisibilitySettings: { + types: [GRID_LINES_CONFIG], + help: strings.getGridlinesVisibilitySettingsHelp(), + }, + axisTitlesVisibilitySettings: { + types: [AXIS_TITLES_VISIBILITY_CONFIG], + help: strings.getAxisTitlesVisibilitySettingsHelp(), + }, + curveType: { + types: ['string'], + options: [...Object.values(XYCurveTypes)], + help: strings.getCurveTypeHelp(), + strict: true, + }, + fillOpacity: { + types: ['number'], + help: strings.getFillOpacityHelp(), + }, + hideEndzones: { + types: ['boolean'], + default: false, + help: strings.getHideEndzonesHelp(), + }, + valuesInLegend: { + types: ['boolean'], + default: false, + help: strings.getValuesInLegendHelp(), + }, + ariaLabel: { + types: ['string'], + help: strings.getAriaLabelHelp(), + }, +}; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts index ec75648c85a9b..e3bb9283101a2 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts @@ -10,23 +10,15 @@ import { i18n } from '@kbn/i18n'; import type { ExpressionFunctionDefinition, Datatable } from '@kbn/expressions-plugin'; import { LayeredXYArgs, XYExtendedLayerConfigResult, XYRender } from '../types'; import { - XYCurveTypes, - LEGEND_CONFIG, - ValueLabelModes, - FittingFunctions, - GRID_LINES_CONFIG, XY_VIS_RENDERER, - AXIS_EXTENT_CONFIG, - TICK_LABELS_CONFIG, - LABELS_ORIENTATION_CONFIG, - AXIS_TITLES_VISIBILITY_CONFIG, EXTENDED_DATA_LAYER, EXTENDED_REFERENCE_LINE_LAYER, LAYERED_XY_VIS, - EndValues, ANNOTATION_LAYER, } from '../constants'; import { logDatatables } from '../utils'; +import { commonArgsXY } from './common_args_xy'; +import { strings } from '../i18n'; export const layeredXyVisFunction: ExpressionFunctionDefinition< typeof LAYERED_XY_VIS, @@ -37,98 +29,9 @@ export const layeredXyVisFunction: ExpressionFunctionDefinition< name: LAYERED_XY_VIS, type: 'render', inputTypes: ['datatable'], - help: i18n.translate('expressionXY.layeredXyVis.help', { - defaultMessage: 'An X/Y chart', - }), + help: strings.getXYHelp(), args: { - xTitle: { - types: ['string'], - help: i18n.translate('expressionXY.layeredXyVis.xTitle.help', { - defaultMessage: 'X axis title', - }), - }, - yTitle: { - types: ['string'], - help: i18n.translate('expressionXY.layeredXyVis.yLeftTitle.help', { - defaultMessage: 'Y left axis title', - }), - }, - yRightTitle: { - types: ['string'], - help: i18n.translate('expressionXY.layeredXyVis.yRightTitle.help', { - defaultMessage: 'Y right axis title', - }), - }, - yLeftExtent: { - types: [AXIS_EXTENT_CONFIG], - help: i18n.translate('expressionXY.layeredXyVis.yLeftExtent.help', { - defaultMessage: 'Y left axis extents', - }), - }, - yRightExtent: { - types: [AXIS_EXTENT_CONFIG], - help: i18n.translate('expressionXY.layeredXyVis.yRightExtent.help', { - defaultMessage: 'Y right axis extents', - }), - }, - legend: { - types: [LEGEND_CONFIG], - help: i18n.translate('expressionXY.layeredXyVis.legend.help', { - defaultMessage: 'Configure the chart legend.', - }), - }, - fittingFunction: { - types: ['string'], - options: [...Object.values(FittingFunctions)], - help: i18n.translate('expressionXY.layeredXyVis.fittingFunction.help', { - defaultMessage: 'Define how missing values are treated', - }), - strict: true, - }, - endValue: { - types: ['string'], - options: [...Object.values(EndValues)], - help: i18n.translate('expressionXY.layeredXyVis.endValue.help', { - defaultMessage: 'End value', - }), - }, - emphasizeFitting: { - types: ['boolean'], - default: false, - help: '', - }, - valueLabels: { - types: ['string'], - options: [...Object.values(ValueLabelModes)], - help: i18n.translate('expressionXY.layeredXyVis.valueLabels.help', { - defaultMessage: 'Value labels mode', - }), - strict: true, - }, - tickLabelsVisibilitySettings: { - types: [TICK_LABELS_CONFIG], - help: i18n.translate('expressionXY.layeredXyVis.tickLabelsVisibilitySettings.help', { - defaultMessage: 'Show x and y axes tick labels', - }), - }, - labelsOrientation: { - types: [LABELS_ORIENTATION_CONFIG], - help: i18n.translate('expressionXY.layeredXyVis.labelsOrientation.help', { - defaultMessage: 'Defines the rotation of the axis labels', - }), - }, - gridlinesVisibilitySettings: { - types: [GRID_LINES_CONFIG], - help: i18n.translate('expressionXY.layeredXyVis.gridlinesVisibilitySettings.help', { - defaultMessage: 'Show x and y axes gridlines', - }), - }, - axisTitlesVisibilitySettings: { - types: [AXIS_TITLES_VISIBILITY_CONFIG], - help: i18n.translate('expressionXY.layeredXyVis.axisTitlesVisibilitySettings.help', { - defaultMessage: 'Show x and y axes titles', - }), - }, + ...commonArgsXY, layers: { types: [EXTENDED_DATA_LAYER, EXTENDED_REFERENCE_LINE_LAYER, ANNOTATION_LAYER], help: i18n.translate('expressionXY.layeredXyVis.layers.help', { @@ -136,41 +39,6 @@ export const layeredXyVisFunction: ExpressionFunctionDefinition< }), multi: true, }, - curveType: { - types: ['string'], - options: [...Object.values(XYCurveTypes)], - help: i18n.translate('expressionXY.layeredXyVis.curveType.help', { - defaultMessage: 'Define how curve type is rendered for a line chart', - }), - strict: true, - }, - fillOpacity: { - types: ['number'], - help: i18n.translate('expressionXY.layeredXyVis.fillOpacity.help', { - defaultMessage: 'Define the area chart fill opacity', - }), - }, - hideEndzones: { - types: ['boolean'], - default: false, - help: i18n.translate('expressionXY.layeredXyVis.hideEndzones.help', { - defaultMessage: 'Hide endzone markers for partial data', - }), - }, - valuesInLegend: { - types: ['boolean'], - default: false, - help: i18n.translate('expressionXY.layeredXyVis.valuesInLegend.help', { - defaultMessage: 'Show values in legend', - }), - }, - ariaLabel: { - types: ['string'], - help: i18n.translate('expressionXY.layeredXyVis.ariaLabel.help', { - defaultMessage: 'Specifies the aria label of the xy chart', - }), - required: false, - }, }, fn(data, args, handlers) { const layers = (args.layers ?? []).filter( diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts index bce212538437d..d6e27c398f572 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts @@ -19,23 +19,16 @@ import { import { XY_VIS, DATA_LAYER, - XYCurveTypes, - LEGEND_CONFIG, ValueLabelModes, - FittingFunctions, - GRID_LINES_CONFIG, XY_VIS_RENDERER, - AXIS_EXTENT_CONFIG, - TICK_LABELS_CONFIG, REFERENCE_LINE_LAYER, - LABELS_ORIENTATION_CONFIG, - AXIS_TITLES_VISIBILITY_CONFIG, - EndValues, ANNOTATION_LAYER, LayerTypes, AxisExtentModes, } from '../constants'; import { getLayerDimensions } from '../utils'; +import { strings } from '../i18n'; +import { commonArgsXY } from './common_args_xy'; const errors = { extendBoundsAreInvalidError: () => @@ -92,157 +85,24 @@ export const xyVisFunction: ExpressionFunctionDefinition< name: XY_VIS, type: 'render', inputTypes: ['datatable'], - help: i18n.translate('expressionXY.xyVis.help', { - defaultMessage: 'An X/Y chart', - }), + help: strings.getXYHelp(), args: { - xTitle: { - types: ['string'], - help: i18n.translate('expressionXY.xyVis.xTitle.help', { - defaultMessage: 'X axis title', - }), - }, - yTitle: { - types: ['string'], - help: i18n.translate('expressionXY.xyVis.yLeftTitle.help', { - defaultMessage: 'Y left axis title', - }), - }, - yRightTitle: { - types: ['string'], - help: i18n.translate('expressionXY.xyVis.yRightTitle.help', { - defaultMessage: 'Y right axis title', - }), - }, - yLeftExtent: { - types: [AXIS_EXTENT_CONFIG], - help: i18n.translate('expressionXY.xyVis.yLeftExtent.help', { - defaultMessage: 'Y left axis extents', - }), - default: `{${AXIS_EXTENT_CONFIG}}`, - }, - yRightExtent: { - types: [AXIS_EXTENT_CONFIG], - help: i18n.translate('expressionXY.xyVis.yRightExtent.help', { - defaultMessage: 'Y right axis extents', - }), - default: `{${AXIS_EXTENT_CONFIG}}`, - }, - legend: { - types: [LEGEND_CONFIG], - help: i18n.translate('expressionXY.xyVis.legend.help', { - defaultMessage: 'Configure the chart legend.', - }), - default: `{${LEGEND_CONFIG}}`, - }, - fittingFunction: { - types: ['string'], - options: [...Object.values(FittingFunctions)], - help: i18n.translate('expressionXY.xyVis.fittingFunction.help', { - defaultMessage: 'Define how missing values are treated', - }), - strict: true, - }, - endValue: { - types: ['string'], - options: [...Object.values(EndValues)], - help: i18n.translate('expressionXY.xyVis.endValue.help', { - defaultMessage: 'End value', - }), - strict: true, - }, - emphasizeFitting: { - types: ['boolean'], - default: false, - help: '', - }, - valueLabels: { - types: ['string'], - options: [...Object.values(ValueLabelModes)], - help: i18n.translate('expressionXY.xyVis.valueLabels.help', { - defaultMessage: 'Value labels mode', - }), - strict: true, - default: ValueLabelModes.HIDE, - }, - tickLabelsVisibilitySettings: { - types: [TICK_LABELS_CONFIG], - help: i18n.translate('expressionXY.xyVis.tickLabelsVisibilitySettings.help', { - defaultMessage: 'Show x and y axes tick labels', - }), - }, - labelsOrientation: { - types: [LABELS_ORIENTATION_CONFIG], - help: i18n.translate('expressionXY.xyVis.labelsOrientation.help', { - defaultMessage: 'Defines the rotation of the axis labels', - }), - }, - gridlinesVisibilitySettings: { - types: [GRID_LINES_CONFIG], - help: i18n.translate('expressionXY.xyVis.gridlinesVisibilitySettings.help', { - defaultMessage: 'Show x and y axes gridlines', - }), - }, - axisTitlesVisibilitySettings: { - types: [AXIS_TITLES_VISIBILITY_CONFIG], - help: i18n.translate('expressionXY.xyVis.axisTitlesVisibilitySettings.help', { - defaultMessage: 'Show x and y axes titles', - }), - }, + ...commonArgsXY, dataLayers: { types: [DATA_LAYER], - help: i18n.translate('expressionXY.xyVis.dataLayer.help', { - defaultMessage: 'Data layer of visual series', - }), + help: strings.getDataLayerHelp(), multi: true, }, referenceLineLayers: { types: [REFERENCE_LINE_LAYER], - help: i18n.translate('expressionXY.xyVis.referenceLineLayer.help', { - defaultMessage: 'Reference line layer', - }), + help: strings.getReferenceLineLayerHelp(), multi: true, }, annotationLayers: { types: [ANNOTATION_LAYER], - help: i18n.translate('expressionXY.xyVis.annotationLayer.help', { - defaultMessage: 'Annotation layer', - }), + help: strings.getAnnotationLayerHelp(), multi: true, }, - curveType: { - types: ['string'], - options: [...Object.values(XYCurveTypes)], - help: i18n.translate('expressionXY.xyVis.curveType.help', { - defaultMessage: 'Define how curve type is rendered for a line chart', - }), - }, - fillOpacity: { - types: ['number'], - help: i18n.translate('expressionXY.xyVis.fillOpacity.help', { - defaultMessage: 'Define the area chart fill opacity', - }), - }, - hideEndzones: { - types: ['boolean'], - default: false, - help: i18n.translate('expressionXY.xyVis.hideEndzones.help', { - defaultMessage: 'Hide endzone markers for partial data', - }), - }, - valuesInLegend: { - types: ['boolean'], - default: false, - help: i18n.translate('expressionXY.xyVis.valuesInLegend.help', { - defaultMessage: 'Show values in legend', - }), - }, - ariaLabel: { - types: ['string'], - help: i18n.translate('expressionXY.xyVis.ariaLabel.help', { - defaultMessage: 'Specifies the aria label of the xy chart', - }), - }, }, fn(data, args, handlers) { const { dataLayers = [], referenceLineLayers = [], annotationLayers = [], ...restArgs } = args; diff --git a/src/plugins/chart_expressions/expression_xy/common/i18n/index.tsx b/src/plugins/chart_expressions/expression_xy/common/i18n/index.tsx index 229b83551e0f0..c04a0cf485bfd 100644 --- a/src/plugins/chart_expressions/expression_xy/common/i18n/index.tsx +++ b/src/plugins/chart_expressions/expression_xy/common/i18n/index.tsx @@ -9,6 +9,10 @@ import { i18n } from '@kbn/i18n'; export const strings = { + getXYHelp: () => + i18n.translate('expressionXY.xyVis.help', { + defaultMessage: 'An X/Y chart', + }), getMetricHelp: () => i18n.translate('expressionXY.xyVis.logDatatable.metric', { defaultMessage: 'Vertical axis', @@ -25,4 +29,88 @@ export const strings = { i18n.translate('expressionXY.xyVis.logDatatable.breakDown', { defaultMessage: 'Break down by', }), + getXTitleHelp: () => + i18n.translate('expressionXY.xyVis.xTitle.help', { + defaultMessage: 'X axis title', + }), + getYTitleHelp: () => + i18n.translate('expressionXY.xyVis.yLeftTitle.help', { + defaultMessage: 'Y left axis title', + }), + getYRightTitleHelp: () => + i18n.translate('expressionXY.xyVis.yRightTitle.help', { + defaultMessage: 'Y right axis title', + }), + getYLeftExtentHelp: () => + i18n.translate('expressionXY.xyVis.yLeftExtent.help', { + defaultMessage: 'Y left axis extents', + }), + getYRightExtentHelp: () => + i18n.translate('expressionXY.xyVis.yRightExtent.help', { + defaultMessage: 'Y right axis extents', + }), + getLegendHelp: () => + i18n.translate('expressionXY.xyVis.legend.help', { + defaultMessage: 'Configure the chart legend.', + }), + getFittingFunctionHelp: () => + i18n.translate('expressionXY.xyVis.fittingFunction.help', { + defaultMessage: 'Define how missing values are treated', + }), + getEndValueHelp: () => + i18n.translate('expressionXY.xyVis.endValue.help', { + defaultMessage: 'End value', + }), + getValueLabelsHelp: () => + i18n.translate('expressionXY.xyVis.valueLabels.help', { + defaultMessage: 'Value labels mode', + }), + getTickLabelsVisibilitySettingsHelp: () => + i18n.translate('expressionXY.xyVis.tickLabelsVisibilitySettings.help', { + defaultMessage: 'Show x and y axes tick labels', + }), + getLabelsOrientationHelp: () => + i18n.translate('expressionXY.xyVis.labelsOrientation.help', { + defaultMessage: 'Defines the rotation of the axis labels', + }), + getGridlinesVisibilitySettingsHelp: () => + i18n.translate('expressionXY.xyVis.gridlinesVisibilitySettings.help', { + defaultMessage: 'Show x and y axes gridlines', + }), + getAxisTitlesVisibilitySettingsHelp: () => + i18n.translate('expressionXY.xyVis.axisTitlesVisibilitySettings.help', { + defaultMessage: 'Show x and y axes titles', + }), + getDataLayerHelp: () => + i18n.translate('expressionXY.xyVis.dataLayer.help', { + defaultMessage: 'Data layer of visual series', + }), + getReferenceLineLayerHelp: () => + i18n.translate('expressionXY.xyVis.referenceLineLayer.help', { + defaultMessage: 'Reference line layer', + }), + getAnnotationLayerHelp: () => + i18n.translate('expressionXY.xyVis.annotationLayer.help', { + defaultMessage: 'Annotation layer', + }), + getCurveTypeHelp: () => + i18n.translate('expressionXY.xyVis.curveType.help', { + defaultMessage: 'Define how curve type is rendered for a line chart', + }), + getFillOpacityHelp: () => + i18n.translate('expressionXY.xyVis.fillOpacity.help', { + defaultMessage: 'Define the area chart fill opacity', + }), + getHideEndzonesHelp: () => + i18n.translate('expressionXY.xyVis.hideEndzones.help', { + defaultMessage: 'Hide endzone markers for partial data', + }), + getValuesInLegendHelp: () => + i18n.translate('expressionXY.xyVis.valuesInLegend.help', { + defaultMessage: 'Show values in legend', + }), + getAriaLabelHelp: () => + i18n.translate('expressionXY.xyVis.ariaLabel.help', { + defaultMessage: 'Specifies the aria label of the xy chart', + }), }; diff --git a/src/plugins/chart_expressions/expression_xy/common/index.ts b/src/plugins/chart_expressions/expression_xy/common/index.ts index 2fbb6d5ef6c87..d3ebe9abb812d 100755 --- a/src/plugins/chart_expressions/expression_xy/common/index.ts +++ b/src/plugins/chart_expressions/expression_xy/common/index.ts @@ -9,24 +9,6 @@ export const PLUGIN_ID = 'expressionXy'; export const PLUGIN_NAME = 'expressionXy'; -export { - xyVisFunction, - layeredXyVisFunction, - yAxisConfigFunction, - extendedYAxisConfigFunction, - legendConfigFunction, - gridlinesConfigFunction, - dataLayerFunction, - annotationLayerFunction, - extendedDataLayerFunction, - axisExtentConfigFunction, - tickLabelsConfigFunction, - labelsOrientationConfigFunction, - referenceLineLayerFunction, - extendedReferenceLineLayerFunction, - axisTitlesVisibilityConfigFunction, -} from './expression_functions'; - export type { XYArgs, YConfig, diff --git a/src/plugins/chart_expressions/expression_xy/public/plugin.ts b/src/plugins/chart_expressions/expression_xy/public/plugin.ts index 61b24c4293dca..ee78400e35831 100755 --- a/src/plugins/chart_expressions/expression_xy/public/plugin.ts +++ b/src/plugins/chart_expressions/expression_xy/public/plugin.ts @@ -30,7 +30,7 @@ import { annotationLayerFunction, labelsOrientationConfigFunction, axisTitlesVisibilityConfigFunction, -} from '../common'; +} from '../common/expression_functions'; import { GetStartDepsFn, getXyChartRenderer } from './expression_renderers'; export interface XYPluginStartDependencies { diff --git a/src/plugins/chart_expressions/expression_xy/server/plugin.ts b/src/plugins/chart_expressions/expression_xy/server/plugin.ts index 19bb3dc6cc624..9350279f18d03 100755 --- a/src/plugins/chart_expressions/expression_xy/server/plugin.ts +++ b/src/plugins/chart_expressions/expression_xy/server/plugin.ts @@ -25,7 +25,7 @@ import { extendedDataLayerFunction, extendedReferenceLineLayerFunction, layeredXyVisFunction, -} from '../common'; +} from '../common/expression_functions'; import { SetupDeps } from './types'; export class ExpressionXyPlugin From 9eaecc1978bc343c2daa600c537f624e7c72f1df Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Tue, 19 Apr 2022 18:36:58 +0300 Subject: [PATCH 119/133] Reused strings and args. --- .../common_data_layer_args.ts | 72 ++++++++++++ .../common_reference_line_layer_args.ts | 30 +++++ .../{common_args_xy.ts => common_xy_args.ts} | 14 +-- .../common_y_config_args.ts | 33 ++++++ .../common/expression_functions/data_layer.ts | 107 ++--------------- .../extended_data_layer.ts | 109 ++---------------- .../extended_reference_line_layer.ts | 44 ++----- .../extended_y_axis_config.ts | 38 +----- .../expression_functions/layered_xy_vis.ts | 14 +-- .../expression_functions/legend_config.ts | 10 +- .../reference_line_layer.ts | 42 ++----- .../common/expression_functions/xy_vis.ts | 15 +-- .../expression_functions/y_axis_config.ts | 42 ++----- .../expression_xy/common/i18n/index.tsx | 80 +++++++++++++ .../common/types/expression_functions.ts | 54 ++++++++- 15 files changed, 325 insertions(+), 379 deletions(-) create mode 100644 src/plugins/chart_expressions/expression_xy/common/expression_functions/common_data_layer_args.ts create mode 100644 src/plugins/chart_expressions/expression_xy/common/expression_functions/common_reference_line_layer_args.ts rename src/plugins/chart_expressions/expression_xy/common/expression_functions/{common_args_xy.ts => common_xy_args.ts} (89%) create mode 100644 src/plugins/chart_expressions/expression_xy/common/expression_functions/common_y_config_args.ts diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_data_layer_args.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_data_layer_args.ts new file mode 100644 index 0000000000000..85beee8bfb59f --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_data_layer_args.ts @@ -0,0 +1,72 @@ +/* + * 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 { SeriesTypes, XScaleTypes, YScaleTypes, Y_CONFIG } from '../constants'; +import { strings } from '../i18n'; +import { DataLayerFn, ExtendedDataLayerFn } from '../types'; + +type CommonDataLayerFn = DataLayerFn | ExtendedDataLayerFn; + +export const commonDataLayerArgs: Omit = { + hide: { + types: ['boolean'], + default: false, + help: strings.getHideHelp(), + }, + xAccessor: { + types: ['string'], + help: strings.getXAccessorHelp(), + }, + seriesType: { + types: ['string'], + options: [...Object.values(SeriesTypes)], + help: strings.getSeriesTypeHelp(), + required: true, + strict: true, + }, + xScaleType: { + options: [...Object.values(XScaleTypes)], + help: strings.getXScaleTypeHelp(), + default: XScaleTypes.ORDINAL, + strict: true, + }, + isHistogram: { + types: ['boolean'], + default: false, + help: strings.getIsHistogramHelp(), + }, + yScaleType: { + options: [...Object.values(YScaleTypes)], + help: strings.getYScaleTypeHelp(), + default: YScaleTypes.LINEAR, + strict: true, + }, + splitAccessor: { + types: ['string'], + help: strings.getSplitAccessorHelp(), + }, + accessors: { + types: ['string'], + help: strings.getAccessorsHelp(), + multi: true, + }, + yConfig: { + types: [Y_CONFIG], + help: strings.getYConfigHelp(), + multi: true, + }, + columnToLabel: { + types: ['string'], + help: strings.getColumnToLabelHelp(), + }, + palette: { + types: ['palette', 'system_palette'], + help: strings.getPaletteHelp(), + default: '{palette}', + }, +}; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_reference_line_layer_args.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_reference_line_layer_args.ts new file mode 100644 index 0000000000000..9e538039b4cb7 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_reference_line_layer_args.ts @@ -0,0 +1,30 @@ +/* + * 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 { EXTENDED_Y_CONFIG } from '../constants'; +import { strings } from '../i18n'; +import { ReferenceLineLayerFn, ExtendedReferenceLineLayerFn } from '../types'; + +type CommonReferenceLineLayerFn = ReferenceLineLayerFn | ExtendedReferenceLineLayerFn; + +export const commonReferenceLineLayerArgs: Omit = { + accessors: { + types: ['string'], + help: strings.getRLAccessorsHelp(), + multi: true, + }, + yConfig: { + types: [EXTENDED_Y_CONFIG], + help: strings.getRLYConfigHelp(), + multi: true, + }, + columnToLabel: { + types: ['string'], + help: strings.getColumnToLabelHelp(), + }, +}; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_args_xy.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_xy_args.ts similarity index 89% rename from src/plugins/chart_expressions/expression_xy/common/expression_functions/common_args_xy.ts rename to src/plugins/chart_expressions/expression_xy/common/expression_functions/common_xy_args.ts index 1713e8927918e..535c8c6e3357b 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_args_xy.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_xy_args.ts @@ -6,7 +6,6 @@ * Side Public License, v 1. */ -import { Datatable, ExpressionFunctionDefinition } from '@kbn/expressions-plugin/common'; import { AXIS_EXTENT_CONFIG, AXIS_TITLES_VISIBILITY_CONFIG, @@ -20,17 +19,12 @@ import { XYCurveTypes, } from '../constants'; import { strings } from '../i18n'; -import { LayeredXYArgs, XYArgs, XYRender } from '../types'; +import { LayeredXyVisFn, XyVisFn } from '../types'; -type XYFnArgs = ExpressionFunctionDefinition< - string, - Datatable, - XYArgs | LayeredXYArgs, - XYRender ->['args']; +type CommonXYFn = XyVisFn | LayeredXyVisFn; -export const commonArgsXY: Omit< - XYFnArgs, +export const commonXYArgs: Omit< + CommonXYFn['args'], 'dataLayers' | 'referenceLineLayers' | 'annotationLayers' | 'layers' > = { xTitle: { diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_y_config_args.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_y_config_args.ts new file mode 100644 index 0000000000000..3a90232cc47d3 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_y_config_args.ts @@ -0,0 +1,33 @@ +/* + * 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 { YAxisModes } from '../constants'; +import { strings } from '../i18n'; +import { YConfigFn, ExtendedYConfigFn } from '../types'; + +type CommonYConfigFn = YConfigFn | ExtendedYConfigFn; + +export const commonYConfigArgs: Pick< + CommonYConfigFn['args'], + 'forAccessor' | 'axisMode' | 'color' +> = { + forAccessor: { + types: ['string'], + help: strings.getForAccessorHelp(), + }, + axisMode: { + types: ['string'], + options: [...Object.values(YAxisModes)], + help: strings.getAxisModeHelp(), + strict: true, + }, + color: { + types: ['string'], + help: strings.getColorHelp(), + }, +}; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer.ts index e7f6480a08db1..f36a0ea4c101f 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer.ts @@ -6,111 +6,18 @@ * Side Public License, v 1. */ -import { i18n } from '@kbn/i18n'; -import type { Datatable, ExpressionFunctionDefinition } from '@kbn/expressions-plugin/common'; -import { DataLayerArgs, DataLayerConfigResult } from '../types'; -import { - DATA_LAYER, - LayerTypes, - SeriesTypes, - XScaleTypes, - YScaleTypes, - Y_CONFIG, -} from '../constants'; +import { DataLayerFn } from '../types'; +import { DATA_LAYER, LayerTypes } from '../constants'; +import { strings } from '../i18n'; +import { commonDataLayerArgs } from './common_data_layer_args'; -export const dataLayerFunction: ExpressionFunctionDefinition< - typeof DATA_LAYER, - Datatable, - DataLayerArgs, - DataLayerConfigResult -> = { +export const dataLayerFunction: DataLayerFn = { name: DATA_LAYER, aliases: [], type: DATA_LAYER, - help: i18n.translate('expressionXY.dataLayer.help', { - defaultMessage: `Configure a layer in the xy chart`, - }), + help: strings.getDataLayerFnHelp(), inputTypes: ['datatable'], - args: { - hide: { - types: ['boolean'], - default: false, - help: i18n.translate('expressionXY.dataLayer.hide.help', { - defaultMessage: 'Show / hide axis', - }), - }, - xAccessor: { - types: ['string'], - help: i18n.translate('expressionXY.dataLayer.xAccessor.help', { - defaultMessage: 'X-axis', - }), - }, - seriesType: { - types: ['string'], - options: [...Object.values(SeriesTypes)], - help: i18n.translate('expressionXY.dataLayer.seriesType.help', { - defaultMessage: 'The type of chart to display.', - }), - required: true, - strict: true, - }, - xScaleType: { - options: [...Object.values(XScaleTypes)], - help: i18n.translate('expressionXY.dataLayer.xScaleType.help', { - defaultMessage: 'The scale type of the x axis', - }), - default: XScaleTypes.ORDINAL, - strict: true, - }, - isHistogram: { - types: ['boolean'], - default: false, - help: i18n.translate('expressionXY.dataLayer.isHistogram.help', { - defaultMessage: 'Whether to layout the chart as a histogram', - }), - }, - yScaleType: { - options: [...Object.values(YScaleTypes)], - help: i18n.translate('expressionXY.dataLayer.yScaleType.help', { - defaultMessage: 'The scale type of the y axes', - }), - default: YScaleTypes.LINEAR, - strict: true, - }, - splitAccessor: { - types: ['string'], - help: i18n.translate('expressionXY.dataLayer.splitAccessor.help', { - defaultMessage: 'The column to split by', - }), - }, - accessors: { - types: ['string'], - help: i18n.translate('expressionXY.dataLayer.accessors.help', { - defaultMessage: 'The columns to display on the y axis.', - }), - multi: true, - }, - yConfig: { - types: [Y_CONFIG], - help: i18n.translate('expressionXY.dataLayer.yConfig.help', { - defaultMessage: 'Additional configuration for y axes', - }), - multi: true, - }, - columnToLabel: { - types: ['string'], - help: i18n.translate('expressionXY.dataLayer.columnToLabel.help', { - defaultMessage: 'JSON key-value pairs of column ID to label', - }), - }, - palette: { - types: ['palette', 'system_palette'], - help: i18n.translate('expressionXY.dataLayer.palette.help', { - defaultMessage: 'Palette', - }), - default: '{palette}', - }, - }, + args: { ...commonDataLayerArgs }, fn(table, args) { return { type: DATA_LAYER, diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_data_layer.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_data_layer.ts index edb174a7ab1b9..6f912cb94b807 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_data_layer.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_data_layer.ts @@ -6,115 +6,22 @@ * Side Public License, v 1. */ -import { i18n } from '@kbn/i18n'; -import type { Datatable, ExpressionFunctionDefinition } from '@kbn/expressions-plugin/common'; -import { ExtendedDataLayerArgs, ExtendedDataLayerConfigResult } from '../types'; -import { - EXTENDED_DATA_LAYER, - LayerTypes, - SeriesTypes, - XScaleTypes, - YScaleTypes, - Y_CONFIG, -} from '../constants'; +import { ExtendedDataLayerFn } from '../types'; +import { EXTENDED_DATA_LAYER, LayerTypes } from '../constants'; +import { strings } from '../i18n'; +import { commonDataLayerArgs } from './common_data_layer_args'; -export const extendedDataLayerFunction: ExpressionFunctionDefinition< - typeof EXTENDED_DATA_LAYER, - Datatable, - ExtendedDataLayerArgs, - ExtendedDataLayerConfigResult -> = { +export const extendedDataLayerFunction: ExtendedDataLayerFn = { name: EXTENDED_DATA_LAYER, aliases: [], type: EXTENDED_DATA_LAYER, - help: i18n.translate('expressionXY.extendedDataLayer.help', { - defaultMessage: `Configure a layer in the xy chart`, - }), + help: strings.getDataLayerFnHelp(), inputTypes: ['datatable'], args: { - hide: { - types: ['boolean'], - default: false, - help: i18n.translate('expressionXY.extendedDataLayer.hide.help', { - defaultMessage: 'Show / hide axis', - }), - }, - xAccessor: { - types: ['string'], - help: i18n.translate('expressionXY.extendedDataLayer.xAccessor.help', { - defaultMessage: 'X-axis', - }), - }, - seriesType: { - types: ['string'], - options: [...Object.values(SeriesTypes)], - help: i18n.translate('expressionXY.extendedDataLayer.seriesType.help', { - defaultMessage: 'The type of chart to display.', - }), - required: true, - strict: true, - }, - xScaleType: { - options: [...Object.values(XScaleTypes)], - help: i18n.translate('expressionXY.extendedDataLayer.xScaleType.help', { - defaultMessage: 'The scale type of the x axis', - }), - default: XScaleTypes.ORDINAL, - strict: true, - }, - isHistogram: { - types: ['boolean'], - default: false, - help: i18n.translate('expressionXY.extendedDataLayer.isHistogram.help', { - defaultMessage: 'Whether to layout the chart as a histogram', - }), - }, - yScaleType: { - options: [...Object.values(YScaleTypes)], - help: i18n.translate('expressionXY.extendedDataLayer.yScaleType.help', { - defaultMessage: 'The scale type of the y axes', - }), - default: YScaleTypes.LINEAR, - strict: true, - }, - splitAccessor: { - types: ['string'], - help: i18n.translate('expressionXY.extendedDataLayer.splitAccessor.help', { - defaultMessage: 'The column to split by', - }), - }, - accessors: { - types: ['string'], - help: i18n.translate('expressionXY.extendedDataLayer.accessors.help', { - defaultMessage: 'The columns to display on the y axis.', - }), - multi: true, - }, - yConfig: { - types: [Y_CONFIG], - help: i18n.translate('expressionXY.extendedDataLayer.yConfig.help', { - defaultMessage: 'Additional configuration for y axes', - }), - multi: true, - }, - columnToLabel: { - types: ['string'], - help: i18n.translate('expressionXY.extendedDataLayer.columnToLabel.help', { - defaultMessage: 'JSON key-value pairs of column ID to label', - }), - }, - palette: { - default: `{theme "palette" default={system_palette name="default"} }`, - help: i18n.translate('expressionXY.extendedDataLayer.palette.help', { - defaultMessage: 'Palette', - }), - types: ['palette'], - }, + ...commonDataLayerArgs, table: { types: ['datatable'], - help: i18n.translate('expressionXY.extendedDataLayer.table.help', { - defaultMessage: 'Table', - }), + help: strings.getTableHelp(), }, }, fn(input, args) { diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_reference_line_layer.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_reference_line_layer.ts index affef45fbc2c8..bccbd85c0be8f 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_reference_line_layer.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_reference_line_layer.ts @@ -6,50 +6,22 @@ * Side Public License, v 1. */ -import { i18n } from '@kbn/i18n'; -import type { Datatable, ExpressionFunctionDefinition } from '@kbn/expressions-plugin/common'; -import { LayerTypes, EXTENDED_REFERENCE_LINE_LAYER, EXTENDED_Y_CONFIG } from '../constants'; -import { ExtendedReferenceLineLayerArgs, ExtendedReferenceLineLayerConfigResult } from '../types'; +import { LayerTypes, EXTENDED_REFERENCE_LINE_LAYER } from '../constants'; +import { ExtendedReferenceLineLayerFn } from '../types'; +import { strings } from '../i18n'; +import { commonReferenceLineLayerArgs } from './common_reference_line_layer_args'; -export const extendedReferenceLineLayerFunction: ExpressionFunctionDefinition< - typeof EXTENDED_REFERENCE_LINE_LAYER, - Datatable, - ExtendedReferenceLineLayerArgs, - ExtendedReferenceLineLayerConfigResult -> = { +export const extendedReferenceLineLayerFunction: ExtendedReferenceLineLayerFn = { name: EXTENDED_REFERENCE_LINE_LAYER, aliases: [], type: EXTENDED_REFERENCE_LINE_LAYER, - help: i18n.translate('expressionXY.extendedReferenceLineLayer.help', { - defaultMessage: `Configure a reference line in the xy chart`, - }), + help: strings.getRLHelp(), inputTypes: ['datatable'], args: { - accessors: { - types: ['string'], - help: i18n.translate('expressionXY.extendedReferenceLineLayer.accessors.help', { - defaultMessage: 'The columns to display on the y axis.', - }), - multi: true, - }, - yConfig: { - types: [EXTENDED_Y_CONFIG], - help: i18n.translate('expressionXY.extendedReferenceLineLayer.yConfig.help', { - defaultMessage: 'Additional configuration for y axes', - }), - multi: true, - }, - columnToLabel: { - types: ['string'], - help: i18n.translate('expressionXY.extendedReferenceLineLayer.columnToLabel.help', { - defaultMessage: 'JSON key-value pairs of column ID to label', - }), - }, + ...commonReferenceLineLayerArgs, table: { types: ['datatable'], - help: i18n.translate('expressionXY.extendedReferenceLineLayer.table.help', { - defaultMessage: 'Table', - }), + help: strings.getTableHelp(), }, }, fn(input, args) { diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_y_axis_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_y_axis_config.ts index 05de3608a2299..606cdd84ac710 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_y_axis_config.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_y_axis_config.ts @@ -7,51 +7,25 @@ */ import { i18n } from '@kbn/i18n'; -import type { ExpressionFunctionDefinition } from '@kbn/expressions-plugin/common'; import { AvailableReferenceLineIcons, EXTENDED_Y_CONFIG, FillStyles, IconPositions, LineStyles, - YAxisModes, } from '../constants'; -import { ExtendedYConfig, ExtendedYConfigResult } from '../types'; +import { strings } from '../i18n'; +import { ExtendedYConfigFn } from '../types'; +import { commonYConfigArgs } from './common_y_config_args'; -export const extendedYAxisConfigFunction: ExpressionFunctionDefinition< - typeof EXTENDED_Y_CONFIG, - null, - ExtendedYConfig, - ExtendedYConfigResult -> = { +export const extendedYAxisConfigFunction: ExtendedYConfigFn = { name: EXTENDED_Y_CONFIG, aliases: [], type: EXTENDED_Y_CONFIG, - help: i18n.translate('expressionXY.yConfig.help', { - defaultMessage: `Configure the behavior of a xy chart's y axis metric`, - }), + help: strings.getYConfigFnHelp(), inputTypes: ['null'], args: { - forAccessor: { - types: ['string'], - help: i18n.translate('expressionXY.yConfig.forAccessor.help', { - defaultMessage: 'The accessor this configuration is for', - }), - }, - axisMode: { - types: ['string'], - options: [...Object.values(YAxisModes)], - help: i18n.translate('expressionXY.yConfig.axisMode.help', { - defaultMessage: 'The axis mode of the metric', - }), - strict: true, - }, - color: { - types: ['string'], - help: i18n.translate('expressionXY.yConfig.color.help', { - defaultMessage: 'The color of the series', - }), - }, + ...commonYConfigArgs, lineStyle: { types: ['string'], options: [...Object.values(LineStyles)], diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts index e3bb9283101a2..e5b1694441ed8 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts @@ -7,8 +7,7 @@ */ import { i18n } from '@kbn/i18n'; -import type { ExpressionFunctionDefinition, Datatable } from '@kbn/expressions-plugin'; -import { LayeredXYArgs, XYExtendedLayerConfigResult, XYRender } from '../types'; +import { LayeredXyVisFn, XYExtendedLayerConfigResult } from '../types'; import { XY_VIS_RENDERER, EXTENDED_DATA_LAYER, @@ -17,21 +16,16 @@ import { ANNOTATION_LAYER, } from '../constants'; import { logDatatables } from '../utils'; -import { commonArgsXY } from './common_args_xy'; +import { commonXYArgs } from './common_xy_args'; import { strings } from '../i18n'; -export const layeredXyVisFunction: ExpressionFunctionDefinition< - typeof LAYERED_XY_VIS, - Datatable, - LayeredXYArgs, - XYRender -> = { +export const layeredXyVisFunction: LayeredXyVisFn = { name: LAYERED_XY_VIS, type: 'render', inputTypes: ['datatable'], help: strings.getXYHelp(), args: { - ...commonArgsXY, + ...commonXYArgs, layers: { types: [EXTENDED_DATA_LAYER, EXTENDED_REFERENCE_LINE_LAYER, ANNOTATION_LAYER], help: i18n.translate('expressionXY.layeredXyVis.layers.help', { diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/legend_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/legend_config.ts index 9daf13d3209ad..8d671c823efb5 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/legend_config.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/legend_config.ts @@ -8,9 +8,8 @@ import { HorizontalAlignment, Position, VerticalAlignment } from '@elastic/charts'; import { i18n } from '@kbn/i18n'; -import type { ExpressionFunctionDefinition } from '@kbn/expressions-plugin/common'; import { LEGEND_CONFIG } from '../constants'; -import { LegendConfig, LegendConfigResult } from '../types'; +import { LegendConfigFn } from '../types'; const errors = { positionUsageWithIsInsideError: () => @@ -45,12 +44,7 @@ const errors = { ), }; -export const legendConfigFunction: ExpressionFunctionDefinition< - typeof LEGEND_CONFIG, - null, - LegendConfig, - LegendConfigResult -> = { +export const legendConfigFunction: LegendConfigFn = { name: LEGEND_CONFIG, aliases: [], type: LEGEND_CONFIG, diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line_layer.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line_layer.ts index 28ef5c1dfc877..9c6e27c958530 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line_layer.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line_layer.ts @@ -6,46 +6,18 @@ * Side Public License, v 1. */ -import { i18n } from '@kbn/i18n'; -import type { Datatable, ExpressionFunctionDefinition } from '@kbn/expressions-plugin/common'; -import { LayerTypes, REFERENCE_LINE_LAYER, EXTENDED_Y_CONFIG } from '../constants'; -import { ReferenceLineLayerArgs, ReferenceLineLayerConfigResult } from '../types'; +import { LayerTypes, REFERENCE_LINE_LAYER } from '../constants'; +import { ReferenceLineLayerFn } from '../types'; +import { strings } from '../i18n'; +import { commonReferenceLineLayerArgs } from './common_reference_line_layer_args'; -export const referenceLineLayerFunction: ExpressionFunctionDefinition< - typeof REFERENCE_LINE_LAYER, - Datatable, - ReferenceLineLayerArgs, - ReferenceLineLayerConfigResult -> = { +export const referenceLineLayerFunction: ReferenceLineLayerFn = { name: REFERENCE_LINE_LAYER, aliases: [], type: REFERENCE_LINE_LAYER, - help: i18n.translate('expressionXY.referenceLineLayer.help', { - defaultMessage: `Configure a reference line in the xy chart`, - }), + help: strings.getRLHelp(), inputTypes: ['datatable'], - args: { - accessors: { - types: ['string'], - help: i18n.translate('expressionXY.referenceLineLayer.accessors.help', { - defaultMessage: 'The columns to display on the y axis.', - }), - multi: true, - }, - yConfig: { - types: [EXTENDED_Y_CONFIG], - help: i18n.translate('expressionXY.referenceLineLayer.yConfig.help', { - defaultMessage: 'Additional configuration for y axes', - }), - multi: true, - }, - columnToLabel: { - types: ['string'], - help: i18n.translate('expressionXY.referenceLineLayer.columnToLabel.help', { - defaultMessage: 'JSON key-value pairs of column ID to label', - }), - }, - }, + args: { ...commonReferenceLineLayerArgs }, fn(table, args) { return { type: REFERENCE_LINE_LAYER, diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts index d6e27c398f572..f416b44d562c1 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts @@ -7,14 +7,12 @@ */ import { i18n } from '@kbn/i18n'; -import type { ExpressionFunctionDefinition, Datatable } from '@kbn/expressions-plugin'; import { Dimension, prepareLogTable } from '@kbn/visualizations-plugin/common/utils'; import { AxisExtentConfigResult, DataLayerConfigResult, - XYArgs, XYLayerConfigResult, - XYRender, + XyVisFn, } from '../types'; import { XY_VIS, @@ -28,7 +26,7 @@ import { } from '../constants'; import { getLayerDimensions } from '../utils'; import { strings } from '../i18n'; -import { commonArgsXY } from './common_args_xy'; +import { commonXYArgs } from './common_xy_args'; const errors = { extendBoundsAreInvalidError: () => @@ -76,18 +74,13 @@ const validateExtent = ( } }; -export const xyVisFunction: ExpressionFunctionDefinition< - typeof XY_VIS, - Datatable, - XYArgs, - XYRender -> = { +export const xyVisFunction: XyVisFn = { name: XY_VIS, type: 'render', inputTypes: ['datatable'], help: strings.getXYHelp(), args: { - ...commonArgsXY, + ...commonXYArgs, dataLayers: { types: [DATA_LAYER], help: strings.getDataLayerHelp(), diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/y_axis_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/y_axis_config.ts index 1d9087dc264ec..882a3231148f5 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/y_axis_config.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/y_axis_config.ts @@ -6,46 +6,18 @@ * Side Public License, v 1. */ -import { i18n } from '@kbn/i18n'; -import type { ExpressionFunctionDefinition } from '@kbn/expressions-plugin/common'; -import { YAxisModes, Y_CONFIG } from '../constants'; -import { YConfig, YConfigResult } from '../types'; +import { Y_CONFIG } from '../constants'; +import { YConfigFn } from '../types'; +import { strings } from '../i18n'; +import { commonYConfigArgs } from './common_y_config_args'; -export const yAxisConfigFunction: ExpressionFunctionDefinition< - typeof Y_CONFIG, - null, - YConfig, - YConfigResult -> = { +export const yAxisConfigFunction: YConfigFn = { name: Y_CONFIG, aliases: [], type: Y_CONFIG, - help: i18n.translate('expressionXY.yConfig.help', { - defaultMessage: `Configure the behavior of a xy chart's y axis metric`, - }), + help: strings.getYConfigFnHelp(), inputTypes: ['null'], - args: { - forAccessor: { - types: ['string'], - help: i18n.translate('expressionXY.yConfig.forAccessor.help', { - defaultMessage: 'The accessor this configuration is for', - }), - }, - axisMode: { - types: ['string'], - options: [...Object.values(YAxisModes)], - help: i18n.translate('expressionXY.yConfig.axisMode.help', { - defaultMessage: 'The axis mode of the metric', - }), - strict: true, - }, - color: { - types: ['string'], - help: i18n.translate('expressionXY.yConfig.color.help', { - defaultMessage: 'The color of the series', - }), - }, - }, + args: { ...commonYConfigArgs }, fn(input, args) { return { type: Y_CONFIG, diff --git a/src/plugins/chart_expressions/expression_xy/common/i18n/index.tsx b/src/plugins/chart_expressions/expression_xy/common/i18n/index.tsx index c04a0cf485bfd..a39b0157f9611 100644 --- a/src/plugins/chart_expressions/expression_xy/common/i18n/index.tsx +++ b/src/plugins/chart_expressions/expression_xy/common/i18n/index.tsx @@ -113,4 +113,84 @@ export const strings = { i18n.translate('expressionXY.xyVis.ariaLabel.help', { defaultMessage: 'Specifies the aria label of the xy chart', }), + getDataLayerFnHelp: () => + i18n.translate('expressionXY.dataLayer.help', { + defaultMessage: `Configure a layer in the xy chart`, + }), + getHideHelp: () => + i18n.translate('expressionXY.dataLayer.hide.help', { + defaultMessage: 'Show / hide axis', + }), + getXAccessorHelp: () => + i18n.translate('expressionXY.dataLayer.xAccessor.help', { + defaultMessage: 'X-axis', + }), + getSeriesTypeHelp: () => + i18n.translate('expressionXY.dataLayer.seriesType.help', { + defaultMessage: 'The type of chart to display.', + }), + getXScaleTypeHelp: () => + i18n.translate('expressionXY.dataLayer.xScaleType.help', { + defaultMessage: 'The scale type of the x axis', + }), + getIsHistogramHelp: () => + i18n.translate('expressionXY.dataLayer.isHistogram.help', { + defaultMessage: 'Whether to layout the chart as a histogram', + }), + getYScaleTypeHelp: () => + i18n.translate('expressionXY.dataLayer.yScaleType.help', { + defaultMessage: 'The scale type of the y axes', + }), + getSplitAccessorHelp: () => + i18n.translate('expressionXY.dataLayer.splitAccessor.help', { + defaultMessage: 'The column to split by', + }), + getAccessorsHelp: () => + i18n.translate('expressionXY.dataLayer.accessors.help', { + defaultMessage: 'The columns to display on the y axis.', + }), + getYConfigHelp: () => + i18n.translate('expressionXY.dataLayer.yConfig.help', { + defaultMessage: 'Additional configuration for y axes', + }), + getColumnToLabelHelp: () => + i18n.translate('expressionXY.layer.columnToLabel.help', { + defaultMessage: 'JSON key-value pairs of column ID to label', + }), + getPaletteHelp: () => + i18n.translate('expressionXY.dataLayer.palette.help', { + defaultMessage: 'Palette', + }), + getTableHelp: () => + i18n.translate('expressionXY.layers.table.help', { + defaultMessage: 'Table', + }), + getRLAccessorsHelp: () => + i18n.translate('expressionXY.referenceLineLayer.accessors.help', { + defaultMessage: 'The columns to display on the y axis.', + }), + getRLYConfigHelp: () => + i18n.translate('expressionXY.referenceLineLayer.yConfig.help', { + defaultMessage: 'Additional configuration for y axes', + }), + getRLHelp: () => + i18n.translate('expressionXY.referenceLineLayer.help', { + defaultMessage: `Configure a reference line in the xy chart`, + }), + getYConfigFnHelp: () => + i18n.translate('expressionXY.yConfig.help', { + defaultMessage: `Configure the behavior of a xy chart's y axis metric`, + }), + getForAccessorHelp: () => + i18n.translate('expressionXY.yConfig.forAccessor.help', { + defaultMessage: 'The accessor this configuration is for', + }), + getAxisModeHelp: () => + i18n.translate('expressionXY.yConfig.axisMode.help', { + defaultMessage: 'The axis mode of the metric', + }), + getColorHelp: () => + i18n.translate('expressionXY.yConfig.color.help', { + defaultMessage: 'The color of the series', + }), }; diff --git a/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts b/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts index ee875082302c2..4ac105c77378c 100644 --- a/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts +++ b/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts @@ -9,7 +9,7 @@ import { HorizontalAlignment, Position, VerticalAlignment } from '@elastic/charts'; import { $Values } from '@kbn/utility-types'; import type { PaletteOutput } from '@kbn/coloring'; -import { Datatable } from '@kbn/expressions-plugin'; +import { Datatable, ExpressionFunctionDefinition } from '@kbn/expressions-plugin'; import { EventAnnotationOutput } from '@kbn/event-annotation-plugin/common'; import { AxisExtentModes, @@ -40,7 +40,10 @@ import { EndValues, EXTENDED_Y_CONFIG, AvailableReferenceLineIcons, + XY_VIS, + LAYERED_XY_VIS, } from '../constants'; +import { XYRender } from './expression_renderers'; export type EndValue = $Values; export type LayerType = $Values; @@ -340,3 +343,52 @@ export type CommonXYDataLayerConfigResult = DataLayerConfigResult | ExtendedData export type CommonXYReferenceLineLayerConfigResult = | ReferenceLineLayerConfigResult | ExtendedReferenceLineLayerConfigResult; + +export type XyVisFn = ExpressionFunctionDefinition; +export type LayeredXyVisFn = ExpressionFunctionDefinition< + typeof LAYERED_XY_VIS, + Datatable, + LayeredXYArgs, + XYRender +>; + +export type DataLayerFn = ExpressionFunctionDefinition< + typeof DATA_LAYER, + Datatable, + DataLayerArgs, + DataLayerConfigResult +>; +export type ExtendedDataLayerFn = ExpressionFunctionDefinition< + typeof EXTENDED_DATA_LAYER, + Datatable, + ExtendedDataLayerArgs, + ExtendedDataLayerConfigResult +>; + +export type ReferenceLineLayerFn = ExpressionFunctionDefinition< + typeof REFERENCE_LINE_LAYER, + Datatable, + ReferenceLineLayerArgs, + ReferenceLineLayerConfigResult +>; +export type ExtendedReferenceLineLayerFn = ExpressionFunctionDefinition< + typeof EXTENDED_REFERENCE_LINE_LAYER, + Datatable, + ExtendedReferenceLineLayerArgs, + ExtendedReferenceLineLayerConfigResult +>; + +export type YConfigFn = ExpressionFunctionDefinition; +export type ExtendedYConfigFn = ExpressionFunctionDefinition< + typeof EXTENDED_Y_CONFIG, + null, + ExtendedYConfig, + ExtendedYConfigResult +>; + +export type LegendConfigFn = ExpressionFunctionDefinition< + typeof LEGEND_CONFIG, + null, + LegendConfig, + LegendConfigResult +>; From 7a8c452cd23767f5f3d2db10a89bbf8a4c2f1e6b Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Tue, 19 Apr 2022 19:00:19 +0300 Subject: [PATCH 120/133] Refactored expression functions. Added asynchronous behavior. --- .../expression_functions/legend_config.ts | 61 +-------- .../expression_functions/legend_config_fn.ts | 68 ++++++++++ .../expression_functions/xy_vis.test.ts | 4 +- .../common/expression_functions/xy_vis.ts | 127 +----------------- .../common/expression_functions/xy_vis_fn.ts | 123 +++++++++++++++++ .../common/types/expression_functions.ts | 9 +- 6 files changed, 208 insertions(+), 184 deletions(-) create mode 100644 src/plugins/chart_expressions/expression_xy/common/expression_functions/legend_config_fn.ts create mode 100644 src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis_fn.ts diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/legend_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/legend_config.ts index 8d671c823efb5..2b383f1899d44 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/legend_config.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/legend_config.ts @@ -11,39 +11,6 @@ import { i18n } from '@kbn/i18n'; import { LEGEND_CONFIG } from '../constants'; import { LegendConfigFn } from '../types'; -const errors = { - positionUsageWithIsInsideError: () => - i18n.translate( - 'expressionXY.reusable.function.legendConfig.errors.positionUsageWithIsInsideError', - { - defaultMessage: - '`position` argument is not applied if `isInside = true`. Please, use `horizontalAlignment` and `verticalAlignment` arguments instead.', - } - ), - alignmentUsageWithFalsyIsInsideError: () => - i18n.translate( - 'expressionXY.reusable.function.legendConfig.errors.alignmentUsageWithFalsyIsInsideError', - { - defaultMessage: - '`horizontalAlignment` and `verticalAlignment` arguments are not applied if `isInside = false`. Please, use the `position` argument instead.', - } - ), - floatingColumnsWithFalsyIsInsideError: () => - i18n.translate( - 'expressionXY.reusable.function.legendConfig.errors.floatingColumnsWithFalsyIsInsideError', - { - defaultMessage: '`floatingColumns` arguments are not applied if `isInside = false`.', - } - ), - legendSizeWithFalsyIsInsideError: () => - i18n.translate( - 'expressionXY.reusable.function.legendConfig.errors.legendSizeWithFalsyIsInsideError', - { - defaultMessage: '`legendSize` argument is not applied if `isInside = false`.', - } - ), -}; - export const legendConfigFunction: LegendConfigFn = { name: LEGEND_CONFIG, aliases: [], @@ -124,30 +91,8 @@ export const legendConfigFunction: LegendConfigFn = { }), }, }, - fn(input, args) { - if (args.isInside) { - if (args.position) { - throw new Error(errors.positionUsageWithIsInsideError()); - } - - if (args.legendSize !== undefined) { - throw new Error(errors.legendSizeWithFalsyIsInsideError()); - } - } - - if (!args.isInside) { - if (args.verticalAlignment || args.horizontalAlignment) { - throw new Error(errors.alignmentUsageWithFalsyIsInsideError()); - } - - if (args.floatingColumns !== undefined) { - throw new Error(errors.floatingColumnsWithFalsyIsInsideError()); - } - } - - return { - type: LEGEND_CONFIG, - ...args, - }; + async fn(input, args, handlers) { + const { legendConfigFn } = await import('./legend_config_fn'); + return await legendConfigFn(input, args, handlers); }, }; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/legend_config_fn.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/legend_config_fn.ts new file mode 100644 index 0000000000000..35df125ae230f --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/legend_config_fn.ts @@ -0,0 +1,68 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { LEGEND_CONFIG } from '../constants'; +import { LegendConfigFn } from '../types'; + +const errors = { + positionUsageWithIsInsideError: () => + i18n.translate( + 'expressionXY.reusable.function.legendConfig.errors.positionUsageWithIsInsideError', + { + defaultMessage: + '`position` argument is not applied if `isInside = true`. Please, use `horizontalAlignment` and `verticalAlignment` arguments instead.', + } + ), + alignmentUsageWithFalsyIsInsideError: () => + i18n.translate( + 'expressionXY.reusable.function.legendConfig.errors.alignmentUsageWithFalsyIsInsideError', + { + defaultMessage: + '`horizontalAlignment` and `verticalAlignment` arguments are not applied if `isInside = false`. Please, use the `position` argument instead.', + } + ), + floatingColumnsWithFalsyIsInsideError: () => + i18n.translate( + 'expressionXY.reusable.function.legendConfig.errors.floatingColumnsWithFalsyIsInsideError', + { + defaultMessage: '`floatingColumns` arguments are not applied if `isInside = false`.', + } + ), + legendSizeWithFalsyIsInsideError: () => + i18n.translate( + 'expressionXY.reusable.function.legendConfig.errors.legendSizeWithFalsyIsInsideError', + { + defaultMessage: '`legendSize` argument is not applied if `isInside = false`.', + } + ), +}; + +export const legendConfigFn: LegendConfigFn['fn'] = async (data, args) => { + if (args.isInside) { + if (args.position) { + throw new Error(errors.positionUsageWithIsInsideError()); + } + + if (args.legendSize !== undefined) { + throw new Error(errors.legendSizeWithFalsyIsInsideError()); + } + } + + if (!args.isInside) { + if (args.verticalAlignment || args.horizontalAlignment) { + throw new Error(errors.alignmentUsageWithFalsyIsInsideError()); + } + + if (args.floatingColumns !== undefined) { + throw new Error(errors.floatingColumnsWithFalsyIsInsideError()); + } + } + + return { type: LEGEND_CONFIG, ...args }; +}; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.test.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.test.ts index 2251bd58a58c4..688efbe122f3e 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.test.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.test.ts @@ -12,10 +12,10 @@ import { sampleArgs, sampleLayer } from '../__mocks__'; import { XY_VIS } from '../constants'; describe('xyVis', () => { - test('it renders with the specified data and args', () => { + test('it renders with the specified data and args', async () => { const { data, args } = sampleArgs(); const { layers, ...rest } = args; - const result = xyVisFunction.fn( + const result = await xyVisFunction.fn( data, { ...rest, dataLayers: [sampleLayer], referenceLineLayers: [], annotationLayers: [] }, createMockExecutionContext() diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts index f416b44d562c1..2e97cb00b3e55 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis.ts @@ -6,74 +6,11 @@ * Side Public License, v 1. */ -import { i18n } from '@kbn/i18n'; -import { Dimension, prepareLogTable } from '@kbn/visualizations-plugin/common/utils'; -import { - AxisExtentConfigResult, - DataLayerConfigResult, - XYLayerConfigResult, - XyVisFn, -} from '../types'; -import { - XY_VIS, - DATA_LAYER, - ValueLabelModes, - XY_VIS_RENDERER, - REFERENCE_LINE_LAYER, - ANNOTATION_LAYER, - LayerTypes, - AxisExtentModes, -} from '../constants'; -import { getLayerDimensions } from '../utils'; +import { XyVisFn } from '../types'; +import { XY_VIS, DATA_LAYER, REFERENCE_LINE_LAYER, ANNOTATION_LAYER } from '../constants'; import { strings } from '../i18n'; import { commonXYArgs } from './common_xy_args'; -const errors = { - extendBoundsAreInvalidError: () => - i18n.translate('expressionXY.reusable.function.xyVis.errors.extendBoundsAreInvalidError', { - defaultMessage: - 'For area and bar modes, and custom extent mode, the lower bound should be less or greater than 0 and the upper bound - be greater or equal than 0', - }), - notUsedFillOpacityError: () => - i18n.translate('expressionXY.reusable.function.xyVis.errors.notUsedFillOpacityError', { - defaultMessage: '`fillOpacity` argument is applicable only for area charts.', - }), - valueLabelsForNotBarsOrHistogramBarsChartsError: () => - i18n.translate( - 'expressionXY.reusable.function.xyVis.errors.valueLabelsForNotBarsOrHistogramBarsChartsError', - { - defaultMessage: - '`valueLabels` argument is applicable only for bar charts, which are not histograms.', - } - ), - dataBoundsForNotLineChartError: () => - i18n.translate('expressionXY.reusable.function.xyVis.errors.dataBoundsForNotLineChartError', { - defaultMessage: 'Only line charts can be fit to the data bounds', - }), -}; - -const validateExtent = ( - extent: AxisExtentConfigResult, - hasBarOrArea: boolean, - dataLayers: DataLayerConfigResult[] -) => { - const isValidLowerBound = - extent.lowerBound === undefined || (extent.lowerBound !== undefined && extent.lowerBound <= 0); - const isValidUpperBound = - extent.upperBound === undefined || (extent.upperBound !== undefined && extent.upperBound >= 0); - - const areValidBounds = isValidLowerBound && isValidUpperBound; - - if (hasBarOrArea && extent.mode === AxisExtentModes.CUSTOM && !areValidBounds) { - throw new Error(errors.extendBoundsAreInvalidError()); - } - - const lineSeries = dataLayers.filter(({ seriesType }) => seriesType.includes('line')); - if (!lineSeries.length && extent.mode === AxisExtentModes.DATA_BOUNDS) { - throw new Error(errors.dataBoundsForNotLineChartError()); - } -}; - export const xyVisFunction: XyVisFn = { name: XY_VIS, type: 'render', @@ -97,62 +34,8 @@ export const xyVisFunction: XyVisFn = { multi: true, }, }, - fn(data, args, handlers) { - const { dataLayers = [], referenceLineLayers = [], annotationLayers = [], ...restArgs } = args; - const inputLayers: Array = [ - ...dataLayers, - ...referenceLineLayers, - ...annotationLayers, - ]; - - const layers: XYLayerConfigResult[] = inputLayers.filter( - (layer): layer is XYLayerConfigResult => layer !== undefined - ); - - if (handlers.inspectorAdapters.tables) { - const layerDimensions = layers.reduce((dimensions, layer) => { - if (layer.layerType === LayerTypes.ANNOTATIONS) { - return dimensions; - } - - return [...dimensions, ...getLayerDimensions(layer)]; - }, []); - - const logTable = prepareLogTable(data, layerDimensions, true); - handlers.inspectorAdapters.tables.logDatatable('default', logTable); - } - - const hasBar = dataLayers.filter(({ seriesType }) => seriesType.includes('bar')).length > 0; - const hasArea = dataLayers.filter(({ seriesType }) => seriesType.includes('area')).length > 0; - - validateExtent(args.yLeftExtent, hasBar || hasArea, dataLayers); - validateExtent(args.yRightExtent, hasBar || hasArea, dataLayers); - - if (!hasArea && args.fillOpacity !== undefined) { - throw new Error(errors.notUsedFillOpacityError()); - } - - const hasNotHistogramBars = - dataLayers.filter(({ seriesType, isHistogram }) => seriesType.includes('bar') && !isHistogram) - .length > 0; - - if ((!hasBar || !hasNotHistogramBars) && args.valueLabels !== ValueLabelModes.HIDE) { - throw new Error(errors.valueLabelsForNotBarsOrHistogramBarsChartsError()); - } - - return { - type: 'render', - as: XY_VIS_RENDERER, - value: { - args: { - ...restArgs, - layers, - ariaLabel: - args.ariaLabel ?? - (handlers.variables?.embeddableTitle as string) ?? - handlers.getExecutionContext?.()?.description, - }, - }, - }; + async fn(data, args, handlers) { + const { xyVisFn } = await import('./xy_vis_fn'); + return await xyVisFn(data, args, handlers); }, }; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis_fn.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis_fn.ts new file mode 100644 index 0000000000000..1194f0f209671 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis_fn.ts @@ -0,0 +1,123 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { Dimension, prepareLogTable } from '@kbn/visualizations-plugin/common/utils'; +import { AxisExtentModes, LayerTypes, ValueLabelModes, XY_VIS_RENDERER } from '../constants'; +import { + AxisExtentConfigResult, + DataLayerConfigResult, + XYLayerConfigResult, + XyVisFn, +} from '../types'; +import { getLayerDimensions } from '../utils'; + +const errors = { + extendBoundsAreInvalidError: () => + i18n.translate('expressionXY.reusable.function.xyVis.errors.extendBoundsAreInvalidError', { + defaultMessage: + 'For area and bar modes, and custom extent mode, the lower bound should be less or greater than 0 and the upper bound - be greater or equal than 0', + }), + notUsedFillOpacityError: () => + i18n.translate('expressionXY.reusable.function.xyVis.errors.notUsedFillOpacityError', { + defaultMessage: '`fillOpacity` argument is applicable only for area charts.', + }), + valueLabelsForNotBarsOrHistogramBarsChartsError: () => + i18n.translate( + 'expressionXY.reusable.function.xyVis.errors.valueLabelsForNotBarsOrHistogramBarsChartsError', + { + defaultMessage: + '`valueLabels` argument is applicable only for bar charts, which are not histograms.', + } + ), + dataBoundsForNotLineChartError: () => + i18n.translate('expressionXY.reusable.function.xyVis.errors.dataBoundsForNotLineChartError', { + defaultMessage: 'Only line charts can be fit to the data bounds', + }), +}; + +const validateExtent = ( + extent: AxisExtentConfigResult, + hasBarOrArea: boolean, + dataLayers: DataLayerConfigResult[] +) => { + const isValidLowerBound = + extent.lowerBound === undefined || (extent.lowerBound !== undefined && extent.lowerBound <= 0); + const isValidUpperBound = + extent.upperBound === undefined || (extent.upperBound !== undefined && extent.upperBound >= 0); + + const areValidBounds = isValidLowerBound && isValidUpperBound; + + if (hasBarOrArea && extent.mode === AxisExtentModes.CUSTOM && !areValidBounds) { + throw new Error(errors.extendBoundsAreInvalidError()); + } + + const lineSeries = dataLayers.filter(({ seriesType }) => seriesType.includes('line')); + if (!lineSeries.length && extent.mode === AxisExtentModes.DATA_BOUNDS) { + throw new Error(errors.dataBoundsForNotLineChartError()); + } +}; + +export const xyVisFn: XyVisFn['fn'] = async (data, args, handlers) => { + const { dataLayers = [], referenceLineLayers = [], annotationLayers = [], ...restArgs } = args; + const inputLayers: Array = [ + ...dataLayers, + ...referenceLineLayers, + ...annotationLayers, + ]; + + const layers: XYLayerConfigResult[] = inputLayers.filter( + (layer): layer is XYLayerConfigResult => layer !== undefined + ); + + if (handlers.inspectorAdapters.tables) { + const layerDimensions = layers.reduce((dimensions, layer) => { + if (layer.layerType === LayerTypes.ANNOTATIONS) { + return dimensions; + } + + return [...dimensions, ...getLayerDimensions(layer)]; + }, []); + + const logTable = prepareLogTable(data, layerDimensions, true); + handlers.inspectorAdapters.tables.logDatatable('default', logTable); + } + + const hasBar = dataLayers.filter(({ seriesType }) => seriesType.includes('bar')).length > 0; + const hasArea = dataLayers.filter(({ seriesType }) => seriesType.includes('area')).length > 0; + + validateExtent(args.yLeftExtent, hasBar || hasArea, dataLayers); + validateExtent(args.yRightExtent, hasBar || hasArea, dataLayers); + + if (!hasArea && args.fillOpacity !== undefined) { + throw new Error(errors.notUsedFillOpacityError()); + } + + const hasNotHistogramBars = + dataLayers.filter(({ seriesType, isHistogram }) => seriesType.includes('bar') && !isHistogram) + .length > 0; + + if ((!hasBar || !hasNotHistogramBars) && args.valueLabels !== ValueLabelModes.HIDE) { + throw new Error(errors.valueLabelsForNotBarsOrHistogramBarsChartsError()); + } + + return { + type: 'render', + as: XY_VIS_RENDERER, + value: { + args: { + ...restArgs, + layers, + ariaLabel: + args.ariaLabel ?? + (handlers.variables?.embeddableTitle as string) ?? + handlers.getExecutionContext?.()?.description, + }, + }, + }; +}; diff --git a/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts b/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts index 4ac105c77378c..d3b3c4b7ed2a0 100644 --- a/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts +++ b/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts @@ -344,7 +344,12 @@ export type CommonXYReferenceLineLayerConfigResult = | ReferenceLineLayerConfigResult | ExtendedReferenceLineLayerConfigResult; -export type XyVisFn = ExpressionFunctionDefinition; +export type XyVisFn = ExpressionFunctionDefinition< + typeof XY_VIS, + Datatable, + XYArgs, + Promise +>; export type LayeredXyVisFn = ExpressionFunctionDefinition< typeof LAYERED_XY_VIS, Datatable, @@ -390,5 +395,5 @@ export type LegendConfigFn = ExpressionFunctionDefinition< typeof LEGEND_CONFIG, null, LegendConfig, - LegendConfigResult + Promise >; From 3c4a3dda9d1ce4b592e4bd9f8777cf6fa71da994 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Tue, 19 Apr 2022 19:05:55 +0300 Subject: [PATCH 121/133] Fixed tests. --- .../common/expression_functions/legend_config.test.ts | 4 ++-- .../xy_visualization/xy_config_panel/xy_config_panel.test.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/legend_config.test.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/legend_config.test.ts index 9d58903e93c62..48e6d1c956acb 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/legend_config.test.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/legend_config.test.ts @@ -12,9 +12,9 @@ import { LegendConfig } from '../types'; import { legendConfigFunction } from './legend_config'; describe('legendConfigFunction', () => { - test('produces the correct arguments', () => { + test('produces the correct arguments', async () => { const args: LegendConfig = { isVisible: true, position: Position.Left }; - const result = legendConfigFunction.fn(null, args, createMockExecutionContext()); + const result = await legendConfigFunction.fn(null, args, createMockExecutionContext()); expect(result).toEqual({ type: 'legendConfig', ...args }); }); diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/xy_config_panel.test.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/xy_config_panel.test.tsx index c0b42487d42d6..ad643aa34ac50 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/xy_config_panel.test.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/xy_config_panel.test.tsx @@ -128,7 +128,7 @@ describe('XY Config panels', () => { it('should pass in information about current data bounds', () => { const state = testState(); frame.activeData = { - 0: { + first: { type: 'datatable', rows: [{ bar: -5 }, { bar: 50 }], columns: [ From 04c5e6deefbf2d442d0b4359673293af05af883a Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Tue, 19 Apr 2022 19:20:30 +0300 Subject: [PATCH 122/133] Updated limits. --- packages/kbn-optimizer/limits.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index 4741ff9c82aed..6fcf3d668d636 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -125,6 +125,6 @@ pageLoadAssetSize: visTypeGauge: 24113 unifiedSearch: 49195 data: 454087 - expressionXY: 47156 + expressionXY: 25000 eventAnnotation: 19334 screenshotting: 22870 From 1310c15f328863319bebfd4dbc44ea8fd2b8f2e6 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Tue, 19 Apr 2022 19:23:07 +0300 Subject: [PATCH 123/133] Updated the limit. --- packages/kbn-optimizer/limits.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index 6fcf3d668d636..6906659d2c643 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -125,6 +125,6 @@ pageLoadAssetSize: visTypeGauge: 24113 unifiedSearch: 49195 data: 454087 - expressionXY: 25000 + expressionXY: 44598 eventAnnotation: 19334 screenshotting: 22870 From e97b67c6210c1051a7db923af273b5101e36464e Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Tue, 19 Apr 2022 20:19:25 +0300 Subject: [PATCH 124/133] Fixed types. --- .../expression_xy/public/helpers/data_layers.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/data_layers.tsx b/src/plugins/chart_expressions/expression_xy/public/helpers/data_layers.tsx index 7c6f9be734d6d..709ab4b2370d4 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/data_layers.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/data_layers.tsx @@ -23,7 +23,7 @@ import { SerializedFieldFormat, } from '@kbn/field-formats-plugin/common'; import { Datatable, DatatableRow } from '@kbn/expressions-plugin'; -import { PaletteRegistry, SeriesLayer } from '@kbn/charts-plugin/public'; +import { PaletteRegistry, SeriesLayer } from '@kbn/coloring'; import { CommonXYDataLayerConfigResult, XScaleType } from '../../common'; import { FormatFactory } from '../types'; import { getSeriesColor } from './state'; From 822bf736420feb0ff18fd24b3eff8d18c7005e65 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Tue, 19 Apr 2022 20:20:20 +0300 Subject: [PATCH 125/133] fixed types. --- .../expression_xy/public/components/data_layers.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/chart_expressions/expression_xy/public/components/data_layers.tsx b/src/plugins/chart_expressions/expression_xy/public/components/data_layers.tsx index 836a6944dd3bd..7ab57bee14562 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/data_layers.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/data_layers.tsx @@ -14,7 +14,7 @@ import { } from '@elastic/charts'; import React, { FC } from 'react'; import { i18n } from '@kbn/i18n'; -import { PaletteRegistry } from '@kbn/charts-plugin/public'; +import { PaletteRegistry } from '@kbn/coloring'; import { FormatFactory } from '@kbn/field-formats-plugin/common'; import { Datatable } from '@kbn/expressions-plugin'; import { From acc63a9173f634b6773fcbefcdc164a56fa3027f Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Wed, 20 Apr 2022 16:49:29 +0300 Subject: [PATCH 126/133] Turned back layerIds. --- .../expression_xy/common/__mocks__/index.ts | 7 +- .../expression_xy/common/constants.ts | 1 + .../expression_functions/annotation_layer.ts | 18 +-- .../expression_functions/common_xy_args.ts | 5 + .../extended_annotation_layer.ts | 51 ++++++++ .../extended_data_layer.ts | 4 + .../extended_reference_line_layer.ts | 4 + .../common/expression_functions/index.ts | 1 + .../expression_functions/layered_xy_vis.ts | 11 +- .../common/expression_functions/xy_vis_fn.ts | 20 +-- .../expression_xy/common/helpers/index.ts | 9 ++ .../expression_xy/common/helpers/layers.ts | 26 ++++ .../expression_xy/common/i18n/index.tsx | 16 +++ .../expression_xy/common/index.ts | 8 +- .../common/types/expression_functions.ts | 49 ++++++-- .../common/utils/log_datatables.ts | 15 ++- .../expression_xy/public/__mocks__/index.tsx | 8 +- .../__snapshots__/xy_chart.test.tsx.snap | 49 ++++---- .../public/components/annotations.tsx | 4 +- .../public/components/data_layers.tsx | 13 +- .../public/components/legend_action.test.tsx | 5 +- .../public/components/legend_action.tsx | 10 +- .../components/reference_lines.test.tsx | 7 +- .../public/components/reference_lines.tsx | 18 ++- .../public/components/x_domain.tsx | 8 +- .../public/components/xy_chart.test.tsx | 116 ++++++++++-------- .../public/components/xy_chart.tsx | 35 +++--- .../public/helpers/axes_configuration.test.ts | 9 +- .../public/helpers/axes_configuration.ts | 14 +-- .../public/helpers/color_assignment.test.ts | 24 ++-- .../public/helpers/color_assignment.ts | 36 ++---- .../public/helpers/data_layers.tsx | 33 ++--- .../public/helpers/interval.test.ts | 6 +- .../expression_xy/public/helpers/layers.ts | 14 +-- .../expression_xy/public/helpers/state.ts | 6 +- .../public/helpers/visualization.ts | 42 +++---- .../expression_xy/public/plugin.ts | 2 + .../expression_xy/server/plugin.ts | 2 + x-pack/plugins/lens/public/index.ts | 4 +- .../__snapshots__/to_expression.test.ts.snap | 3 + .../public/xy_visualization/to_expression.ts | 5 +- 41 files changed, 434 insertions(+), 284 deletions(-) create mode 100644 src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_annotation_layer.ts create mode 100644 src/plugins/chart_expressions/expression_xy/common/helpers/index.ts create mode 100644 src/plugins/chart_expressions/expression_xy/common/helpers/layers.ts diff --git a/src/plugins/chart_expressions/expression_xy/common/__mocks__/index.ts b/src/plugins/chart_expressions/expression_xy/common/__mocks__/index.ts index 9be697c951b50..3a4a1fdb813fc 100644 --- a/src/plugins/chart_expressions/expression_xy/common/__mocks__/index.ts +++ b/src/plugins/chart_expressions/expression_xy/common/__mocks__/index.ts @@ -10,7 +10,7 @@ import { Position } from '@elastic/charts'; import type { PaletteOutput } from '@kbn/coloring'; import { Datatable, DatatableRow } from '@kbn/expressions-plugin'; import { LayerTypes } from '../constants'; -import { DataLayerConfigResult, XYProps } from '../types'; +import { DataLayerConfig, XYProps } from '../types'; export const mockPaletteOutput: PaletteOutput = { type: 'palette', @@ -46,7 +46,8 @@ export const createSampleDatatableWithRows = (rows: DatatableRow[]): Datatable = rows, }); -export const sampleLayer: DataLayerConfigResult = { +export const sampleLayer: DataLayerConfig = { + layerId: 'first', type: 'dataLayer', layerType: LayerTypes.DATA, seriesType: 'line', @@ -62,7 +63,7 @@ export const sampleLayer: DataLayerConfigResult = { }; export const createArgsWithLayers = ( - layers: DataLayerConfigResult | DataLayerConfigResult[] = sampleLayer + layers: DataLayerConfig | DataLayerConfig[] = sampleLayer ): XYProps => ({ xTitle: '', yTitle: '', diff --git a/src/plugins/chart_expressions/expression_xy/common/constants.ts b/src/plugins/chart_expressions/expression_xy/common/constants.ts index a75b3b97f6a43..931ece6ef8a78 100644 --- a/src/plugins/chart_expressions/expression_xy/common/constants.ts +++ b/src/plugins/chart_expressions/expression_xy/common/constants.ts @@ -17,6 +17,7 @@ export const LEGEND_CONFIG = 'legendConfig'; export const XY_VIS_RENDERER = 'xyVis'; export const GRID_LINES_CONFIG = 'gridlinesConfig'; export const ANNOTATION_LAYER = 'annotationLayer'; +export const EXTENDED_ANNOTATION_LAYER = 'extendedAnnotationLayer'; export const TICK_LABELS_CONFIG = 'tickLabelsConfig'; export const AXIS_EXTENT_CONFIG = 'axisExtentConfig'; export const REFERENCE_LINE_LAYER = 'referenceLineLayer'; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/annotation_layer.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/annotation_layer.ts index 6df2f9a4037a1..aac5ec2784ff6 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/annotation_layer.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/annotation_layer.ts @@ -6,38 +6,32 @@ * Side Public License, v 1. */ -import { i18n } from '@kbn/i18n'; import type { Datatable, ExpressionFunctionDefinition } from '@kbn/expressions-plugin/common'; import { LayerTypes, ANNOTATION_LAYER } from '../constants'; -import { AnnotationLayerArgs, CommonXYAnnotationLayerConfigResult } from '../types'; +import { AnnotationLayerArgs, AnnotationLayerConfigResult } from '../types'; +import { strings } from '../i18n'; export function annotationLayerFunction(): ExpressionFunctionDefinition< typeof ANNOTATION_LAYER, Datatable, AnnotationLayerArgs, - CommonXYAnnotationLayerConfigResult + AnnotationLayerConfigResult > { return { name: ANNOTATION_LAYER, aliases: [], type: ANNOTATION_LAYER, inputTypes: ['datatable'], - help: i18n.translate('expressionXY.annotationLayer.help', { - defaultMessage: `Configure an annotation layer in the xy chart`, - }), + help: strings.getAnnotationLayerFnHelp(), args: { hide: { types: ['boolean'], default: false, - help: i18n.translate('expressionXY.annotationLayer.hide.help', { - defaultMessage: 'Show / hide details', - }), + help: strings.getAnnotationLayerHideHelp(), }, annotations: { types: ['manual_event_annotation'], - help: i18n.translate('expressionXY.annotationLayer.annotations.help', { - defaultMessage: 'Annotations', - }), + help: strings.getAnnotationLayerAnnotationsHelp(), multi: true, }, }, diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_xy_args.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_xy_args.ts index 535c8c6e3357b..f2a73a220364d 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_xy_args.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_xy_args.ts @@ -42,14 +42,17 @@ export const commonXYArgs: Omit< yLeftExtent: { types: [AXIS_EXTENT_CONFIG], help: strings.getYLeftExtentHelp(), + default: `{${AXIS_EXTENT_CONFIG}}`, }, yRightExtent: { types: [AXIS_EXTENT_CONFIG], help: strings.getYRightExtentHelp(), + default: `{${AXIS_EXTENT_CONFIG}}`, }, legend: { types: [LEGEND_CONFIG], help: strings.getLegendHelp(), + default: `{${LEGEND_CONFIG}}`, }, fittingFunction: { types: ['string'], @@ -61,6 +64,7 @@ export const commonXYArgs: Omit< types: ['string'], options: [...Object.values(EndValues)], help: strings.getEndValueHelp(), + strict: true, }, emphasizeFitting: { types: ['boolean'], @@ -72,6 +76,7 @@ export const commonXYArgs: Omit< options: [...Object.values(ValueLabelModes)], help: strings.getValueLabelsHelp(), strict: true, + default: ValueLabelModes.HIDE, }, tickLabelsVisibilitySettings: { types: [TICK_LABELS_CONFIG], diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_annotation_layer.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_annotation_layer.ts new file mode 100644 index 0000000000000..a87c59925f484 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_annotation_layer.ts @@ -0,0 +1,51 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { Datatable, ExpressionFunctionDefinition } from '@kbn/expressions-plugin/common'; +import { LayerTypes, EXTENDED_ANNOTATION_LAYER } from '../constants'; +import { ExtendedAnnotationLayerConfigResult, ExtendedAnnotationLayerArgs } from '../types'; +import { strings } from '../i18n'; + +export function extendedAnnotationLayerFunction(): ExpressionFunctionDefinition< + typeof EXTENDED_ANNOTATION_LAYER, + Datatable, + ExtendedAnnotationLayerArgs, + ExtendedAnnotationLayerConfigResult +> { + return { + name: EXTENDED_ANNOTATION_LAYER, + aliases: [], + type: EXTENDED_ANNOTATION_LAYER, + inputTypes: ['datatable'], + help: strings.getAnnotationLayerFnHelp(), + args: { + hide: { + types: ['boolean'], + default: false, + help: strings.getAnnotationLayerHideHelp(), + }, + annotations: { + types: ['manual_event_annotation'], + help: strings.getAnnotationLayerAnnotationsHelp(), + multi: true, + }, + layerId: { + types: ['string'], + help: strings.getLayerIdHelp(), + }, + }, + fn: (input, args) => { + return { + type: EXTENDED_ANNOTATION_LAYER, + ...args, + annotations: args.annotations ?? [], + layerType: LayerTypes.ANNOTATIONS, + }; + }, + }; +} diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_data_layer.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_data_layer.ts index 6f912cb94b807..84c1213fc069d 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_data_layer.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_data_layer.ts @@ -23,6 +23,10 @@ export const extendedDataLayerFunction: ExtendedDataLayerFn = { types: ['datatable'], help: strings.getTableHelp(), }, + layerId: { + types: ['string'], + help: strings.getLayerIdHelp(), + }, }, fn(input, args) { return { diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_reference_line_layer.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_reference_line_layer.ts index bccbd85c0be8f..4f75838bea114 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_reference_line_layer.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_reference_line_layer.ts @@ -23,6 +23,10 @@ export const extendedReferenceLineLayerFunction: ExtendedReferenceLineLayerFn = types: ['datatable'], help: strings.getTableHelp(), }, + layerId: { + types: ['string'], + help: strings.getLayerIdHelp(), + }, }, fn(input, args) { return { diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/index.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/index.ts index fb709f430f2ea..ab1d570a07351 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/index.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/index.ts @@ -10,6 +10,7 @@ export * from './xy_vis'; export * from './layered_xy_vis'; export * from './legend_config'; export * from './annotation_layer'; +export * from './extended_annotation_layer'; export * from './y_axis_config'; export * from './extended_y_axis_config'; export * from './data_layer'; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts index e5b1694441ed8..282b53fac03eb 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts @@ -7,17 +7,18 @@ */ import { i18n } from '@kbn/i18n'; -import { LayeredXyVisFn, XYExtendedLayerConfigResult } from '../types'; +import { LayeredXyVisFn } from '../types'; import { XY_VIS_RENDERER, EXTENDED_DATA_LAYER, EXTENDED_REFERENCE_LINE_LAYER, LAYERED_XY_VIS, - ANNOTATION_LAYER, + EXTENDED_ANNOTATION_LAYER, } from '../constants'; import { logDatatables } from '../utils'; import { commonXYArgs } from './common_xy_args'; import { strings } from '../i18n'; +import { appendLayerIds } from '../helpers'; export const layeredXyVisFunction: LayeredXyVisFn = { name: LAYERED_XY_VIS, @@ -27,7 +28,7 @@ export const layeredXyVisFunction: LayeredXyVisFn = { args: { ...commonXYArgs, layers: { - types: [EXTENDED_DATA_LAYER, EXTENDED_REFERENCE_LINE_LAYER, ANNOTATION_LAYER], + types: [EXTENDED_DATA_LAYER, EXTENDED_REFERENCE_LINE_LAYER, EXTENDED_ANNOTATION_LAYER], help: i18n.translate('expressionXY.layeredXyVis.layers.help', { defaultMessage: 'Layers of visual series', }), @@ -35,9 +36,7 @@ export const layeredXyVisFunction: LayeredXyVisFn = { }, }, fn(data, args, handlers) { - const layers = (args.layers ?? []).filter( - (layer): layer is XYExtendedLayerConfigResult => layer !== undefined - ); + const layers = appendLayerIds(args.layers ?? [], 'layers'); logDatatables(layers, handlers); diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis_fn.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis_fn.ts index 1194f0f209671..23516508dcb09 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis_fn.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis_fn.ts @@ -9,12 +9,8 @@ import { i18n } from '@kbn/i18n'; import { Dimension, prepareLogTable } from '@kbn/visualizations-plugin/common/utils'; import { AxisExtentModes, LayerTypes, ValueLabelModes, XY_VIS_RENDERER } from '../constants'; -import { - AxisExtentConfigResult, - DataLayerConfigResult, - XYLayerConfigResult, - XyVisFn, -} from '../types'; +import { appendLayerIds } from '../helpers'; +import { AxisExtentConfigResult, DataLayerConfigResult, XYLayerConfig, XyVisFn } from '../types'; import { getLayerDimensions } from '../utils'; const errors = { @@ -65,16 +61,12 @@ const validateExtent = ( export const xyVisFn: XyVisFn['fn'] = async (data, args, handlers) => { const { dataLayers = [], referenceLineLayers = [], annotationLayers = [], ...restArgs } = args; - const inputLayers: Array = [ - ...dataLayers, - ...referenceLineLayers, - ...annotationLayers, + const layers: XYLayerConfig[] = [ + ...appendLayerIds(dataLayers, 'dataLayers'), + ...appendLayerIds(referenceLineLayers, 'referenceLineLayers'), + ...appendLayerIds(annotationLayers, 'annotationLayers'), ]; - const layers: XYLayerConfigResult[] = inputLayers.filter( - (layer): layer is XYLayerConfigResult => layer !== undefined - ); - if (handlers.inspectorAdapters.tables) { const layerDimensions = layers.reduce((dimensions, layer) => { if (layer.layerType === LayerTypes.ANNOTATIONS) { diff --git a/src/plugins/chart_expressions/expression_xy/common/helpers/index.ts b/src/plugins/chart_expressions/expression_xy/common/helpers/index.ts new file mode 100644 index 0000000000000..55c4136e0c00d --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/helpers/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 { appendLayerIds } from './layers'; diff --git a/src/plugins/chart_expressions/expression_xy/common/helpers/layers.ts b/src/plugins/chart_expressions/expression_xy/common/helpers/layers.ts new file mode 100644 index 0000000000000..344fb3b460cdd --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/helpers/layers.ts @@ -0,0 +1,26 @@ +/* + * 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 { WithLayerId } from '../types'; + +function isWithLayerId(layer: T): layer is T & WithLayerId { + return (layer as T & WithLayerId).layerId ? true : false; +} + +export const generateLayerId = (keyword: string, index: number) => `${keyword}-${index}`; + +export function appendLayerIds( + layers: Array, + keyword: string +): Array { + return layers + .filter((l): l is T => l !== undefined) + .map((l, index) => ({ + ...l, + layerId: isWithLayerId(l) ? l.layerId : generateLayerId(keyword, index), + })); +} diff --git a/src/plugins/chart_expressions/expression_xy/common/i18n/index.tsx b/src/plugins/chart_expressions/expression_xy/common/i18n/index.tsx index a39b0157f9611..225f9de0d6a7c 100644 --- a/src/plugins/chart_expressions/expression_xy/common/i18n/index.tsx +++ b/src/plugins/chart_expressions/expression_xy/common/i18n/index.tsx @@ -165,6 +165,10 @@ export const strings = { i18n.translate('expressionXY.layers.table.help', { defaultMessage: 'Table', }), + getLayerIdHelp: () => + i18n.translate('expressionXY.layers.layerId.help', { + defaultMessage: 'Layer ID', + }), getRLAccessorsHelp: () => i18n.translate('expressionXY.referenceLineLayer.accessors.help', { defaultMessage: 'The columns to display on the y axis.', @@ -193,4 +197,16 @@ export const strings = { i18n.translate('expressionXY.yConfig.color.help', { defaultMessage: 'The color of the series', }), + getAnnotationLayerFnHelp: () => + i18n.translate('expressionXY.annotationLayer.help', { + defaultMessage: `Configure an annotation layer in the xy chart`, + }), + getAnnotationLayerHideHelp: () => + i18n.translate('expressionXY.annotationLayer.hide.help', { + defaultMessage: 'Show / hide details', + }), + getAnnotationLayerAnnotationsHelp: () => + i18n.translate('expressionXY.annotationLayer.annotations.help', { + defaultMessage: 'Annotations', + }), }; diff --git a/src/plugins/chart_expressions/expression_xy/common/index.ts b/src/plugins/chart_expressions/expression_xy/common/index.ts index d3ebe9abb812d..78c2bf482002d 100755 --- a/src/plugins/chart_expressions/expression_xy/common/index.ts +++ b/src/plugins/chart_expressions/expression_xy/common/index.ts @@ -32,12 +32,14 @@ export type { LensMultiTable, ValueLabelMode, AxisExtentMode, + DataLayerConfig, FittingFunction, ExtendedYConfig, AxisExtentConfig, CollectiveConfig, LegendConfigResult, AxesSettingsConfig, + CommonXYLayerConfig, AnnotationLayerArgs, XYLayerConfigResult, ExtendedYConfigResult, @@ -46,16 +48,18 @@ export type { TickLabelsConfigResult, AxisExtentConfigResult, ReferenceLineLayerArgs, + CommonXYDataLayerConfig, LabelsOrientationConfig, - CommonXYLayerConfigResult, + ReferenceLineLayerConfig, AvailableReferenceLineIcon, XYExtendedLayerConfigResult, + CommonXYAnnotationLayerConfig, ExtendedDataLayerConfigResult, LabelsOrientationConfigResult, CommonXYDataLayerConfigResult, ReferenceLineLayerConfigResult, + CommonXYReferenceLineLayerConfig, AxisTitlesVisibilityConfigResult, - CommonXYAnnotationLayerConfigResult, ExtendedReferenceLineLayerConfigResult, CommonXYReferenceLineLayerConfigResult, } from './types'; diff --git a/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts b/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts index d3b3c4b7ed2a0..5c311aed6798b 100644 --- a/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts +++ b/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts @@ -42,6 +42,7 @@ import { AvailableReferenceLineIcons, XY_VIS, LAYERED_XY_VIS, + EXTENDED_ANNOTATION_LAYER, } from '../constants'; import { XYRender } from './expression_renderers'; @@ -112,6 +113,7 @@ export interface ValidLayer extends DataLayerConfigResult { } export interface ExtendedDataLayerArgs { + layerId?: string; accessors: string[]; seriesType: SeriesType; xAccessor?: string; @@ -193,7 +195,7 @@ export interface XYArgs { valueLabels: ValueLabelMode; dataLayers: DataLayerConfigResult[]; referenceLineLayers: ReferenceLineLayerConfigResult[]; - annotationLayers: CommonXYAnnotationLayerConfigResult[]; + annotationLayers: AnnotationLayerConfigResult[]; fittingFunction?: FittingFunction; axisTitlesVisibilitySettings?: AxisTitlesVisibilityConfigResult; tickLabelsVisibilitySettings?: TickLabelsConfigResult; @@ -237,7 +239,7 @@ export interface XYProps { yRightExtent: AxisExtentConfigResult; legend: LegendConfigResult; valueLabels: ValueLabelMode; - layers: CommonXYLayerConfigResult[]; + layers: CommonXYLayerConfig[]; endValue?: EndValue; emphasizeFitting?: boolean; fittingFunction?: FittingFunction; @@ -257,11 +259,20 @@ export interface AnnotationLayerArgs { hide?: boolean; } -export type CommonXYAnnotationLayerConfigResult = AnnotationLayerArgs & { +export type ExtendedAnnotationLayerArgs = AnnotationLayerArgs & { + layerId?: string; +}; + +export type AnnotationLayerConfigResult = AnnotationLayerArgs & { type: typeof ANNOTATION_LAYER; layerType: typeof LayerTypes.ANNOTATIONS; }; +export type ExtendedAnnotationLayerConfigResult = ExtendedAnnotationLayerArgs & { + type: typeof EXTENDED_ANNOTATION_LAYER; + layerType: typeof LayerTypes.ANNOTATIONS; +}; + export interface ReferenceLineLayerArgs { accessors: string[]; columnToLabel?: string; @@ -269,6 +280,7 @@ export interface ReferenceLineLayerArgs { } export interface ExtendedReferenceLineLayerArgs { + layerId?: string; accessors: string[]; columnToLabel?: string; yConfig?: ExtendedYConfigResult[]; @@ -276,16 +288,20 @@ export interface ExtendedReferenceLineLayerArgs { } export type XYLayerArgs = DataLayerArgs | ReferenceLineLayerArgs | AnnotationLayerArgs; +export type XYLayerConfig = DataLayerConfig | ReferenceLineLayerConfig | AnnotationLayerConfig; +export type XYExtendedLayerConfig = + | ExtendedDataLayerConfig + | ExtendedReferenceLineLayerConfig + | ExtendedAnnotationLayerConfig; export type XYLayerConfigResult = | DataLayerConfigResult | ReferenceLineLayerConfigResult - | CommonXYAnnotationLayerConfigResult; - + | AnnotationLayerConfigResult; export type XYExtendedLayerConfigResult = | ExtendedDataLayerConfigResult | ExtendedReferenceLineLayerConfigResult - | CommonXYAnnotationLayerConfigResult; + | ExtendedAnnotationLayerConfigResult; export interface LensMultiTable { type: typeof MULTITABLE; @@ -315,6 +331,18 @@ export type DataLayerConfigResult = Omit & { table: Datatable; }; +export interface WithLayerId { + layerId: string; +} + +export type DataLayerConfig = DataLayerConfigResult & WithLayerId; +export type ReferenceLineLayerConfig = ReferenceLineLayerConfigResult & WithLayerId; +export type AnnotationLayerConfig = AnnotationLayerConfigResult & WithLayerId; + +export type ExtendedDataLayerConfig = ExtendedDataLayerConfigResult & WithLayerId; +export type ExtendedReferenceLineLayerConfig = ExtendedReferenceLineLayerConfigResult & WithLayerId; +export type ExtendedAnnotationLayerConfig = ExtendedAnnotationLayerConfigResult & WithLayerId; + export type ExtendedDataLayerConfigResult = Omit & { type: typeof EXTENDED_DATA_LAYER; layerType: typeof LayerTypes.DATA; @@ -338,12 +366,19 @@ export type AxisExtentConfigResult = AxisExtentConfig & { type: typeof AXIS_EXTE export type GridlinesConfigResult = AxesSettingsConfig & { type: typeof GRID_LINES_CONFIG }; export type TickLabelsConfigResult = AxesSettingsConfig & { type: typeof TICK_LABELS_CONFIG }; -export type CommonXYLayerConfigResult = XYLayerConfigResult | XYExtendedLayerConfigResult; +export type CommonXYLayerConfig = XYLayerConfig | XYExtendedLayerConfig; export type CommonXYDataLayerConfigResult = DataLayerConfigResult | ExtendedDataLayerConfigResult; export type CommonXYReferenceLineLayerConfigResult = | ReferenceLineLayerConfigResult | ExtendedReferenceLineLayerConfigResult; +export type CommonXYDataLayerConfig = DataLayerConfig | ExtendedDataLayerConfig; +export type CommonXYReferenceLineLayerConfig = + | ReferenceLineLayerConfig + | ExtendedReferenceLineLayerConfig; + +export type CommonXYAnnotationLayerConfig = AnnotationLayerConfig | ExtendedAnnotationLayerConfig; + export type XyVisFn = ExpressionFunctionDefinition< typeof XY_VIS, Datatable, diff --git a/src/plugins/chart_expressions/expression_xy/common/utils/log_datatables.ts b/src/plugins/chart_expressions/expression_xy/common/utils/log_datatables.ts index 88d194d4646b5..16106b6763628 100644 --- a/src/plugins/chart_expressions/expression_xy/common/utils/log_datatables.ts +++ b/src/plugins/chart_expressions/expression_xy/common/utils/log_datatables.ts @@ -11,28 +11,27 @@ import { Dimension, prepareLogTable } from '@kbn/visualizations-plugin/common/ut import { LayerTypes } from '../constants'; import { strings } from '../i18n'; import { - CommonXYDataLayerConfigResult, - CommonXYLayerConfigResult, - CommonXYReferenceLineLayerConfigResult, + CommonXYDataLayerConfig, + CommonXYLayerConfig, + CommonXYReferenceLineLayerConfig, } from '../types'; -export const logDatatables = (layers: CommonXYLayerConfigResult[], handlers: ExecutionContext) => { +export const logDatatables = (layers: CommonXYLayerConfig[], handlers: ExecutionContext) => { if (!handlers?.inspectorAdapters?.tables) { return; } - layers.forEach((layer, index) => { + layers.forEach((layer) => { if (layer.layerType === LayerTypes.ANNOTATIONS) { return; } const logTable = prepareLogTable(layer.table, getLayerDimensions(layer), true); - - handlers.inspectorAdapters.tables.logDatatable(index, logTable); + handlers.inspectorAdapters.tables.logDatatable(layer.layerId, logTable); }); }; export const getLayerDimensions = ( - layer: CommonXYDataLayerConfigResult | CommonXYReferenceLineLayerConfigResult + layer: CommonXYDataLayerConfig | CommonXYReferenceLineLayerConfig ): Dimension[] => { let xAccessor; let splitAccessor; diff --git a/src/plugins/chart_expressions/expression_xy/public/__mocks__/index.tsx b/src/plugins/chart_expressions/expression_xy/public/__mocks__/index.tsx index eb139232e2783..194bfc2bf5c9d 100644 --- a/src/plugins/chart_expressions/expression_xy/public/__mocks__/index.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/__mocks__/index.tsx @@ -8,9 +8,9 @@ import { Datatable } from '@kbn/expressions-plugin/common'; import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; -import { DataLayerConfigResult, LensMultiTable } from '../../common'; +import { LensMultiTable } from '../../common'; import { LayerTypes } from '../../common/constants'; -import { XYProps } from '../../common/types'; +import { DataLayerConfig, XYProps } from '../../common/types'; import { mockPaletteOutput, sampleArgs } from '../../common/__mocks__'; const chartSetupContract = chartPluginMock.createSetupContract(); @@ -168,7 +168,8 @@ export const dateHistogramData: LensMultiTable = { }, }; -export const dateHistogramLayer: DataLayerConfigResult = { +export const dateHistogramLayer: DataLayerConfig = { + layerId: 'dateHistogramLayer', type: 'dataLayer', layerType: LayerTypes.DATA, hide: false, @@ -202,6 +203,7 @@ export function sampleArgsWithReferenceLine(value: number = 150) { layers: [ ...sArgs.layers, { + layerId: 'referenceLine-a', type: 'referenceLineLayer', layerType: LayerTypes.REFERENCELINE, accessors: ['referenceLine-a'], diff --git a/src/plugins/chart_expressions/expression_xy/public/components/__snapshots__/xy_chart.test.tsx.snap b/src/plugins/chart_expressions/expression_xy/public/components/__snapshots__/xy_chart.test.tsx.snap index 304ef186330ed..b0b0e4d6ab4ad 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/__snapshots__/xy_chart.test.tsx.snap +++ b/src/plugins/chart_expressions/expression_xy/public/components/__snapshots__/xy_chart.test.tsx.snap @@ -326,7 +326,7 @@ exports[`XYChart component it renders area 1`] = ` { }; export const getAnnotationsGroupedByInterval = ( - layers: CommonXYAnnotationLayerConfigResult[], + layers: CommonXYAnnotationLayerConfig[], minInterval?: number, firstTimestamp?: number, formatter?: FieldFormat diff --git a/src/plugins/chart_expressions/expression_xy/public/components/data_layers.tsx b/src/plugins/chart_expressions/expression_xy/public/components/data_layers.tsx index 7ab57bee14562..2ea503c404078 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/data_layers.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/data_layers.tsx @@ -18,7 +18,7 @@ import { PaletteRegistry } from '@kbn/coloring'; import { FormatFactory } from '@kbn/field-formats-plugin/common'; import { Datatable } from '@kbn/expressions-plugin'; import { - CommonXYDataLayerConfigResult, + CommonXYDataLayerConfig, EndValue, FittingFunction, ValueLabelMode, @@ -34,7 +34,7 @@ import { } from '../helpers'; interface Props { - layers: CommonXYDataLayerConfigResult[]; + layers: CommonXYDataLayerConfig[]; formatFactory: FormatFactory; chartHasMoreThanOneBarSeries?: boolean; yAxesConfiguration: GroupsConfiguration; @@ -42,7 +42,7 @@ interface Props { fittingFunction?: FittingFunction; endValue?: EndValue | undefined; paletteService: PaletteRegistry; - areLayersAlreadyFormatted: Record>; + areLayersAlreadyFormatted: Record>; syncColors?: boolean; timeZone?: string; emphasizeFitting?: boolean; @@ -71,7 +71,7 @@ export const DataLayers: FC = ({ const colorAssignments = getColorAssignments(layers, formatFactory); return ( <> - {layers.flatMap((layer, layerIndex) => + {layers.flatMap((layer) => layer.accessors.map((accessor, accessorIndex) => { const { splitAccessor, seriesType, xAccessor, table, columnToLabel, xScaleType } = layer; const columnToLabelMap: Record = columnToLabel @@ -116,14 +116,13 @@ export const DataLayers: FC = ({ const seriesProps = getSeriesProps({ layer, - layerId: layerIndex, accessor, chartHasMoreThanOneBarSeries, colorAssignments, formatFactory, columnToLabelMap, paletteService, - alreadyFormattedColumns: areLayersAlreadyFormatted[layerIndex] ?? {}, + alreadyFormattedColumns: areLayersAlreadyFormatted[layer.layerId] ?? {}, syncColors, yAxis, timeZone, @@ -131,7 +130,7 @@ export const DataLayers: FC = ({ fillOpacity, }); - const index = `${layerIndex}-${accessorIndex}`; + const index = `${layer.layerId}-${accessorIndex}`; const curve = curveType ? CurveType[curveType] : undefined; diff --git a/src/plugins/chart_expressions/expression_xy/public/components/legend_action.test.tsx b/src/plugins/chart_expressions/expression_xy/public/components/legend_action.test.tsx index c62d0fda94960..7c60a6a3a5769 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/legend_action.test.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/legend_action.test.tsx @@ -11,7 +11,7 @@ import { LegendActionProps, SeriesIdentifier } from '@elastic/charts'; import { EuiPopover } from '@elastic/eui'; import { mountWithIntl } from '@kbn/test-jest-helpers'; import { ComponentType, ReactWrapper } from 'enzyme'; -import type { DataLayerConfigResult, LensMultiTable } from '../../common'; +import type { DataLayerConfig, LensMultiTable } from '../../common'; import { LayerTypes } from '../../common/constants'; import { getLegendAction } from './legend_action'; import { LegendActionPopover } from './legend_action_popover'; @@ -153,7 +153,8 @@ const tables = { }, } as LensMultiTable['tables']; -const sampleLayer: DataLayerConfigResult = { +const sampleLayer: DataLayerConfig = { + layerId: 'first', type: 'dataLayer', layerType: LayerTypes.DATA, seriesType: 'line', diff --git a/src/plugins/chart_expressions/expression_xy/public/components/legend_action.tsx b/src/plugins/chart_expressions/expression_xy/public/components/legend_action.tsx index 53e3708323702..e2a4ca8da554b 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/legend_action.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/legend_action.tsx @@ -9,15 +9,15 @@ import React from 'react'; import type { LegendAction, XYChartSeriesIdentifier } from '@elastic/charts'; import type { FilterEvent } from '../types'; -import type { CommonXYDataLayerConfigResult } from '../../common'; +import type { CommonXYDataLayerConfig } from '../../common'; import type { FormatFactory } from '../types'; import { LegendActionPopover } from './legend_action_popover'; export const getLegendAction = ( - dataLayers: CommonXYDataLayerConfigResult[], + dataLayers: CommonXYDataLayerConfig[], onFilter: (data: FilterEvent['data']) => void, formatFactory: FormatFactory, - layersAlreadyFormatted: Record> + layersAlreadyFormatted: Record> ): LegendAction => React.memo(({ series: [xySeries] }) => { const series = xySeries as XYChartSeriesIdentifier; @@ -42,7 +42,7 @@ export const getLegendAction = ( const formatter = formatFactory(splitColumn && splitColumn.meta?.params); const rowIndex = table.rows.findIndex((row) => { - if (layersAlreadyFormatted[layerIndex]?.[accessor]) { + if (layersAlreadyFormatted[layer.layerId]?.[accessor]) { // stringify the value to compare with the chart value return formatter.convert(row[accessor]) === splitLabel; } @@ -67,7 +67,7 @@ export const getLegendAction = ( return ( forAccessor), yConfig: yConfigs, type: 'referenceLineLayer', diff --git a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.tsx b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.tsx index dbde7291ae9be..d17dbf2a70ad1 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines.tsx @@ -13,11 +13,7 @@ import { groupBy } from 'lodash'; import { RectAnnotation, AnnotationDomainType, LineAnnotation, Position } from '@elastic/charts'; import { euiLightVars } from '@kbn/ui-theme'; import type { FieldFormat } from '@kbn/field-formats-plugin/common'; -import type { - CommonXYReferenceLineLayerConfigResult, - IconPosition, - YAxisMode, -} from '../../common/types'; +import type { CommonXYReferenceLineLayerConfig, IconPosition, YAxisMode } from '../../common/types'; import { LINES_MARKER_SIZE, mapVerticalToHorizontalPlacement, @@ -92,7 +88,7 @@ export function getBaseIconPlacement( } export interface ReferenceLineAnnotationsProps { - layers: CommonXYReferenceLineLayerConfigResult[]; + layers: CommonXYReferenceLineLayerConfig[]; formatters: Record<'left' | 'right' | 'bottom', FieldFormat | undefined>; axesMap: Record<'left' | 'right', boolean>; isHorizontal: boolean; @@ -108,7 +104,7 @@ export const ReferenceLineAnnotations = ({ }: ReferenceLineAnnotationsProps) => { return ( <> - {layers.flatMap((layer, index) => { + {layers.flatMap((layer) => { if (!layer.yConfig) { return []; } @@ -194,8 +190,8 @@ export const ReferenceLineAnnotations = ({ annotations.push( ({ dataValue: row[yConfig.forAccessor], header: columnToLabelMap[yConfig.forAccessor], @@ -225,8 +221,8 @@ export const ReferenceLineAnnotations = ({ annotations.push( { const nextValue = shouldCheckNextReferenceLine ? row[groupedByDirection[yConfig.fill!][indexFromSameType + 1].forAccessor] diff --git a/src/plugins/chart_expressions/expression_xy/public/components/x_domain.tsx b/src/plugins/chart_expressions/expression_xy/public/components/x_domain.tsx index c08bd6286a7b0..78b6ef91926a8 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/x_domain.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/x_domain.tsx @@ -11,7 +11,7 @@ import React from 'react'; import moment from 'moment'; import { Endzones } from '@kbn/charts-plugin/public'; import { search } from '@kbn/data-plugin/public'; -import type { CommonXYDataLayerConfigResult } from '../../common'; +import type { CommonXYDataLayerConfig } from '../../common'; export interface XDomain { min?: number; @@ -19,7 +19,7 @@ export interface XDomain { minInterval?: number; } -export const getAppliedTimeRange = (layers: CommonXYDataLayerConfigResult[]) => { +export const getAppliedTimeRange = (layers: CommonXYDataLayerConfig[]) => { return layers .map(({ xAccessor, table }) => { const xColumn = table.columns.find((col) => col.id === xAccessor); @@ -28,7 +28,7 @@ export const getAppliedTimeRange = (layers: CommonXYDataLayerConfigResult[]) => if (timeRange) { return { timeRange, - field: xColumn.meta.field, + field: xColumn?.meta.field, }; } }) @@ -36,7 +36,7 @@ export const getAppliedTimeRange = (layers: CommonXYDataLayerConfigResult[]) => }; export const getXDomain = ( - layers: CommonXYDataLayerConfigResult[], + layers: CommonXYDataLayerConfig[], minInterval: number | undefined, isTimeViz: boolean, isHistogram: boolean diff --git a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx index 76366f24a76c2..b56bb17a29543 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx @@ -30,7 +30,7 @@ import { Datatable } from '@kbn/expressions-plugin/common'; import { EmptyPlaceholder } from '@kbn/charts-plugin/public'; import { eventAnnotationServiceMock } from '@kbn/event-annotation-plugin/public/mocks'; import { EventAnnotationOutput } from '@kbn/event-annotation-plugin/common'; -import { CommonXYAnnotationLayerConfigResult, DataLayerConfigResult } from '../../common'; +import { CommonXYAnnotationLayerConfig, DataLayerConfig } from '../../common'; import { LayerTypes } from '../../common/constants'; import { XyEndzones } from './x_domain'; import { @@ -49,7 +49,7 @@ import { sampleLayer, } from '../../common/__mocks__'; import { XYChart, XYChartRenderProps } from './xy_chart'; -import { ExtendedDataLayerConfigResult, XYChartProps, XYProps } from '../../common/types'; +import { ExtendedDataLayerConfig, XYChartProps, XYProps } from '../../common/types'; import { DataLayers } from './data_layers'; const onClickValue = jest.fn(); @@ -121,7 +121,7 @@ describe('XYChart component', () => { {...defaultProps} args={{ ...args, - layers: [{ ...(args.layers[0] as DataLayerConfigResult), seriesType: 'line' }], + layers: [{ ...(args.layers[0] as DataLayerConfig), seriesType: 'line' }], }} /> ); @@ -136,7 +136,8 @@ describe('XYChart component', () => { describe('date range', () => { const { data, args } = sampleArgs(); - const timeSampleLayer: DataLayerConfigResult = { + const timeSampleLayer: DataLayerConfig = { + layerId: 'timeLayer', type: 'dataLayer', layerType: LayerTypes.DATA, seriesType: 'line', @@ -185,7 +186,7 @@ describe('XYChart component', () => { ...args, layers: [ { - ...(args.layers[0] as DataLayerConfigResult), + ...(args.layers[0] as DataLayerConfig), seriesType: 'line', xScaleType: 'time', table: timeSampleLayer.table, @@ -214,8 +215,8 @@ describe('XYChart component', () => { args={{ ...multiLayerArgs, layers: [ - { ...(multiLayerArgs.layers[0] as DataLayerConfigResult), table: table1 }, - { ...(multiLayerArgs.layers[1] as DataLayerConfigResult), table: table2 }, + { ...(multiLayerArgs.layers[0] as DataLayerConfig), table: table1 }, + { ...(multiLayerArgs.layers[1] as DataLayerConfig), table: table2 }, ], }} /> @@ -232,7 +233,8 @@ describe('XYChart component', () => { }); describe('axis time', () => { - const defaultTimeLayer: DataLayerConfigResult = { + const defaultTimeLayer: DataLayerConfig = { + layerId: 'defaultTimeLayer', type: 'dataLayer', layerType: LayerTypes.DATA, seriesType: 'line', @@ -294,7 +296,7 @@ describe('XYChart component', () => { expect(axisStyle).toBe(3); }); test('it should disable the new time axis for a vertical bar with break down dimension', () => { - const timeLayer: DataLayerConfigResult = { + const timeLayer: DataLayerConfig = { ...defaultTimeLayer, seriesType: 'bar', }; @@ -319,7 +321,7 @@ describe('XYChart component', () => { }); test('it should enable the new time axis for a stacked vertical bar with break down dimension', () => { - const timeLayer: DataLayerConfigResult = { + const timeLayer: DataLayerConfig = { ...defaultTimeLayer, seriesType: 'bar_stacked', }; @@ -384,7 +386,7 @@ describe('XYChart component', () => { isHistogram: true, splitAccessor: undefined, table: newData, - } as DataLayerConfigResult, + } as DataLayerConfig, ], }; @@ -447,7 +449,7 @@ describe('XYChart component', () => { ...args, layers: [ { - ...(args.layers[0] as DataLayerConfigResult), + ...(args.layers[0] as DataLayerConfig), seriesType: 'bar', xScaleType: 'time', isHistogram: true, @@ -538,7 +540,7 @@ describe('XYChart component', () => { }, layers: [ { - ...(args.layers[0] as DataLayerConfigResult), + ...(args.layers[0] as DataLayerConfig), seriesType: 'area', }, ], @@ -567,7 +569,7 @@ describe('XYChart component', () => { }, layers: [ { - ...(args.layers[0] as DataLayerConfigResult), + ...(args.layers[0] as DataLayerConfig), seriesType: 'bar', }, ], @@ -590,7 +592,7 @@ describe('XYChart component', () => { fit: false, min: NaN, max: NaN, - includeDataFromIds: ['0-referenceLine-a-rect'], + includeDataFromIds: ['referenceLine-a-referenceLine-a-rect'], }); }); }); @@ -605,7 +607,7 @@ describe('XYChart component', () => { ...args, layers: [ { - ...(args.layers[0] as DataLayerConfigResult), + ...(args.layers[0] as DataLayerConfig), seriesType: 'line', xScaleType: 'linear', }, @@ -628,7 +630,7 @@ describe('XYChart component', () => { ...args, layers: [ { - ...(args.layers[0] as DataLayerConfigResult), + ...(args.layers[0] as DataLayerConfig), seriesType: 'line', xScaleType: 'linear', isHistogram: true, @@ -680,7 +682,7 @@ describe('XYChart component', () => { {...defaultProps} args={{ ...args, - layers: [{ ...(args.layers[0] as DataLayerConfigResult), seriesType: 'bar' }], + layers: [{ ...(args.layers[0] as DataLayerConfig), seriesType: 'bar' }], }} /> ); @@ -699,7 +701,7 @@ describe('XYChart component', () => { {...defaultProps} args={{ ...args, - layers: [{ ...(args.layers[0] as DataLayerConfigResult), seriesType: 'area' }], + layers: [{ ...(args.layers[0] as DataLayerConfig), seriesType: 'area' }], }} /> ); @@ -718,7 +720,7 @@ describe('XYChart component', () => { {...defaultProps} args={{ ...args, - layers: [{ ...(args.layers[0] as DataLayerConfigResult), seriesType: 'bar_horizontal' }], + layers: [{ ...(args.layers[0] as DataLayerConfig), seriesType: 'bar_horizontal' }], }} /> ); @@ -805,7 +807,8 @@ describe('XYChart component', () => { ], }; - const numberLayer: DataLayerConfigResult = { + const numberLayer: DataLayerConfig = { + layerId: 'numberLayer', type: 'dataLayer', layerType: LayerTypes.DATA, hide: false, @@ -880,6 +883,7 @@ describe('XYChart component', () => { ...args, layers: [ { + layerId: 'first', type: 'dataLayer', layerType: LayerTypes.DATA, isHistogram: true, @@ -1001,7 +1005,8 @@ describe('XYChart component', () => { ], }; - const numberLayer: DataLayerConfigResult = { + const numberLayer: DataLayerConfig = { + layerId: 'numberLayer', type: 'dataLayer', layerType: LayerTypes.DATA, hide: false, @@ -1078,6 +1083,7 @@ describe('XYChart component', () => { ...args, layers: [ { + layerId: 'first', type: 'dataLayer', layerType: LayerTypes.DATA, seriesType: 'line', @@ -1136,6 +1142,7 @@ describe('XYChart component', () => { ...args, layers: [ { + layerId: 'first', type: 'dataLayer', layerType: LayerTypes.DATA, seriesType: 'line', @@ -1166,6 +1173,7 @@ describe('XYChart component', () => { ...args, layers: [ { + layerId: 'first', type: 'dataLayer', layerType: LayerTypes.DATA, seriesType: 'line', @@ -1209,7 +1217,7 @@ describe('XYChart component', () => { {...defaultProps} args={{ ...args, - layers: [{ ...(args.layers[0] as DataLayerConfigResult), seriesType: 'bar_stacked' }], + layers: [{ ...(args.layers[0] as DataLayerConfig), seriesType: 'bar_stacked' }], }} /> ); @@ -1228,7 +1236,7 @@ describe('XYChart component', () => { {...defaultProps} args={{ ...args, - layers: [{ ...(args.layers[0] as DataLayerConfigResult), seriesType: 'area_stacked' }], + layers: [{ ...(args.layers[0] as DataLayerConfig), seriesType: 'area_stacked' }], }} /> ); @@ -1248,7 +1256,7 @@ describe('XYChart component', () => { args={{ ...args, layers: [ - { ...(args.layers[0] as DataLayerConfigResult), seriesType: 'bar_horizontal_stacked' }, + { ...(args.layers[0] as DataLayerConfig), seriesType: 'bar_horizontal_stacked' }, ], }} /> @@ -1272,7 +1280,7 @@ describe('XYChart component', () => { ...args, layers: [ { - ...(args.layers[0] as DataLayerConfigResult), + ...(args.layers[0] as DataLayerConfig), xAccessor: undefined, splitAccessor: 'e', seriesType: 'bar_stacked', @@ -1297,12 +1305,12 @@ describe('XYChart component', () => { test('it applies histogram mode to the series for single series', () => { const { args } = sampleArgs(); - const firstLayer: DataLayerConfigResult = { + const firstLayer: DataLayerConfig = { ...args.layers[0], accessors: ['b'], seriesType: 'bar', isHistogram: true, - } as DataLayerConfigResult; + } as DataLayerConfig; delete firstLayer.splitAccessor; const component = shallow( @@ -1314,11 +1322,11 @@ describe('XYChart component', () => { test('it does not apply histogram mode to more than one bar series for unstacked bar chart', () => { const { args } = sampleArgs(); - const firstLayer: DataLayerConfigResult = { + const firstLayer: DataLayerConfig = { ...args.layers[0], seriesType: 'bar', isHistogram: true, - } as DataLayerConfigResult; + } as DataLayerConfig; delete firstLayer.splitAccessor; const component = shallow( @@ -1331,17 +1339,17 @@ describe('XYChart component', () => { test('it applies histogram mode to more than one the series for unstacked line/area chart', () => { const { args } = sampleArgs(); - const firstLayer: DataLayerConfigResult = { + const firstLayer: DataLayerConfig = { ...args.layers[0], seriesType: 'line', isHistogram: true, - } as DataLayerConfigResult; + } as DataLayerConfig; delete firstLayer.splitAccessor; - const secondLayer: DataLayerConfigResult = { + const secondLayer: DataLayerConfig = { ...args.layers[0], seriesType: 'line', isHistogram: true, - } as DataLayerConfigResult; + } as DataLayerConfig; delete secondLayer.splitAccessor; const component = shallow( @@ -1361,7 +1369,7 @@ describe('XYChart component', () => { ...args, layers: [ { - ...(args.layers[0] as DataLayerConfigResult), + ...(args.layers[0] as DataLayerConfig), seriesType: 'bar_stacked', isHistogram: true, }, @@ -1383,7 +1391,7 @@ describe('XYChart component', () => { args={{ ...args, layers: [ - { ...(args.layers[0] as DataLayerConfigResult), seriesType: 'bar', isHistogram: true }, + { ...(args.layers[0] as DataLayerConfig), seriesType: 'bar', isHistogram: true }, ], }} /> @@ -1396,7 +1404,7 @@ describe('XYChart component', () => { describe('y axes', () => { const args = createArgsWithLayers(); - const layer = args.layers[0] as DataLayerConfigResult; + const layer = args.layers[0] as DataLayerConfig; test('single axis if possible', () => { const newArgs = { @@ -1497,7 +1505,7 @@ describe('XYChart component', () => { describe('y series coloring', () => { const args = createArgsWithLayers(); - const layer = args.layers[0] as DataLayerConfigResult; + const layer = args.layers[0] as DataLayerConfig; test('color is applied to chart for multiple series', () => { const newArgs: XYProps = { @@ -1521,7 +1529,7 @@ describe('XYChart component', () => { }, ], table: dataWithoutFormats, - } as ExtendedDataLayerConfigResult, + } as ExtendedDataLayerConfig, { ...layer, type: 'extendedDataLayer', @@ -1535,7 +1543,7 @@ describe('XYChart component', () => { }, ], table: dataWithoutFormats, - } as ExtendedDataLayerConfigResult, + } as ExtendedDataLayerConfig, ], }; @@ -1838,7 +1846,7 @@ describe('XYChart component', () => { {...defaultProps} args={{ ...args, - layers: [{ ...(args.layers[0] as DataLayerConfigResult), xScaleType: 'ordinal' }], + layers: [{ ...(args.layers[0] as DataLayerConfig), xScaleType: 'ordinal' }], }} /> ); @@ -1856,7 +1864,7 @@ describe('XYChart component', () => { {...defaultProps} args={{ ...args, - layers: [{ ...(args.layers[0] as DataLayerConfigResult), yScaleType: 'sqrt' }], + layers: [{ ...(args.layers[0] as DataLayerConfig), yScaleType: 'sqrt' }], }} /> ); @@ -1882,7 +1890,7 @@ describe('XYChart component', () => { {...defaultProps} args={{ ...args, - layers: [{ ...(args.layers[0] as DataLayerConfigResult), accessors: ['a'] }], + layers: [{ ...(args.layers[0] as DataLayerConfig), accessors: ['a'] }], }} /> ); @@ -2095,6 +2103,7 @@ describe('XYChart component', () => { }, layers: [ { + layerId: 'first', type: 'dataLayer', layerType: LayerTypes.DATA, seriesType: 'line', @@ -2109,6 +2118,7 @@ describe('XYChart component', () => { table: data1, }, { + layerId: 'second', type: 'dataLayer', layerType: LayerTypes.DATA, seriesType: 'line', @@ -2182,6 +2192,7 @@ describe('XYChart component', () => { }, layers: [ { + layerId: 'first', type: 'dataLayer', layerType: LayerTypes.DATA, seriesType: 'line', @@ -2253,6 +2264,7 @@ describe('XYChart component', () => { }, layers: [ { + layerId: 'first', type: 'dataLayer', layerType: LayerTypes.DATA, seriesType: 'line', @@ -2284,7 +2296,7 @@ describe('XYChart component', () => { ...args, layers: [ { - ...(args.layers[0] as DataLayerConfigResult), + ...(args.layers[0] as DataLayerConfig), accessors: ['a'], splitAccessor: undefined, }, @@ -2307,7 +2319,7 @@ describe('XYChart component', () => { ...args, layers: [ { - ...(args.layers[0] as DataLayerConfigResult), + ...(args.layers[0] as DataLayerConfig), accessors: ['a'], splitAccessor: undefined, }, @@ -2386,7 +2398,7 @@ describe('XYChart component', () => { test('it should apply None fitting function if not specified', () => { const { args } = sampleArgs(); - (args.layers[0] as DataLayerConfigResult).accessors = ['a']; + (args.layers[0] as DataLayerConfig).accessors = ['a']; const component = shallow(); @@ -2472,7 +2484,8 @@ describe('XYChart component', () => { ], }; - const timeSampleLayer: DataLayerConfigResult = { + const timeSampleLayer: DataLayerConfig = { + layerId: 'timeLayer', type: 'dataLayer', layerType: LayerTypes.DATA, seriesType: 'line', @@ -2518,8 +2531,9 @@ describe('XYChart component', () => { lineStyle: 'dashed', lineWidth: 3, }; - const sampleAnnotationLayers: CommonXYAnnotationLayerConfigResult[] = [ + const sampleAnnotationLayers: CommonXYAnnotationLayerConfig[] = [ { + layerId: 'annotationLayer', type: 'annotationLayer', layerType: LayerTypes.ANNOTATIONS, annotations: [ @@ -2548,7 +2562,7 @@ describe('XYChart component', () => { }); test('should render simplified annotation when hide is true', () => { const { args } = sampleArgsWithAnnotation(); - (args.layers[0] as CommonXYAnnotationLayerConfigResult).hide = true; + (args.layers[0] as CommonXYAnnotationLayerConfig).hide = true; const component = mount(); expect(component.find('LineAnnotation')).toMatchSnapshot(); }); @@ -2556,6 +2570,7 @@ describe('XYChart component', () => { test('should render grouped annotations preserving the shared styles', () => { const { args } = sampleArgsWithAnnotation([ { + layerId: 'annotationLayer', type: 'annotationLayer', layerType: LayerTypes.ANNOTATIONS, annotations: [ @@ -2590,11 +2605,13 @@ describe('XYChart component', () => { test('should render grouped annotations with default styles', () => { const { args } = sampleArgsWithAnnotation([ { + layerId: 'annotationLayer', type: 'annotationLayer', layerType: LayerTypes.ANNOTATIONS, annotations: [sampleStyledAnnotation], }, { + layerId: 'annotationLayer2', type: 'annotationLayer', layerType: LayerTypes.ANNOTATIONS, annotations: [ @@ -2620,6 +2637,7 @@ describe('XYChart component', () => { test('should not render hidden annotations', () => { const { args } = sampleArgsWithAnnotation([ { + layerId: 'annotationLayer', type: 'annotationLayer', layerType: LayerTypes.ANNOTATIONS, annotations: [ diff --git a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx index 853ba6c03570b..5b7b0b5577394 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx @@ -33,7 +33,7 @@ import { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public' import { ChartsPluginSetup, ChartsPluginStart, useActiveCursor } from '@kbn/charts-plugin/public'; import { MULTILAYER_TIME_AXIS_STYLE } from '@kbn/charts-plugin/common'; import type { FilterEvent, BrushEvent, FormatFactory } from '../types'; -import type { SeriesType, XYChartProps } from '../../common/types'; +import type { CommonXYDataLayerConfig, SeriesType, XYChartProps } from '../../common/types'; import { isHorizontalChart, getAnnotationsLayers, @@ -53,7 +53,7 @@ import { getXDomain, XyEndzones } from './x_domain'; import { getLegendAction } from './legend_action'; import { ReferenceLineAnnotations, computeChartMargins } from './reference_lines'; import { visualizationDefinitions } from '../definitions'; -import { CommonXYDataLayerConfigResult, CommonXYLayerConfigResult } from '../../common/types'; +import { CommonXYLayerConfig } from '../../common/types'; import { Annotations, getAnnotationsGroupedByInterval } from './annotations'; import { SeriesTypes, ValueLabelModes } from '../../common/constants'; import { DataLayers } from './data_layers'; @@ -144,11 +144,8 @@ export function XYChart({ const chartBaseTheme = chartsThemeService.useChartsBaseTheme(); const darkMode = chartsThemeService.useDarkMode(); const filteredLayers = getFilteredLayers(layers); - const layersById = filteredLayers.reduce>( - (hashMap, layer, index) => { - hashMap[index] = layer; - return hashMap; - }, + const layersById = filteredLayers.reduce>( + (hashMap, layer) => ({ ...hashMap, [layer.layerId]: layer }), {} ); @@ -163,7 +160,7 @@ export function XYChart({ return ; } - const dataLayers: CommonXYDataLayerConfigResult[] = filteredLayers.filter(isDataLayer); + const dataLayers: CommonXYDataLayerConfig[] = filteredLayers.filter(isDataLayer); // use formatting hint of first x axis column to format ticks const xAxisColumn = dataLayers[0]?.table.columns.find(({ id }) => id === dataLayers[0].xAccessor); @@ -173,7 +170,7 @@ export function XYChart({ // This is a safe formatter for the xAccessor that abstracts the knowledge of already formatted layers const safeXAccessorLabelRenderer = (value: unknown): string => - xAxisColumn && areLayersAlreadyFormatted[0]?.[xAxisColumn.id] + xAxisColumn && areLayersAlreadyFormatted[dataLayers[0]?.layerId]?.[xAxisColumn.id] ? String(value) : String(xAxisFormatter.convert(value)); @@ -228,9 +225,9 @@ export function XYChart({ axisSeries .map( (series) => - filteredLayers[series.layer].table.columns.find( - (column) => column.id === series.accessor - )?.name + filteredLayers + .find(({ layerId }) => series.layer === layerId) + ?.table.columns.find((column) => column.id === series.accessor)?.name ) .filter((name) => Boolean(name))[0] ); @@ -317,11 +314,13 @@ export function XYChart({ min, max, includeDataFromIds: referenceLineLayers - .flatMap((l, index) => (l.yConfig ? l.yConfig.map((yConfig) => ({ index, yConfig })) : [])) + .flatMap((l) => + l.yConfig ? l.yConfig.map((yConfig) => ({ layerId: l.layerId, yConfig })) : [] + ) .filter(({ yConfig }) => yConfig.axisMode === axis.groupId) .map( - ({ index, yConfig }) => - `${index}-${yConfig.forAccessor}-${yConfig.fill !== 'none' ? 'rect' : 'line'}` + ({ layerId, yConfig }) => + `${layerId}-${yConfig.forAccessor}-${yConfig.fill !== 'none' ? 'rect' : 'line'}` ), }; }; @@ -355,13 +354,13 @@ export function XYChart({ const xColumn = table.columns.find((col) => col.id === layer.xAccessor); const currentXFormatter = - layer.xAccessor && areLayersAlreadyFormatted[layerIndex]?.[layer.xAccessor] && xColumn + layer.xAccessor && areLayersAlreadyFormatted[layer.layerId]?.[layer.xAccessor] && xColumn ? formatFactory(xColumn.meta.params) : xAxisFormatter; const rowIndex = table.rows.findIndex((row) => { if (layer.xAccessor) { - if (areLayersAlreadyFormatted[layerIndex]?.[layer.xAccessor]) { + if (areLayersAlreadyFormatted[layer.layerId]?.[layer.xAccessor]) { // stringify the value to compare with the chart value return currentXFormatter.convert(row[layer.xAccessor]) === xyGeometry.x; } @@ -386,7 +385,7 @@ export function XYChart({ points.push({ row: table.rows.findIndex((row) => { if (layer.splitAccessor) { - if (areLayersAlreadyFormatted[layerIndex]?.[layer.splitAccessor]) { + if (areLayersAlreadyFormatted[layer.layerId]?.[layer.splitAccessor]) { return splitFormatter.convert(row[layer.splitAccessor]) === pointValue; } return row[layer.splitAccessor] === pointValue; diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/axes_configuration.test.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/axes_configuration.test.ts index 01e72d8870f6a..f3abf76b2d05a 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/axes_configuration.test.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/axes_configuration.test.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { DataLayerConfigResult } from '../../common'; +import { DataLayerConfig } from '../../common'; import { LayerTypes } from '../../common/constants'; import { Datatable } from '@kbn/expressions-plugin/public'; import { getAxesConfiguration } from './axes_configuration'; @@ -220,7 +220,8 @@ describe('axes_configuration', () => { }, }; - const sampleLayer: DataLayerConfigResult = { + const sampleLayer: DataLayerConfig = { + layerId: 'first', type: 'dataLayer', layerType: LayerTypes.DATA, seriesType: 'line', @@ -241,7 +242,7 @@ describe('axes_configuration', () => { expect(groups.length).toEqual(1); expect(groups[0].position).toEqual('left'); expect(groups[0].series[0].accessor).toEqual('yAccessorId'); - expect(groups[0].series[0].layer).toEqual(0); + expect(groups[0].series[0].layer).toEqual('first'); }); it('should map auto series to right axis if formatters do not match', () => { @@ -285,7 +286,7 @@ describe('axes_configuration', () => { expect(groups.length).toEqual(1); expect(groups[0].position).toEqual('right'); expect(groups[0].series[0].accessor).toEqual('yAccessorId'); - expect(groups[0].series[0].layer).toEqual(0); + expect(groups[0].series[0].layer).toEqual('first'); }); it('should map series with matching formatters to same axis', () => { diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/axes_configuration.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/axes_configuration.ts index 3de817d5505a5..ea1b7d09709d6 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/axes_configuration.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/axes_configuration.ts @@ -9,15 +9,15 @@ import type { IFieldFormat, SerializedFieldFormat } from '@kbn/field-formats-plugin/common'; import { FormatFactory } from '../types'; import { - CommonXYDataLayerConfigResult, - CommonXYReferenceLineLayerConfigResult, + CommonXYDataLayerConfig, + CommonXYReferenceLineLayerConfig, ExtendedYConfig, YConfig, } from '../../common'; import { isDataLayer } from './visualization'; export interface Series { - layer: number; + layer: string; accessor: string; } @@ -40,7 +40,7 @@ export function isFormatterCompatible( } export function groupAxesByType( - layers: Array + layers: Array ) { const series: { auto: FormattedMetric[]; @@ -54,7 +54,7 @@ export function groupAxesByType( bottom: [], }; - layers.forEach((layer, index) => { + layers.forEach((layer) => { const { table } = layer; layer.accessors.forEach((accessor) => { const yConfig: Array | undefined = layer.yConfig; @@ -75,7 +75,7 @@ export function groupAxesByType( }; } series[mode].push({ - layer: index, + layer: layer.layerId, accessor, fieldFormat: formatter, }); @@ -111,7 +111,7 @@ export function groupAxesByType( } export function getAxesConfiguration( - layers: Array, + layers: Array, shouldRotate: boolean, formatFactory?: FormatFactory ): GroupsConfiguration { diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.test.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.test.ts index 704406b857331..8b1bdeeadb834 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.test.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.test.ts @@ -7,7 +7,7 @@ */ import { getColorAssignments } from './color_assignment'; -import type { DataLayerConfigResult } from '../../common'; +import type { DataLayerConfig } from '../../common'; import type { FormatFactory } from '../types'; import { LayerTypes } from '../../common/constants'; import { Datatable } from '@kbn/expressions-plugin'; @@ -48,8 +48,9 @@ describe('color_assignment', () => { }, }; - const layers: DataLayerConfigResult[] = [ + const layers: DataLayerConfig[] = [ { + layerId: 'first', type: 'dataLayer', yScaleType: 'linear', xScaleType: 'linear', @@ -62,6 +63,7 @@ describe('color_assignment', () => { table: tables['1'], }, { + layerId: 'second', type: 'dataLayer', yScaleType: 'linear', xScaleType: 'linear', @@ -155,18 +157,18 @@ describe('color_assignment', () => { it('should return the correct rank for a series key', () => { const assignments = getColorAssignments(layers, formatFactory); // 3 series in front of 2/y2 - 1/y1, 1/y2 and 2/y1 - expect(assignments.palette1.getRank(layers[0], 0, '2', 'y2')).toEqual(3); + expect(assignments.palette1.getRank(layers[0], '2', 'y2')).toEqual(3); // 1 series in front of 1/y4 - 1/y3 - expect(assignments.palette2.getRank(layers[1], 1, '1', 'y4')).toEqual(1); + expect(assignments.palette2.getRank(layers[1], '1', 'y4')).toEqual(1); }); it('should return the correct rank for a series key spanning multiple layers', () => { const newLayers = [layers[0], { ...layers[1], palette: layers[0].palette }]; const assignments = getColorAssignments(newLayers, formatFactory); // 3 series in front of 2/y2 - 1/y1, 1/y2 and 2/y1 - expect(assignments.palette1.getRank(newLayers[0], 0, '2', 'y2')).toEqual(3); + expect(assignments.palette1.getRank(newLayers[0], '2', 'y2')).toEqual(3); // 2 series in front for the current layer (1/y3, 1/y4), plus all 6 series from the first layer - expect(assignments.palette1.getRank(newLayers[1], 1, '2', 'y3')).toEqual(8); + expect(assignments.palette1.getRank(newLayers[1], '2', 'y3')).toEqual(8); }); it('should return the correct rank for a series without a split', () => { @@ -176,9 +178,9 @@ describe('color_assignment', () => { ]; const assignments = getColorAssignments(newLayers, formatFactory); // 3 series in front of 2/y2 - 1/y1, 1/y2 and 2/y1 - expect(assignments.palette1.getRank(newLayers[0], 0, '2', 'y2')).toEqual(3); + expect(assignments.palette1.getRank(newLayers[0], '2', 'y2')).toEqual(3); // 1 series in front for the current layer (y3), plus all 6 series from the first layer - expect(assignments.palette1.getRank(newLayers[1], 1, 'Metric y4', 'y4')).toEqual(7); + expect(assignments.palette1.getRank(newLayers[1], 'Metric y4', 'y4')).toEqual(7); }); it('should return the correct rank for a series with a non-primitive value', () => { @@ -198,7 +200,7 @@ describe('color_assignment', () => { } as unknown)) as FormatFactory ); // 3 series in front of (complex object)/y1 - abc/y1, abc/y2 - expect(assignments.palette1.getRank(layers[0], 0, 'formatted', 'y1')).toEqual(2); + expect(assignments.palette1.getRank(layers[0], 'formatted', 'y1')).toEqual(2); }); it('should handle missing tables', () => { @@ -207,7 +209,7 @@ describe('color_assignment', () => { formatFactory ); // if there is no data, assume it is the first splitted series. One series in front - 0/y1 - expect(assignments.palette1.getRank(layers[0], 0, '2', 'y2')).toEqual(1); + expect(assignments.palette1.getRank(layers[0], '2', 'y2')).toEqual(1); }); it('should handle missing columns', () => { @@ -216,7 +218,7 @@ describe('color_assignment', () => { const assignments = getColorAssignments(newLayers, formatFactory); // if the split column is missing, assume it is the first splitted series. One series in front - 0/y1 - expect(assignments.palette1.getRank(layers[0], 0, '2', 'y2')).toEqual(1); + expect(assignments.palette1.getRank(layers[0], '2', 'y2')).toEqual(1); }); }); }); diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.ts index b0fb3e00a14ad..e94d22471aba9 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.ts @@ -10,7 +10,7 @@ import { uniq, mapValues } from 'lodash'; import { euiLightVars } from '@kbn/ui-theme'; import { FormatFactory } from '../types'; import { isDataLayer } from './visualization'; -import { CommonXYDataLayerConfigResult, CommonXYLayerConfigResult } from '../../common'; +import { CommonXYDataLayerConfig, CommonXYLayerConfig } from '../../common'; const isPrimitive = (value: unknown): boolean => value != null && typeof value !== 'object'; @@ -20,28 +20,17 @@ export type ColorAssignments = Record< string, { totalSeriesCount: number; - getRank( - sortedLayer: CommonXYDataLayerConfigResult, - layerId: number, - seriesKey: string, - yAccessor: string - ): number; + getRank(sortedLayer: CommonXYDataLayerConfig, seriesKey: string, yAccessor: string): number; } >; export function getColorAssignments( - layers: CommonXYLayerConfigResult[], + layers: CommonXYLayerConfig[], formatFactory: FormatFactory ): ColorAssignments { - const layersPerPalette: Record< - string, - Array<{ - index: number; - layer: CommonXYDataLayerConfigResult; - }> - > = {}; + const layersPerPalette: Record = {}; - layers.forEach((layer, index) => { + layers.forEach((layer) => { if (!isDataLayer(layer)) { return; } @@ -50,11 +39,11 @@ export function getColorAssignments( if (!layersPerPalette[palette]) { layersPerPalette[palette] = []; } - layersPerPalette[palette].push({ layer, index }); + layersPerPalette[palette].push(layer); }); return mapValues(layersPerPalette, (paletteLayers) => { - const seriesPerLayer = paletteLayers.map(({ layer }) => { + const seriesPerLayer = paletteLayers.map((layer) => { if (!layer.splitAccessor) { return { numberOfSeries: layer.accessors.length, splits: [] }; } @@ -83,13 +72,10 @@ export function getColorAssignments( ); return { totalSeriesCount, - getRank( - sortedLayer: CommonXYDataLayerConfigResult, - layerId: number, - seriesKey: string, - yAccessor: string - ) { - const layerIndex = paletteLayers.findIndex(({ index }) => layerId === index); + getRank(sortedLayer: CommonXYDataLayerConfig, seriesKey: string, yAccessor: string) { + const layerIndex = paletteLayers.findIndex( + (layer) => sortedLayer.layerId === layer.layerId + ); const currentSeriesPerLayer = seriesPerLayer[layerIndex]; const splitRank = currentSeriesPerLayer.splits.indexOf(seriesKey); return ( diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/data_layers.tsx b/src/plugins/chart_expressions/expression_xy/public/helpers/data_layers.tsx index 709ab4b2370d4..1fbbbfb19f4ce 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/data_layers.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/data_layers.tsx @@ -24,7 +24,7 @@ import { } from '@kbn/field-formats-plugin/common'; import { Datatable, DatatableRow } from '@kbn/expressions-plugin'; import { PaletteRegistry, SeriesLayer } from '@kbn/coloring'; -import { CommonXYDataLayerConfigResult, XScaleType } from '../../common'; +import { CommonXYDataLayerConfig, XScaleType } from '../../common'; import { FormatFactory } from '../types'; import { getSeriesColor } from './state'; import { ColorAssignments } from './color_assignment'; @@ -33,8 +33,7 @@ import { GroupsConfiguration } from './axes_configuration'; type SeriesSpec = LineSeriesProps & BarSeriesProps & AreaSeriesProps; type GetSeriesPropsFn = (config: { - layer: CommonXYDataLayerConfigResult; - layerId: number; + layer: CommonXYDataLayerConfig; accessor: string; chartHasMoreThanOneBarSeries?: boolean; formatFactory: FormatFactory; @@ -52,7 +51,7 @@ type GetSeriesPropsFn = (config: { type GetSeriesNameFn = ( data: XYChartSeriesIdentifier, config: { - layer: CommonXYDataLayerConfigResult; + layer: CommonXYDataLayerConfig; splitHint: SerializedFieldFormat | undefined; splitFormatter: FieldFormat; alreadyFormattedColumns: Record; @@ -63,8 +62,7 @@ type GetSeriesNameFn = ( type GetColorFn = ( seriesIdentifier: XYChartSeriesIdentifier, config: { - layer: CommonXYDataLayerConfigResult; - layerId: number; + layer: CommonXYDataLayerConfig; accessor: string; colorAssignments: ColorAssignments; columnToLabelMap: Record; @@ -99,7 +97,7 @@ export const getFormattedTable = ( }); export const getIsAlreadyFormattedLayerInfo = ( - { table, xAccessor, xScaleType }: CommonXYDataLayerConfigResult, + { table, xAccessor, xScaleType }: CommonXYDataLayerConfig, formatFactory: FormatFactory ): Record => { const formattedTable = getFormattedTable(table, formatFactory, xAccessor, xScaleType); @@ -118,13 +116,13 @@ export const getIsAlreadyFormattedLayerInfo = ( }; export const getAreAlreadyFormattedLayersInfo = ( - layers: CommonXYDataLayerConfigResult[], + layers: CommonXYDataLayerConfig[], formatFactory: FormatFactory -): Record> => - layers.reduce>>( - (areAlreadyFormatted, layer, index) => ({ +): Record> => + layers.reduce>>( + (areAlreadyFormatted, layer) => ({ ...areAlreadyFormatted, - [index]: getIsAlreadyFormattedLayerInfo(layer, formatFactory), + [layer.layerId]: getIsAlreadyFormattedLayerInfo(layer, formatFactory), }), {} ); @@ -172,7 +170,7 @@ const getLineConfig = () => ({ visible: true, stroke: ColorVariant.Series, opaci const getColor: GetColorFn = ( { yAccessor, seriesKeys }, - { layer, layerId, accessor, colorAssignments, columnToLabelMap, paletteService, syncColors } + { layer, accessor, colorAssignments, columnToLabelMap, paletteService, syncColors } ) => { const overwriteColor = getSeriesColor(layer, accessor); if (overwriteColor !== null) { @@ -183,12 +181,7 @@ const getColor: GetColorFn = ( { name: layer.splitAccessor ? String(seriesKeys[0]) : columnToLabelMap[seriesKeys[0]], totalSeriesAtDepth: colorAssignment.totalSeriesCount, - rankAtDepth: colorAssignment.getRank( - layer, - layerId, - String(seriesKeys[0]), - String(yAccessor) - ), + rankAtDepth: colorAssignment.getRank(layer, String(seriesKeys[0]), String(yAccessor)), }, ]; return paletteService.get(layer.palette.name).getCategoricalColor( @@ -205,7 +198,6 @@ const getColor: GetColorFn = ( export const getSeriesProps: GetSeriesPropsFn = ({ layer, - layerId, accessor, chartHasMoreThanOneBarSeries, colorAssignments, @@ -276,7 +268,6 @@ export const getSeriesProps: GetSeriesPropsFn = ({ color: (series) => getColor(series, { layer, - layerId, accessor, colorAssignments, columnToLabelMap, diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/interval.test.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/interval.test.ts index 7234e921789a4..6721c293dbe57 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/interval.test.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/interval.test.ts @@ -6,18 +6,18 @@ * Side Public License, v 1. */ -import { DataLayerConfigResult, XYChartProps } from '../../common'; +import { DataLayerConfig, XYChartProps } from '../../common'; import { sampleArgs } from '../../common/__mocks__'; import { calculateMinInterval } from './interval'; describe('calculateMinInterval', () => { let xyProps: XYChartProps; - let layer: DataLayerConfigResult; + let layer: DataLayerConfig; beforeEach(() => { const { layers, ...restArgs } = sampleArgs().args; xyProps = { args: { ...restArgs, layers } }; - layer = xyProps.args.layers[0] as DataLayerConfigResult; + layer = xyProps.args.layers[0] as DataLayerConfig; layer.xScaleType = 'time'; }); it('should use first valid layer and determine interval', async () => { diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/layers.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/layers.ts index 4e11c7e52543d..4408ebd3feb84 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/layers.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/layers.ts @@ -8,15 +8,15 @@ import { Datatable } from '@kbn/expressions-plugin/common'; import { - CommonXYDataLayerConfigResult, - CommonXYLayerConfigResult, - CommonXYReferenceLineLayerConfigResult, -} from '../../common'; + CommonXYDataLayerConfig, + CommonXYLayerConfig, + CommonXYReferenceLineLayerConfig, +} from '../../common/types'; import { isDataLayer, isReferenceLayer } from './visualization'; -export function getFilteredLayers(layers: CommonXYLayerConfigResult[]) { - return layers.filter( - (layer): layer is CommonXYReferenceLineLayerConfigResult | CommonXYDataLayerConfigResult => { +export function getFilteredLayers(layers: CommonXYLayerConfig[]) { + return layers.filter( + (layer): layer is CommonXYReferenceLineLayerConfig | CommonXYDataLayerConfig => { let table: Datatable | undefined; let accessors: string[] = []; let xAccessor: undefined | string | number; diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/state.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/state.ts index 23a8ddfc49f13..e2f95491dbce8 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/state.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/state.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import type { CommonXYLayerConfigResult, SeriesType, ExtendedYConfig, YConfig } from '../../common'; +import type { CommonXYLayerConfig, SeriesType, ExtendedYConfig, YConfig } from '../../common'; import { getDataLayers, isAnnotationsLayer, isDataLayer } from './visualization'; export function isHorizontalSeries(seriesType: SeriesType) { @@ -21,11 +21,11 @@ export function isStackedChart(seriesType: SeriesType) { return seriesType.includes('stacked'); } -export function isHorizontalChart(layers: CommonXYLayerConfigResult[]) { +export function isHorizontalChart(layers: CommonXYLayerConfig[]) { return getDataLayers(layers).every((l) => isHorizontalSeries(l.seriesType)); } -export const getSeriesColor = (layer: CommonXYLayerConfigResult, accessor: string) => { +export const getSeriesColor = (layer: CommonXYLayerConfig, accessor: string) => { if ((isDataLayer(layer) && layer.splitAccessor) || isAnnotationsLayer(layer)) { return null; } diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/visualization.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/visualization.ts index 9f3e4246bda9f..db0b431d56fac 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/visualization.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/visualization.ts @@ -8,40 +8,38 @@ import { LayerTypes } from '../../common/constants'; import { - CommonXYDataLayerConfigResult, - CommonXYLayerConfigResult, - CommonXYAnnotationLayerConfigResult, - CommonXYReferenceLineLayerConfigResult, -} from '../../common'; - -export const isDataLayer = ( - layer: CommonXYLayerConfigResult -): layer is CommonXYDataLayerConfigResult => + CommonXYLayerConfig, + CommonXYDataLayerConfig, + CommonXYReferenceLineLayerConfig, + CommonXYAnnotationLayerConfig, +} from '../../common/types'; + +export const isDataLayer = (layer: CommonXYLayerConfig): layer is CommonXYDataLayerConfig => layer.layerType === LayerTypes.DATA || !layer.layerType; -export const getDataLayers = (layers: CommonXYLayerConfigResult[]) => - (layers || []).filter((layer): layer is CommonXYDataLayerConfigResult => isDataLayer(layer)); +export const getDataLayers = (layers: CommonXYLayerConfig[]) => + (layers || []).filter((layer): layer is CommonXYDataLayerConfig => isDataLayer(layer)); export const isReferenceLayer = ( - layer: CommonXYLayerConfigResult -): layer is CommonXYReferenceLineLayerConfigResult => layer.layerType === LayerTypes.REFERENCELINE; + layer: CommonXYLayerConfig +): layer is CommonXYReferenceLineLayerConfig => layer.layerType === LayerTypes.REFERENCELINE; -export const getReferenceLayers = (layers: CommonXYLayerConfigResult[]) => - (layers || []).filter((layer): layer is CommonXYReferenceLineLayerConfigResult => +export const getReferenceLayers = (layers: CommonXYLayerConfig[]) => + (layers || []).filter((layer): layer is CommonXYReferenceLineLayerConfig => isReferenceLayer(layer) ); const isAnnotationLayerCommon = ( - layer: CommonXYLayerConfigResult -): layer is CommonXYAnnotationLayerConfigResult => layer.layerType === LayerTypes.ANNOTATIONS; + layer: CommonXYLayerConfig +): layer is CommonXYAnnotationLayerConfig => layer.layerType === LayerTypes.ANNOTATIONS; export const isAnnotationsLayer = ( - layer: CommonXYLayerConfigResult -): layer is CommonXYAnnotationLayerConfigResult => isAnnotationLayerCommon(layer); + layer: CommonXYLayerConfig +): layer is CommonXYAnnotationLayerConfig => isAnnotationLayerCommon(layer); export const getAnnotationsLayers = ( - layers: CommonXYLayerConfigResult[] -): CommonXYAnnotationLayerConfigResult[] => - (layers || []).filter((layer): layer is CommonXYAnnotationLayerConfigResult => + layers: CommonXYLayerConfig[] +): CommonXYAnnotationLayerConfig[] => + (layers || []).filter((layer): layer is CommonXYAnnotationLayerConfig => isAnnotationsLayer(layer) ); diff --git a/src/plugins/chart_expressions/expression_xy/public/plugin.ts b/src/plugins/chart_expressions/expression_xy/public/plugin.ts index ee78400e35831..5e68d2c621894 100755 --- a/src/plugins/chart_expressions/expression_xy/public/plugin.ts +++ b/src/plugins/chart_expressions/expression_xy/public/plugin.ts @@ -30,6 +30,7 @@ import { annotationLayerFunction, labelsOrientationConfigFunction, axisTitlesVisibilityConfigFunction, + extendedAnnotationLayerFunction, } from '../common/expression_functions'; import { GetStartDepsFn, getXyChartRenderer } from './expression_renderers'; @@ -63,6 +64,7 @@ export class ExpressionXyPlugin { expressions.registerFunction(axisExtentConfigFunction); expressions.registerFunction(tickLabelsConfigFunction); expressions.registerFunction(annotationLayerFunction); + expressions.registerFunction(extendedAnnotationLayerFunction); expressions.registerFunction(labelsOrientationConfigFunction); expressions.registerFunction(referenceLineLayerFunction); expressions.registerFunction(extendedReferenceLineLayerFunction); diff --git a/src/plugins/chart_expressions/expression_xy/server/plugin.ts b/src/plugins/chart_expressions/expression_xy/server/plugin.ts index 9350279f18d03..37252a7296580 100755 --- a/src/plugins/chart_expressions/expression_xy/server/plugin.ts +++ b/src/plugins/chart_expressions/expression_xy/server/plugin.ts @@ -25,6 +25,7 @@ import { extendedDataLayerFunction, extendedReferenceLineLayerFunction, layeredXyVisFunction, + extendedAnnotationLayerFunction, } from '../common/expression_functions'; import { SetupDeps } from './types'; @@ -41,6 +42,7 @@ export class ExpressionXyPlugin expressions.registerFunction(axisExtentConfigFunction); expressions.registerFunction(tickLabelsConfigFunction); expressions.registerFunction(annotationLayerFunction); + expressions.registerFunction(extendedAnnotationLayerFunction); expressions.registerFunction(labelsOrientationConfigFunction); expressions.registerFunction(referenceLineLayerFunction); expressions.registerFunction(extendedReferenceLineLayerFunction); diff --git a/x-pack/plugins/lens/public/index.ts b/x-pack/plugins/lens/public/index.ts index 2ba1220156a39..b8d00e7ff61b8 100644 --- a/x-pack/plugins/lens/public/index.ts +++ b/x-pack/plugins/lens/public/index.ts @@ -90,18 +90,18 @@ export type { LensMultiTable, ValueLabelMode, AxisExtentMode, + DataLayerConfig, FittingFunction, AxisExtentConfig, LegendConfigResult, AxesSettingsConfig, GridlinesConfigResult, - DataLayerConfigResult, TickLabelsConfigResult, AxisExtentConfigResult, ReferenceLineLayerArgs, LabelsOrientationConfig, + ReferenceLineLayerConfig, LabelsOrientationConfigResult, - ReferenceLineLayerConfigResult, AxisTitlesVisibilityConfigResult, } from '@kbn/expression-xy-plugin/common'; export type { LensEmbeddableInput } from './embeddable'; diff --git a/x-pack/plugins/lens/public/xy_visualization/__snapshots__/to_expression.test.ts.snap b/x-pack/plugins/lens/public/xy_visualization/__snapshots__/to_expression.test.ts.snap index 0743946c4b9e0..9c4ee0d3b245f 100644 --- a/x-pack/plugins/lens/public/xy_visualization/__snapshots__/to_expression.test.ts.snap +++ b/x-pack/plugins/lens/public/xy_visualization/__snapshots__/to_expression.test.ts.snap @@ -107,6 +107,9 @@ Object { "isHistogram": Array [ false, ], + "layerId": Array [ + "first", + ], "palette": Array [ Object { "chain": Array [ diff --git a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts index 0451140e1be19..4b4546ae839ec 100644 --- a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts +++ b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts @@ -407,6 +407,7 @@ const referenceLineLayerToExpression = ( type: 'function', function: 'extendedReferenceLineLayer', arguments: { + layerId: [layer.layerId], yConfig: layer.yConfig ? layer.yConfig.map((yConfig) => extendedYConfigToExpression(yConfig, defaultReferenceLineColor) @@ -430,9 +431,10 @@ const annotationLayerToExpression = ( chain: [ { type: 'function', - function: 'annotationLayer', + function: 'extendedAnnotationLayer', arguments: { hide: [Boolean(layer.hide)], + layerId: [layer.layerId], annotations: layer.annotations ? layer.annotations.map( (ann): Ast => @@ -479,6 +481,7 @@ const dataLayerToExpression = ( type: 'function', function: 'extendedDataLayer', arguments: { + layerId: [layer.layerId], hide: [Boolean(layer.hide)], xAccessor: layer.xAccessor ? [layer.xAccessor] : [], yScaleType: [ From 40ba7c0f73ea8640ee041b7e8c82eecccef83347 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Wed, 20 Apr 2022 17:15:37 +0300 Subject: [PATCH 127/133] Removed convertActiveData from Lens. --- .../editor_frame/config_panel/layer_panel.tsx | 10 +- .../config_panel/layer_settings.tsx | 5 +- .../workspace_panel_wrapper.tsx | 2 +- .../lens/public/embeddable/embeddable.tsx | 9 -- x-pack/plugins/lens/public/types.ts | 9 -- .../xy_visualization/visualization.test.ts | 4 +- .../public/xy_visualization/visualization.tsx | 101 ++++-------------- .../visualization_helpers.test.tsx | 75 ------------- .../visualization_helpers.tsx | 40 ------- 9 files changed, 27 insertions(+), 228 deletions(-) delete mode 100644 x-pack/plugins/lens/public/xy_visualization/visualization_helpers.test.tsx 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 620eda0e80907..c577bf89d6bd1 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 @@ -357,7 +357,7 @@ export function LayerPanel( <> {layerDatasource ? ( {activeGroup && activeId && layerDatasource && ( + ); } diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.tsx index c17e90b48e811..d476a8689f06b 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.tsx @@ -127,7 +127,7 @@ export function WorkspacePanelWrapper({ {activeVisualization && activeVisualization.renderToolbar && ( { this.activeDataInfo.activeData = adapters?.tables?.tables; - if (this.savedVis?.visualizationType) { - const { activeData } = this.activeDataInfo; - this.activeDataInfo.activeData = - this.deps.visualizationMap[this.savedVis.visualizationType].convertActiveData?.( - activeData, - this.savedVis.state.visualization - ) ?? activeData; - } - if (this.input.onLoad) { // once onData$ is get's called from expression renderer, loading becomes false this.input.onLoad(false); diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index 6f12bf072a2b1..0d60db866a2c1 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -928,15 +928,6 @@ export interface Visualization { * functions and datasource expressions will not be appended to the expression automatically. */ shouldBuildDatasourceExpressionManually?: () => boolean; - - /** - * Converts `activeData`, came from expressions in the form of hashmap as `{ [index]: table, ...}`, to the hashmap of - * layer ids and tables as `{ [layerId]: table }`. - */ - convertActiveData?: ( - activeData?: FramePublicAPI['activeData'], - state?: T - ) => FramePublicAPI['activeData']; } export interface LensFilterEvent { diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts b/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts index a2262a72b8dbc..e1c63e4bb54d9 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts @@ -1493,7 +1493,7 @@ describe('xy_visualization', () => { it('differ vertical axis if the formatters are not compatibles between each other', () => { const tables: Record = { - 0: { + first: { type: 'datatable', rows: [], columns: [ @@ -2186,7 +2186,7 @@ describe('xy_visualization', () => { }; frame.activeData = { - 0: { + first: { type: 'datatable', columns: [ { id: 'a', name: 'A', meta: { type: 'number' } }, diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx index fc63030163544..97bac36a93465 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx @@ -26,14 +26,7 @@ import { getSuggestions } from './xy_suggestions'; import { XyToolbar } from './xy_config_panel'; import { DimensionEditor } from './xy_config_panel/dimension_editor'; import { LayerHeader } from './xy_config_panel/layer_header'; -import type { - Visualization, - AccessorConfig, - FramePublicAPI, - VisualizationDimensionChangeProps, - VisualizationConfigProps, - VisualizationToolbarProps, -} from '../types'; +import type { Visualization, AccessorConfig, FramePublicAPI } from '../types'; import { State, visualizationTypes, XYSuggestion, XYLayerConfig, XYDataLayerConfig } from './types'; import { layerTypes } from '../../common'; import { isHorizontalChart } from './state_helpers'; @@ -54,7 +47,6 @@ import { } from './annotations/helpers'; import { checkXAccessorCompatibility, - convertActiveDataFromIndexesToLayers, defaultSeriesType, getAxisName, getDataLayers, @@ -79,46 +71,6 @@ import { AnnotationsPanel } from './xy_config_panel/annotations_config_panel'; import { DimensionTrigger } from '../shared_components/dimension_trigger'; import { defaultAnnotationLabel } from './annotations/helpers'; -type ConvertActiveDataFn = ( - activeData?: FramePublicAPI['activeData'], - state?: State -) => FramePublicAPI['activeData']; - -const updateFrame = ( - state: State | undefined, - frame: Pick, - convertActiveData?: ConvertActiveDataFn -) => { - if (!frame) { - return frame; - } - - const activeData = convertActiveData?.(frame?.activeData, state) ?? frame?.activeData; - return Object.assign(frame, { activeData }); -}; - -const isVisualizationDimensionChangeProps = ( - props: - | VisualizationConfigProps - | VisualizationDimensionChangeProps - | VisualizationToolbarProps -): props is VisualizationDimensionChangeProps => { - if ((props as VisualizationDimensionChangeProps).prevState) { - return true; - } - return false; -}; - -function updateProps< - T extends - | VisualizationConfigProps - | VisualizationDimensionChangeProps - | VisualizationToolbarProps ->(props: T, convertActiveData?: ConvertActiveDataFn) { - const state = isVisualizationDimensionChangeProps(props) ? props.prevState : props.state; - return { ...props, frame: updateFrame(state, props.frame), convertActiveData }; -} - export const getXyVisualization = ({ paletteService, fieldFormats, @@ -221,36 +173,34 @@ export const getXyVisualization = ({ }, getSupportedLayers(state, frame) { - const newFrame = frame ? updateFrame(state, frame, this.convertActiveData) : frame; return [ supportedDataLayer, - getAnnotationsSupportedLayer(state, newFrame), - getReferenceSupportedLayer(state, newFrame), + getAnnotationsSupportedLayer(state, frame), + getReferenceSupportedLayer(state, frame), ]; }, getConfiguration({ state, frame, layerId }) { - const newFrame = updateFrame(state, frame, this.convertActiveData); const layer = state.layers.find((l) => l.layerId === layerId); if (!layer) { return { groups: [] }; } if (isAnnotationsLayer(layer)) { - return getAnnotationsConfiguration({ state, frame: newFrame, layer }); + return getAnnotationsConfiguration({ state, frame, layer }); } const sortedAccessors: string[] = getSortedAccessors( - newFrame.datasourceLayers[layer.layerId], + frame.datasourceLayers[layer.layerId], layer ); if (isReferenceLayer(layer)) { - return getReferenceConfiguration({ state, frame: newFrame, layer, sortedAccessors }); + return getReferenceConfiguration({ state, frame, layer, sortedAccessors }); } const mappedAccessors = getMappedAccessors({ state, - frame: newFrame, + frame, layer, fieldFormats, paletteService, @@ -258,14 +208,14 @@ export const getXyVisualization = ({ }); if (isReferenceLayer(layer)) { - return getReferenceConfiguration({ state, frame: newFrame, layer, sortedAccessors }); + return getReferenceConfiguration({ state, frame, layer, sortedAccessors }); } const dataLayer: XYDataLayerConfig = layer; const dataLayers = getDataLayers(state.layers); const isHorizontal = isHorizontalChart(state.layers); - const { left, right } = groupAxesByType([layer], newFrame.activeData); + const { left, right } = groupAxesByType([layer], frame.activeData); // Check locally if it has one accessor OR one accessor per axis const layerHasOnlyOneAccessor = Boolean( dataLayer.accessors.length < 2 || @@ -285,10 +235,7 @@ export const getXyVisualization = ({ Boolean(l.xAccessor) === Boolean(dataLayer.xAccessor) && Boolean(l.splitAccessor) === Boolean(dataLayer.splitAccessor) ) { - const { left: localLeft, right: localRight } = groupAxesByType( - [l], - newFrame.activeData - ); + const { left: localLeft, right: localRight } = groupAxesByType([l], frame.activeData); // return true only if matching axis are found return ( l.accessors.length && @@ -352,8 +299,7 @@ export const getXyVisualization = ({ }, setDimension(props) { - const newProps = updateProps(props, this.convertActiveData); - const { prevState, layerId, columnId, groupId } = newProps; + const { prevState, layerId, columnId, groupId } = props; const foundLayer: XYLayerConfig | undefined = prevState.layers.find( (l) => l.layerId === layerId @@ -363,10 +309,10 @@ export const getXyVisualization = ({ } if (isReferenceLayer(foundLayer)) { - return setReferenceDimension(newProps); + return setReferenceDimension(props); } if (isAnnotationsLayer(foundLayer)) { - return setAnnotationsDimension(newProps); + return setAnnotationsDimension(props); } const newLayer: XYDataLayerConfig = Object.assign({}, foundLayer); @@ -466,7 +412,6 @@ export const getXyVisualization = ({ }, removeDimension({ prevState, layerId, columnId, frame }) { - const newFrame = updateFrame(prevState, frame, this.convertActiveData); const foundLayer = prevState.layers.find((l) => l.layerId === layerId); if (!foundLayer) { return prevState; @@ -504,8 +449,8 @@ export const getXyVisualization = ({ // check for data layers if they all still have xAccessors const groupsAvailable = getGroupsAvailableInData( getDataLayers(prevState.layers), - newFrame.datasourceLayers, - newFrame.activeData + frame.datasourceLayers, + frame.activeData ); if ( @@ -525,11 +470,10 @@ export const getXyVisualization = ({ }, renderLayerHeader(domElement, props) { - const newProps = updateProps(props, this.convertActiveData); render( - + , domElement @@ -537,11 +481,10 @@ export const getXyVisualization = ({ }, renderToolbar(domElement, props) { - const newProps = updateProps(props, this.convertActiveData); render( - + , domElement @@ -549,10 +492,8 @@ export const getXyVisualization = ({ }, renderDimensionEditor(domElement, props) { - const newProps = updateProps(props, this.convertActiveData); - const allProps = { - ...newProps, + ...props, formatFactory: fieldFormats.deserialize, paletteService, }; @@ -575,9 +516,6 @@ export const getXyVisualization = ({ shouldBuildDatasourceExpressionManually: () => true, - convertActiveData: (activeData, state) => - convertActiveDataFromIndexesToLayers(activeData, state?.layers), - toExpression: (state, layers, attributes, datasourceExpressionsByLayers = {}) => toExpression( state, @@ -671,7 +609,6 @@ export const getXyVisualization = ({ if (state?.layers.length === 0 || !frame.activeData) { return; } - const newFrame = updateFrame(state, frame, this.convertActiveData); const filteredLayers = [ ...getDataLayers(state.layers), @@ -682,7 +619,7 @@ export const getXyVisualization = ({ for (const layer of filteredLayers) { const { layerId, accessors } = layer; - const rows = newFrame.activeData?.[layerId] && newFrame.activeData[layerId].rows; + const rows = frame.activeData?.[layerId] && frame.activeData[layerId].rows; if (!rows) { break; } diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization_helpers.test.tsx b/x-pack/plugins/lens/public/xy_visualization/visualization_helpers.test.tsx deleted file mode 100644 index a57a4397e4366..0000000000000 --- a/x-pack/plugins/lens/public/xy_visualization/visualization_helpers.test.tsx +++ /dev/null @@ -1,75 +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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { Datatable } from '@kbn/expressions-plugin/common'; -import { XYDataLayerConfig, XYState } from './types'; -import { convertActiveDataFromIndexesToLayers } from './visualization_helpers'; - -const generateDatatable = (columnName: string): Datatable => ({ - type: 'datatable', - columns: [{ id: columnName, name: columnName, meta: { type: 'number' } }], - rows: [], -}); - -describe('#convertActiveDataFromIndexesToLayers', () => { - const partialLayer: Omit = { - layerType: 'data', - accessors: [], - seriesType: 'area', - }; - - const datatable1: Datatable = generateDatatable('first'); - const datatable2: Datatable = generateDatatable('second'); - const datatable3: Datatable = generateDatatable('third'); - const datatable4: Datatable = generateDatatable('fourth'); - const datatable5: Datatable = generateDatatable('fifth'); - - const activeData = { - 0: datatable1, - 1: datatable2, - 2: datatable3, - 3: datatable4, - }; - - const layers: XYState['layers'] = [ - { layerId: 'id1', ...partialLayer }, - { layerId: 'id2', ...partialLayer }, - { layerId: 'id3', ...partialLayer }, - { layerId: 'id4', ...partialLayer }, - ]; - - it('should convert activeData indexes to layerIds', () => { - const result = convertActiveDataFromIndexesToLayers(activeData, layers); - expect(result).toStrictEqual({ - id1: datatable1, - id2: datatable2, - id3: datatable3, - id4: datatable4, - }); - }); - - it('should not remap layerIds from activeData', () => { - const result = convertActiveDataFromIndexesToLayers({ ...activeData, id0: datatable5 }, layers); - expect(result).toStrictEqual({ - id1: datatable1, - id2: datatable2, - id3: datatable3, - id4: datatable4, - id0: datatable5, - }); - }); - - it('should return undefined if activeData is empty', () => { - const result = convertActiveDataFromIndexesToLayers({}, layers); - expect(result).toBeUndefined(); - }); - - it('should skip if no activeData is passed', () => { - const result = convertActiveDataFromIndexesToLayers(undefined, []); - expect(result).toBeUndefined(); - }); -}); diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization_helpers.tsx b/x-pack/plugins/lens/public/xy_visualization/visualization_helpers.tsx index 87ba94408f075..d390d081258a5 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization_helpers.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/visualization_helpers.tsx @@ -8,7 +8,6 @@ import { i18n } from '@kbn/i18n'; import { uniq } from 'lodash'; import { SeriesType } from '@kbn/expression-xy-plugin/common'; -import { Datatable } from '@kbn/expressions-plugin/common'; import { DatasourceLayers, OperationMetadata, VisualizationType } from '../types'; import { State, @@ -338,42 +337,3 @@ export const isNumericMetric = (op: OperationMetadata) => export const isNumericDynamicMetric = (op: OperationMetadata) => isNumericMetric(op) && !op.isStaticValue; export const isBucketed = (op: OperationMetadata) => op.isBucketed; - -/** - * Converts hashmap of tables, stored by layers' indexes - * (created at `layeredXyVis` expression function), to hashmap of tables, stored by layers' ids. Before, - * layers, passed to `xy` expression function contained layerIds. But it is impossible to continue using - * this approach any more, as far as the idea of multitable is going to be deprecated. - * @param activeData hashmap of tables, containing requested data. - * @param layers array of data visualization configuration. Each layer has its own table at the `activeData`. - * @returns new hashmap of tables, where all the tables are mapped by layerId. - */ -export const convertActiveDataFromIndexesToLayers = ( - activeData: Record | undefined, - layers: XYState['layers'] = [] -): Record | undefined => { - if (!activeData) { - return activeData; - } - - const indexesToLayerIds = layers.reduce>( - (layersWithIndexes, { layerId }, index) => - layerId ? { ...layersWithIndexes, [index]: layerId } : layersWithIndexes, - {} - ); - - const convertedActiveData = Object.entries(activeData).reduce< - Record - >((dataByLayerIds, [layerIndex, dataPerLayer]) => { - // if layer index doesn't exist at the map of layer index, it means, that is - // a layerId and should be mapped without conveting from index to layerId. - const index = Number(layerIndex); - const layerId = isNaN(index) ? layerIndex : indexesToLayerIds[index] ?? layerIndex; - return { - ...dataByLayerIds, - [layerId]: dataPerLayer, - }; - }, {}); - - return Object.keys(convertedActiveData).length ? convertedActiveData : undefined; -}; From 7cd49d0ec8fd135d02d5bc41cce65f69f6877ade Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Wed, 20 Apr 2022 17:51:09 +0300 Subject: [PATCH 128/133] Added test to the layerIds generator. --- .../common/helpers/layers.test.ts | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 src/plugins/chart_expressions/expression_xy/common/helpers/layers.test.ts diff --git a/src/plugins/chart_expressions/expression_xy/common/helpers/layers.test.ts b/src/plugins/chart_expressions/expression_xy/common/helpers/layers.test.ts new file mode 100644 index 0000000000000..ac44ef18fc505 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/helpers/layers.test.ts @@ -0,0 +1,49 @@ +/* + * 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 { generateLayerId, appendLayerIds } from './layers'; + +describe('#generateLayerId', () => { + it('should return the combination of keyword and index', () => { + const key = 'some-key'; + const index = 10; + const id = generateLayerId(key, index); + expect(id).toBe(`${key}-${index}`); + }); +}); + +describe('#appendLayerIds', () => { + it('should add layerId to each layer', () => { + const layers = [{ name: 'someName' }, { name: 'someName2' }, { name: 'someName3' }]; + const keyword = 'keyword'; + const expectedLayerIds = [ + { ...layers[0], layerId: `${keyword}-0` }, + { ...layers[1], layerId: `${keyword}-1` }, + { ...layers[2], layerId: `${keyword}-2` }, + ]; + + const layersWithIds = appendLayerIds(layers, keyword); + expect(layersWithIds).toStrictEqual(expectedLayerIds); + }); + + it('should filter out undefined layers', () => { + const layers = [undefined, undefined, undefined]; + const result = appendLayerIds(layers, 'some-key'); + expect(result).toStrictEqual([]); + + const layers2 = [{ name: 'someName' }, undefined, { name: 'someName3' }]; + const keyword = 'keyword'; + const expectedLayerIds = [ + { ...layers2[0], layerId: `${keyword}-0` }, + { ...layers2[2], layerId: `${keyword}-1` }, + ]; + + const layersWithIds = appendLayerIds(layers2, keyword); + expect(layersWithIds).toStrictEqual(expectedLayerIds); + }); +}); From 128643d0421c0886633a217df50e2f923e814329 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Thu, 21 Apr 2022 13:33:14 +0300 Subject: [PATCH 129/133] Fixed types. --- .../common/expression_functions/common_data_layer_args.ts | 2 +- .../expression_functions/common_reference_line_layer_args.ts | 2 +- .../common/expression_functions/common_xy_args.ts | 5 +---- .../common/expression_functions/common_y_config_args.ts | 5 +---- 4 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_data_layer_args.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_data_layer_args.ts index 85beee8bfb59f..49446310a894b 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_data_layer_args.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_data_layer_args.ts @@ -12,7 +12,7 @@ import { DataLayerFn, ExtendedDataLayerFn } from '../types'; type CommonDataLayerFn = DataLayerFn | ExtendedDataLayerFn; -export const commonDataLayerArgs: Omit = { +export const commonDataLayerArgs: CommonDataLayerFn['args'] = { hide: { types: ['boolean'], default: false, diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_reference_line_layer_args.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_reference_line_layer_args.ts index 9e538039b4cb7..f338e08a88940 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_reference_line_layer_args.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_reference_line_layer_args.ts @@ -12,7 +12,7 @@ import { ReferenceLineLayerFn, ExtendedReferenceLineLayerFn } from '../types'; type CommonReferenceLineLayerFn = ReferenceLineLayerFn | ExtendedReferenceLineLayerFn; -export const commonReferenceLineLayerArgs: Omit = { +export const commonReferenceLineLayerArgs: CommonReferenceLineLayerFn['args'] = { accessors: { types: ['string'], help: strings.getRLAccessorsHelp(), diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_xy_args.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_xy_args.ts index f2a73a220364d..f80d814571076 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_xy_args.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_xy_args.ts @@ -23,10 +23,7 @@ import { LayeredXyVisFn, XyVisFn } from '../types'; type CommonXYFn = XyVisFn | LayeredXyVisFn; -export const commonXYArgs: Omit< - CommonXYFn['args'], - 'dataLayers' | 'referenceLineLayers' | 'annotationLayers' | 'layers' -> = { +export const commonXYArgs: CommonXYFn['args'] = { xTitle: { types: ['string'], help: strings.getXTitleHelp(), diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_y_config_args.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_y_config_args.ts index 3a90232cc47d3..76ac6ba2a1a97 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_y_config_args.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_y_config_args.ts @@ -12,10 +12,7 @@ import { YConfigFn, ExtendedYConfigFn } from '../types'; type CommonYConfigFn = YConfigFn | ExtendedYConfigFn; -export const commonYConfigArgs: Pick< - CommonYConfigFn['args'], - 'forAccessor' | 'axisMode' | 'color' -> = { +export const commonYConfigArgs: CommonYConfigFn['args'] = { forAccessor: { types: ['string'], help: strings.getForAccessorHelp(), From 8ddc6c45aa3a5a4b59fc6858869b998f51611b53 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Thu, 21 Apr 2022 14:13:57 +0300 Subject: [PATCH 130/133] Updated snapshots. --- .../public/components/__snapshots__/xy_chart.test.tsx.snap | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/plugins/chart_expressions/expression_xy/public/components/__snapshots__/xy_chart.test.tsx.snap b/src/plugins/chart_expressions/expression_xy/public/components/__snapshots__/xy_chart.test.tsx.snap index b0b0e4d6ab4ad..6b4687d92628a 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/__snapshots__/xy_chart.test.tsx.snap +++ b/src/plugins/chart_expressions/expression_xy/public/components/__snapshots__/xy_chart.test.tsx.snap @@ -254,6 +254,7 @@ exports[`XYChart component it renders area 1`] = ` Object { "boundary": undefined, "headerFormatter": [Function], + "type": "vertical", } } /> @@ -655,6 +656,7 @@ exports[`XYChart component it renders bar 1`] = ` Object { "boundary": undefined, "headerFormatter": [Function], + "type": "vertical", } } /> @@ -1056,6 +1058,7 @@ exports[`XYChart component it renders horizontal bar 1`] = ` Object { "boundary": undefined, "headerFormatter": [Function], + "type": "vertical", } } /> @@ -1457,6 +1460,7 @@ exports[`XYChart component it renders line 1`] = ` Object { "boundary": undefined, "headerFormatter": [Function], + "type": "vertical", } } /> @@ -1858,6 +1862,7 @@ exports[`XYChart component it renders stacked area 1`] = ` Object { "boundary": undefined, "headerFormatter": [Function], + "type": "vertical", } } /> @@ -2259,6 +2264,7 @@ exports[`XYChart component it renders stacked bar 1`] = ` Object { "boundary": undefined, "headerFormatter": [Function], + "type": "vertical", } } /> @@ -2660,6 +2666,7 @@ exports[`XYChart component it renders stacked horizontal bar 1`] = ` Object { "boundary": undefined, "headerFormatter": [Function], + "type": "vertical", } } /> From 87e69d33650d0fa25744cbd0bee2635ff7e42a64 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Thu, 26 May 2022 11:36:28 +0300 Subject: [PATCH 131/133] Reverted legacy parts of code. --- .../chart_expressions/expression_xy/common/constants.ts | 1 - .../expression_xy/public/components/xy_chart.test.tsx | 4 ++-- x-pack/plugins/lens/public/xy_visualization/to_expression.ts | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/common/constants.ts b/src/plugins/chart_expressions/expression_xy/common/constants.ts index d8d55874b45dc..fc2e41700b94f 100644 --- a/src/plugins/chart_expressions/expression_xy/common/constants.ts +++ b/src/plugins/chart_expressions/expression_xy/common/constants.ts @@ -22,7 +22,6 @@ export const TICK_LABELS_CONFIG = 'tickLabelsConfig'; export const AXIS_EXTENT_CONFIG = 'axisExtentConfig'; export const REFERENCE_LINE = 'referenceLine'; export const REFERENCE_LINE_LAYER = 'referenceLineLayer'; -export const EXTENDED_REFERENCE_LINE_LAYER = 'extendedReferenceLineLayer'; export const LABELS_ORIENTATION_CONFIG = 'labelsOrientationConfig'; export const AXIS_TITLES_VISIBILITY_CONFIG = 'axisTitlesVisibilityConfig'; diff --git a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx index 4993fcc165203..a4a2eccb29bb2 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx @@ -593,8 +593,8 @@ describe('XYChart component', () => { ); expect(component.find(Axis).find('[id="left"]').prop('domain')).toEqual({ fit: false, - min: 123, - max: 456, + min: NaN, + max: NaN, includeDataFromIds: [], }); }); diff --git a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts index cb6e6cff2d70e..ff5a692a76e96 100644 --- a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts +++ b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts @@ -356,7 +356,7 @@ const referenceLineLayerToExpression = ( chain: [ { type: 'function', - function: 'extendedReferenceLineLayer', + function: 'referenceLineLayer', arguments: { layerId: [layer.layerId], yConfig: layer.yConfig From c17cf37d70e76512336041ecade5d869d5d8cc0b Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Thu, 26 May 2022 11:38:10 +0300 Subject: [PATCH 132/133] Fixed snapshot. --- .../public/components/__snapshots__/xy_chart.test.tsx.snap | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/plugins/chart_expressions/expression_xy/public/components/__snapshots__/xy_chart.test.tsx.snap b/src/plugins/chart_expressions/expression_xy/public/components/__snapshots__/xy_chart.test.tsx.snap index b275988c40230..103c39488da29 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/__snapshots__/xy_chart.test.tsx.snap +++ b/src/plugins/chart_expressions/expression_xy/public/components/__snapshots__/xy_chart.test.tsx.snap @@ -4195,6 +4195,7 @@ exports[`XYChart component split chart should render split chart if both, splitR Object { "boundary": undefined, "headerFormatter": [Function], + "type": "vertical", } } /> @@ -5001,6 +5002,7 @@ exports[`XYChart component split chart should render split chart if splitColumnA Object { "boundary": undefined, "headerFormatter": [Function], + "type": "vertical", } } /> @@ -5806,6 +5808,7 @@ exports[`XYChart component split chart should render split chart if splitRowAcce Object { "boundary": undefined, "headerFormatter": [Function], + "type": "vertical", } } /> From 273d280d5b224f2f78a4106825b4e8592c7bd166 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Thu, 26 May 2022 11:47:36 +0300 Subject: [PATCH 133/133] Fixed types. --- .../common/expression_functions/layered_xy_vis_fn.ts | 1 - .../expression_xy/common/expression_functions/xy_vis_fn.ts | 1 - .../expression_xy/common/types/expression_functions.ts | 6 +++--- .../expression_xy/public/components/xy_chart.test.tsx | 3 +++ 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis_fn.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis_fn.ts index de4f2b89f2807..fb7c91c682847 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis_fn.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis_fn.ts @@ -42,7 +42,6 @@ export const layeredXyVisFn: LayeredXyVisFn['fn'] = async (data, args, handlers) args: { ...args, layers, - showTooltip: args.showTooltip ?? false, markSizeRatio: hasMarkSizeAccessors && !args.markSizeRatio ? 10 : args.markSizeRatio, ariaLabel: args.ariaLabel ?? diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis_fn.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis_fn.ts index a95df8729021d..8e352a6f34b4b 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis_fn.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis_fn.ts @@ -142,7 +142,6 @@ export const xyVisFn: XyVisFn['fn'] = async (data, args, handlers) => { args: { ...restArgs, layers, - showTooltip: args.showTooltip ?? false, markSizeRatio: dataLayers[0].markSizeAccessor && !args.markSizeRatio ? 10 : args.markSizeRatio, ariaLabel: diff --git a/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts b/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts index a2b2abd700a1e..8b852fff257f9 100644 --- a/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts +++ b/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts @@ -221,7 +221,7 @@ export interface XYArgs extends DataLayerArgs { splitRowAccessor?: ExpressionValueVisDimension | string; splitColumnAccessor?: ExpressionValueVisDimension | string; orderBucketsBySum?: boolean; - showTooltip?: boolean; + showTooltip: boolean; } export interface LayeredXYArgs { @@ -251,7 +251,7 @@ export interface LayeredXYArgs { markSizeRatio?: number; minTimeBarInterval?: string; orderBucketsBySum?: boolean; - showTooltip?: boolean; + showTooltip: boolean; } export interface XYProps { @@ -283,7 +283,7 @@ export interface XYProps { splitRowAccessor?: ExpressionValueVisDimension | string; splitColumnAccessor?: ExpressionValueVisDimension | string; orderBucketsBySum?: boolean; - showTooltip?: boolean; + showTooltip: boolean; } export interface AnnotationLayerArgs { diff --git a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx index a4a2eccb29bb2..f847c78646ac2 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx @@ -2236,6 +2236,7 @@ describe('XYChart component', () => { yRightTitle: '', yLeftScale: 'linear', yRightScale: 'linear', + showTooltip: true, legend: { type: 'legendConfig', isVisible: false, position: Position.Top }, valueLabels: 'hide', tickLabelsVisibilitySettings: { @@ -2328,6 +2329,7 @@ describe('XYChart component', () => { yRightTitle: '', legend: { type: 'legendConfig', isVisible: false, position: Position.Top }, valueLabels: 'hide', + showTooltip: true, tickLabelsVisibilitySettings: { type: 'tickLabelsConfig', x: true, @@ -2401,6 +2403,7 @@ describe('XYChart component', () => { xTitle: '', yTitle: '', yRightTitle: '', + showTooltip: true, legend: { type: 'legendConfig', isVisible: true, position: Position.Top }, valueLabels: 'hide', tickLabelsVisibilitySettings: {