From 9388bbae03f7607452ed3879b0725aea54655f2b Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Tue, 8 Mar 2022 16:08:39 +0200 Subject: [PATCH 001/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] [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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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 c8216f932d1b113dff7adcdb9fb9bc5b2e89eb0c Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Tue, 5 Apr 2022 17:21:09 +0300 Subject: [PATCH 085/213] Add 'axis' arg --- .../expression_xy/common/constants.ts | 1 + .../expression_functions/axis_config.ts | 61 ++++++++++ .../common/expression_functions/index.ts | 1 + .../expression_functions/layered_xy_vis.ts | 8 ++ .../common/expression_functions/xy_vis.ts | 10 +- .../expression_functions/y_axis_config.ts | 15 ++- .../expression_xy/common/index.ts | 1 + .../common/types/expression_functions.ts | 12 +- .../expression_xy/public/__mocks__/index.tsx | 2 +- .../__snapshots__/xy_chart.test.tsx.snap | 8 +- .../public/components/annotations.tsx | 4 +- .../components/reference_lines.test.tsx | 53 +++++---- .../public/components/reference_lines.tsx | 47 ++++---- .../public/components/xy_chart.test.tsx | 24 +++- .../public/components/xy_chart.tsx | 36 +++--- .../public/helpers/annotations.tsx | 12 +- .../public/helpers/axes_configuration.test.ts | 35 ++++-- .../public/helpers/axes_configuration.ts | 110 ++++++++++++------ .../expression_xy/public/plugin.ts | 2 + .../empty_prompts/empty_prompts.tsx | 6 +- .../public/xy_visualization/to_expression.ts | 57 ++++++++- .../lens/public/xy_visualization/types.ts | 17 ++- 22 files changed, 378 insertions(+), 144 deletions(-) create mode 100644 src/plugins/chart_expressions/expression_xy/common/expression_functions/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..ef97b60e7aa4c 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 AXIS_CONFIG = 'axisConfig'; 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/axis_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/axis_config.ts new file mode 100644 index 0000000000000..17ce51ff7bd62 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/axis_config.ts @@ -0,0 +1,61 @@ +/* + * 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 { AxisConfig, AxisConfigResult } from '../types'; +import { AXIS_CONFIG } from '../constants'; + +export const axisConfigFunction: ExpressionFunctionDefinition< + typeof AXIS_CONFIG, + null, + AxisConfig, + AxisConfigResult +> = { + name: AXIS_CONFIG, + aliases: [], + type: AXIS_CONFIG, + help: i18n.translate('expressionXY.axisExtentConfig.help', { + defaultMessage: `Configure the xy chart's axis config`, + }), + inputTypes: ['null'], + args: { + title: { + types: ['string'], + help: i18n.translate('expressionXY.axisConfig.id.help', { + defaultMessage: 'Title of axis', + }), + }, + id: { + types: ['string'], + help: i18n.translate('expressionXY.axisConfig.id.help', { + defaultMessage: 'Id of axis', + }), + required: true, + }, + position: { + types: ['string'], + help: i18n.translate('expressionXY.axisConfig.position.help', { + defaultMessage: 'Position of the axis', + }), + default: 'left', + }, + hide: { + types: ['boolean'], + help: i18n.translate('expressionXY.axisConfig.boolean.help', { + defaultMessage: 'Hide the specified axis', + }), + }, + }, + fn(input, args) { + return { + type: AXIS_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..d6ba9ce0acf45 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 './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/layered_xy_vis.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts index 0ff8849a42744..bec5cb7bfcf9b 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 @@ -16,6 +16,7 @@ import { FittingFunctions, GRID_LINES_CONFIG, XY_VIS_RENDERER, + AXIS_CONFIG, AXIS_EXTENT_CONFIG, TICK_LABELS_CONFIG, LABELS_ORIENTATION_CONFIG, @@ -176,6 +177,13 @@ export const layeredXyVisFunction: ExpressionFunctionDefinition< }), required: false, }, + axes: { + types: [AXIS_CONFIG], + help: i18n.translate('expressionXY.xyVis.ariaLabel.help', { + defaultMessage: 'Specifies the configs for axes', + }), + multi: true, + }, }, 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 e580e20d38053..04b36148462a6 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 @@ -26,6 +26,7 @@ import { EndValues, ANNOTATION_LAYER, LayerTypes, + AXIS_CONFIG, } from '../constants'; import { Dimension, prepareLogTable } from '../../../../visualizations/common/utils'; import { getLayerDimensions } from '../utils'; @@ -192,9 +193,16 @@ export const xyVisFunction: ExpressionFunctionDefinition< }), required: false, }, + axes: { + types: [AXIS_CONFIG], + help: i18n.translate('expressionXY.xyVis.ariaLabel.help', { + defaultMessage: 'Specifies the configs for axes', + }), + multi: true, + }, }, fn(data, args, handlers) { - const { dataLayers, referenceLineLayers, annotationLayers, ...restArgs } = args; + const { dataLayers, referenceLineLayers = [], annotationLayers = [], ...restArgs } = args; const inputLayers: Array = [ ...dataLayers, ...referenceLineLayers, 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..94eef97cde069 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 { FillStyles, IconPositions, LineStyles, Y_CONFIG } from '../constants'; import { YConfig, YConfigResult } from '../types'; export const yAxisConfigFunction: ExpressionFunctionDefinition< @@ -31,13 +31,6 @@ export const yAxisConfigFunction: ExpressionFunctionDefinition< 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', - }), - }, color: { types: ['string'], help: i18n.translate('expressionXY.yConfig.color.help', { @@ -83,6 +76,12 @@ export const yAxisConfigFunction: ExpressionFunctionDefinition< defaultMessage: 'Fill', }), }, + axisId: { + types: ['string'], + help: i18n.translate('expressionXY.yConfig.axisId.help', { + defaultMessage: 'An optional id of axis', + }), + }, }, 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..5f9b298d5da45 100755 --- a/src/plugins/chart_expressions/expression_xy/common/index.ts +++ b/src/plugins/chart_expressions/expression_xy/common/index.ts @@ -11,6 +11,7 @@ export const PLUGIN_NAME = 'expressionXy'; export { xyVisFunction, + axisConfigFunction, layeredXyVisFunction, yAxisConfigFunction, legendConfigFunction, 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..b85c2695839c3 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, + AXIS_CONFIG, } from '../constants'; export type EndValue = $Values; @@ -68,13 +69,14 @@ export interface AxisExtentConfig { } export interface AxisConfig { - title: string; + title?: string; hide?: boolean; + id: string; + position?: Position; } export interface YConfig { forAccessor: string; - axisMode?: YAxisMode; color?: string; icon?: string; lineWidth?: number; @@ -82,6 +84,7 @@ export interface YConfig { fill?: FillStyle; iconPosition?: IconPosition; textVisibility?: boolean; + axisId?: string; } export interface DataLayerArgs { @@ -198,6 +201,7 @@ export interface XYArgs { hideEndzones?: boolean; valuesInLegend?: boolean; ariaLabel?: string; + axes?: AxisConfigResult[]; } export interface LayeredXYArgs { @@ -223,6 +227,7 @@ export interface LayeredXYArgs { hideEndzones?: boolean; valuesInLegend?: boolean; ariaLabel?: string; + axes?: AxisConfigResult[]; } export interface XYProps { @@ -248,6 +253,7 @@ export interface XYProps { hideEndzones?: boolean; valuesInLegend?: boolean; ariaLabel?: string; + axes?: AxisConfigResult[]; } export interface AnnotationLayerArgs { @@ -335,6 +341,8 @@ export type ExtendedDataLayerConfigResult = Omit 1) { const commonStyles = getCommonStyles(configArr); 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 76da0222ef6bc..4568fdb9c37f4 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 @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { LineAnnotation, RectAnnotation } from '@elastic/charts'; +import { LineAnnotation, Position, RectAnnotation } from '@elastic/charts'; import { shallow } from 'enzyme'; import React from 'react'; import { Datatable } from '../../../../expressions'; @@ -64,7 +64,7 @@ interface XCoords { x1: number | undefined; } -function getAxisFromId(layerPrefix: string): YConfig['axisMode'] { +function getAxisFromId(layerPrefix: string): Position { return /left/i.test(layerPrefix) ? 'left' : /right/i.test(layerPrefix) ? 'right' : 'bottom'; } @@ -72,20 +72,29 @@ 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, + xAxisFormatter: { convert: jest.fn((x) => x) } as unknown as FieldFormat, isHorizontal: false, - axesMap: { left: true, right: false }, + yAxesConfiguration: [ + { + groupId: 'left', + position: 'left', + series: [], + }, + { + groupId: 'right', + position: 'right', + series: [], + }, + { + groupId: 'bottom', + position: 'bottom', + series: [], + }, + ], paddingMap: {}, }; }); @@ -98,17 +107,16 @@ describe('ReferenceLineAnnotations', () => { ] 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( @@ -143,10 +151,10 @@ describe('ReferenceLineAnnotations', () => { layers={createLayers([ { forAccessor: `${layerPrefix}FirstId`, - axisMode: 'bottom', lineStyle: 'solid', type: 'yConfig', fill, + axisId: 'bottom', }, ])} /> @@ -177,24 +185,23 @@ describe('ReferenceLineAnnotations', () => { ] 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( @@ -233,17 +240,17 @@ describe('ReferenceLineAnnotations', () => { layers={createLayers([ { forAccessor: `${layerPrefix}FirstId`, - axisMode: 'bottom', lineStyle: 'solid', type: 'yConfig', fill, + axisId: 'bottom', }, { forAccessor: `${layerPrefix}SecondId`, - axisMode: 'bottom', lineStyle: 'solid', type: 'yConfig', fill, + axisId: 'bottom', }, ])} /> @@ -281,17 +288,17 @@ describe('ReferenceLineAnnotations', () => { layers={createLayers([ { forAccessor: `${layerPrefix}FirstId`, - axisMode, lineStyle: 'solid', fill: 'above', type: 'yConfig', + axisId: axisMode, }, { forAccessor: `${layerPrefix}SecondId`, - axisMode, lineStyle: 'solid', fill: 'below', type: 'yConfig', + axisId: axisMode, }, ])} /> @@ -330,17 +337,17 @@ describe('ReferenceLineAnnotations', () => { layers={createLayers([ { forAccessor: `yAccessorLeftFirstId`, - axisMode: 'left', lineStyle: 'solid', fill, type: 'yConfig', + axisId: 'left', }, { forAccessor: `yAccessorRightSecondId`, - axisMode: 'right', lineStyle: 'solid', fill, type: 'yConfig', + axisId: 'right', }, ])} /> 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 856425b8be79f..3c77ccc3b4a4f 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 @@ -23,6 +23,7 @@ import { mapVerticalToHorizontalPlacement, Marker, MarkerBody, + GroupsConfiguration, } from '../helpers'; export const computeChartMargins = ( @@ -65,14 +66,14 @@ export const computeChartMargins = ( export function getBaseIconPlacement( iconPosition: IconPosition | undefined, axesMap?: Record, - axisMode?: YAxisMode + position?: Position ) { if (iconPosition === 'auto') { - if (axisMode === 'bottom') { + if (position === 'bottom') { return Position.Top; } if (axesMap) { - if (axisMode === 'left') { + if (position === 'left') { return axesMap.right ? Position.Left : Position.Right; } return axesMap.left ? Position.Right : Position.Left; @@ -93,16 +94,16 @@ export function getBaseIconPlacement( export interface ReferenceLineAnnotationsProps { layers: CommonXYReferenceLineLayerConfigResult[]; - formatters: Record<'left' | 'right' | 'bottom', FieldFormat | undefined>; - axesMap: Record<'left' | 'right', boolean>; + xAxisFormatter: FieldFormat; + yAxesConfiguration: GroupsConfiguration; isHorizontal: boolean; paddingMap: Partial>; } export const ReferenceLineAnnotations = ({ layers, - formatters, - axesMap, + xAxisFormatter, + yAxesConfiguration, isHorizontal, paddingMap, }: ReferenceLineAnnotationsProps) => { @@ -130,28 +131,32 @@ export const ReferenceLineAnnotations = ({ return yConfigByValue.flatMap((yConfig, i) => { // Find the formatter for the given axis - const groupId = - yConfig.axisMode === 'bottom' - ? undefined - : yConfig.axisMode === 'right' - ? 'right' - : 'left'; + const axisGroup = yAxesConfiguration.find( + (axis) => + (yConfig.axisId && axis.groupId === yConfig.axisId) || + axis.series.some((s) => s.accessor === yConfig.forAccessor) + ); - const formatter = formatters[groupId || 'bottom']; + const formatter = axisGroup?.formatter || xAxisFormatter; const defaultColor = euiLightVars.euiColorDarkShade; + const axisMap = { + left: yAxesConfiguration.some((axes) => axes.position === 'left'), + right: yAxesConfiguration.some((axes) => axes.position === 'right'), + } + // get the position for vertical chart const markerPositionVertical = getBaseIconPlacement( yConfig.iconPosition, - axesMap, - yConfig.axisMode + axisMap, + axisGroup?.position ); // the padding map is built for vertical chart const hasReducedPadding = paddingMap[markerPositionVertical] === LINES_MARKER_SIZE; const props = { - groupId, + groupId: axisGroup?.groupId || 'bottom', marker: ( ), @@ -202,7 +207,7 @@ export const ReferenceLineAnnotations = ({ details: formatter?.convert(row[yConfig.forAccessor]) || row[yConfig.forAccessor], }))} domainType={ - yConfig.axisMode === 'bottom' + axisGroup?.position === 'bottom' ? AnnotationDomainType.XDomain : AnnotationDomainType.YDomain } @@ -231,7 +236,7 @@ export const ReferenceLineAnnotations = ({ const nextValue = shouldCheckNextReferenceLine ? row[groupedByDirection[yConfig.fill!][indexFromSameType + 1].forAccessor] : undefined; - if (yConfig.axisMode === 'bottom') { + if (axisGroup?.position === 'bottom') { return { coordinates: { x0: isFillAbove ? row[yConfig.forAccessor] : nextValue, 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..f4a3893cc3221 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 @@ -1425,17 +1425,27 @@ describe('XYChart component', () => { { type: 'yConfig', forAccessor: 'a', - axisMode: 'left', + axisId: '1', }, { type: 'yConfig', forAccessor: 'b', - axisMode: 'right', + axisId: '2', }, ], table: dataWithoutFormats, }, ], + axes: [ + { + id: '1', + position: 'left', + }, + { + id: '2', + position: 'right', + }, + ], }; const component = getRenderedComponent(newArgs); @@ -1475,17 +1485,23 @@ describe('XYChart component', () => { { type: 'yConfig', forAccessor: 'c', - axisMode: 'left', + axisId: '1', }, { type: 'yConfig', forAccessor: 'd', - axisMode: 'left', + axisId: '1', }, ], table: dataWithoutFormats, }, ], + axes: [ + { + id: '1', + position: 'left', + }, + ], }; const component = getRenderedComponent(newArgs); 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..6440d2c95d688 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,6 +71,7 @@ import { computeOverallDataDomain, getColorAssignments, getLinesCausedPaddings, + getAxisConfig, } from '../helpers'; import { getXDomain, XyEndzones } from './x_domain'; import { getLegendAction } from './legend_action'; @@ -163,6 +164,7 @@ export function XYChart({ yLeftExtent, yRightExtent, valuesInLegend, + axes, } = args; const chartRef = useRef(null); const chartTheme = chartsThemeService.useChartsTheme(); @@ -208,7 +210,12 @@ export function XYChart({ filteredLayers.some((layer) => isDataLayer(layer) && layer.splitAccessor); const shouldRotate = isHorizontalChart(dataLayers); - const yAxesConfiguration = getAxesConfiguration(filteredLayers, shouldRotate, formatFactory); + const yAxesConfiguration = getAxesConfiguration( + filteredLayers, + shouldRotate, + axes, + formatFactory + ); const xTitle = args.xTitle || (xAxisColumn && xAxisColumn.name); const axisTitlesVisibilitySettings = args.axisTitlesVisibilitySettings || { @@ -276,7 +283,9 @@ export function XYChart({ xAxisFormatter ); const visualConfigs = [ - ...referenceLineLayers.flatMap(({ yConfig }) => yConfig), + ...referenceLineLayers + .flatMap(({ yConfig }) => yConfig) + .map((config) => ({ ...config, position: getAxisConfig(yAxesConfiguration, config)?.position })), ...groupedAnnotations, ].filter(Boolean); @@ -343,7 +352,7 @@ export function XYChart({ } } else { const axisHasReferenceLine = referenceLineLayers.some(({ yConfig }) => - yConfig?.some(({ axisMode }) => axisMode === axis.groupId) + yConfig?.some((config) => Boolean(getAxisConfig([axis], config))) ); if (!fit && axisHasReferenceLine) { // Remove this once the chart will support automatic annotation fit for other type of charts @@ -357,10 +366,10 @@ export function XYChart({ min = Math.min(computedMin, min || 0); } for (const { yConfig, table } of referenceLineLayers) { - for (const { axisMode, forAccessor } of yConfig || []) { - if (axis.groupId === axisMode) { + for (const config of yConfig || []) { + if (Boolean(getAxisConfig([axis], config))) { for (const row of table.rows) { - const value = row[forAccessor]; + const value = row[config.forAccessor]; // keep the 0 in view max = Math.max(value, max || 0, 0); min = Math.min(value, min || 0, 0); @@ -826,7 +835,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 +852,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; }, }; @@ -910,15 +919,8 @@ export function XYChart({ {referenceLineLayers.length ? ( 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..21f8f7524eaf4 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, YConfig } 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 & { position: any }) | undefined >, axesMap: Record<'left' | 'right', unknown> ) => { @@ -31,9 +31,9 @@ export const getLinesCausedPaddings = ( if (!config) { return; } - const { axisMode, icon, iconPosition, textVisibility } = config; - if (axisMode && (hasIcon(icon) || textVisibility)) { - const placement = getBaseIconPlacement(iconPosition, axesMap, axisMode); + const { position, icon, iconPosition, textVisibility } = config; + if (position && hasIcon(icon) || textVisibility) { + const placement = getBaseIconPlacement(iconPosition, axesMap, position); paddings[placement] = Math.max( paddings[placement] || 0, LINES_MARKER_SIZE * (textVisibility ? 2 : 1) // double the padding size if there's text @@ -158,7 +158,7 @@ export const AnnotationIcon = ({ }; interface MarkerConfig { - axisMode?: YAxisMode; + position?: Position; icon?: string; textVisibility?: boolean; iconPosition?: IconPosition; 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 c67ffe12caa2f..2820966050a48 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 { AxisConfig, DataLayerConfigResult } from '../../common'; import { LayerTypes } from '../../common/constants'; import { Datatable } from '../../../../../plugins/expressions/public'; import { getAxesConfiguration } from './axes_configuration'; @@ -220,6 +220,13 @@ describe('axes_configuration', () => { }, }; + const axes: AxisConfig[] = [ + { + id: '1', + position: 'right' + } + ] + const sampleLayer: DataLayerConfigResult = { type: 'dataLayer', layerType: LayerTypes.DATA, @@ -237,7 +244,7 @@ describe('axes_configuration', () => { it('should map auto series to left axis', () => { const formatFactory = jest.fn(); - const groups = getAxesConfiguration([sampleLayer], false, 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'); @@ -247,7 +254,7 @@ describe('axes_configuration', () => { 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, 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 +268,7 @@ describe('axes_configuration', () => { ...sampleLayer, accessors: ['yAccessorId', 'yAccessorId2', 'yAccessorId3'], }; - const groups = getAxesConfiguration([threeSeriesLayer], false, formatFactory); + const groups = getAxesConfiguration([threeSeriesLayer], false, [], formatFactory); expect(groups.length).toEqual(2); expect(groups[0].position).toEqual('left'); expect(groups[1].position).toEqual('right'); @@ -276,10 +283,11 @@ describe('axes_configuration', () => { [ { ...sampleLayer, - yConfig: [{ type: 'yConfig', forAccessor: 'yAccessorId', axisMode: 'right' }], + yConfig: [{ type: 'yConfig', forAccessor: 'yAccessorId', axisId: '1' }], }, ], false, + axes, formatFactory ); expect(groups.length).toEqual(1); @@ -295,18 +303,20 @@ describe('axes_configuration', () => { { ...sampleLayer, accessors: ['yAccessorId', 'yAccessorId3', 'yAccessorId4'], - yConfig: [{ type: 'yConfig', forAccessor: 'yAccessorId', axisMode: 'right' }], + yConfig: [{ type: 'yConfig', forAccessor: 'yAccessorId', axisId: '1' }], }, ], false, + axes, formatFactory ); + console.log(groups) 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(groups[0].position).toEqual('right'); + expect(groups[0].series[0].accessor).toEqual('yAccessorId'); + expect(groups[1].position).toEqual('left'); + expect(groups[1].series[0].accessor).toEqual('yAccessorId3'); + expect(groups[1].series[1].accessor).toEqual('yAccessorId4'); expect(formatFactory).toHaveBeenCalledWith({ id: 'number' }); expect(formatFactory).toHaveBeenCalledWith({ id: 'currency' }); }); @@ -318,10 +328,11 @@ describe('axes_configuration', () => { { ...sampleLayer, accessors: ['yAccessorId', 'yAccessorId3', 'yAccessorId4'], - yConfig: [{ type: 'yConfig', forAccessor: 'yAccessorId', axisMode: 'right' }], + yConfig: [{ type: 'yConfig', forAccessor: 'yAccessorId', axisId: '1' }], }, ], false, + axes, 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 1ed8bcefe555e..bbb1cef819465 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,6 +8,8 @@ import { FormatFactory } from '../types'; import { + AxisConfig, + YConfigResult, AxisExtentConfig, CommonXYDataLayerConfigResult, CommonXYReferenceLineLayerConfigResult, @@ -17,6 +19,7 @@ import type { SerializedFieldFormat, } from '../../../../../plugins/field_formats/common'; import { isDataLayer } from './visualization'; +import { Position } from '@elastic/charts'; export interface Series { layer: number; @@ -25,6 +28,11 @@ export interface Series { interface FormattedMetric extends Series { fieldFormat: SerializedFieldFormat; + axisId?: string; +} + +interface AxesSeries { + [key: string]: FormattedMetric[]; } export type GroupsConfiguration = Array<{ @@ -42,26 +50,23 @@ export function isFormatterCompatible( } export function groupAxesByType( - layers: Array + layers: Array, + axes?: AxisConfig[] ) { - const series: { - auto: FormattedMetric[]; - left: FormattedMetric[]; - right: FormattedMetric[]; - bottom: FormattedMetric[]; - } = { + const series: AxesSeries = { auto: [], left: [], right: [], - bottom: [], }; layers.forEach((layer, index) => { - const { table } = layer; + const { table, yConfig } = layer; layer.accessors.forEach((accessor) => { - const mode = - layer.yConfig?.find((yAxisConfig) => yAxisConfig.forAccessor === accessor)?.axisMode || - 'auto'; + const yConfigByAccessor = yConfig?.find((config) => config.forAccessor === accessor); + const axisConfigById = axes?.find( + (axis) => yConfigByAccessor?.axisId && axis.id === yConfigByAccessor?.axisId + ); + const key = axisConfigById?.id || 'auto'; let formatter: SerializedFieldFormat = table.columns?.find((column) => column.id === accessor) ?.meta?.params || { id: 'number' }; if ( @@ -76,10 +81,15 @@ export function groupAxesByType( }, }; } - series[mode].push({ + if (!series[key]) { + series[key] = []; + } + series[key].push({ layer: index, accessor, fieldFormat: formatter, + axisId: yConfigByAccessor?.axisId, + ...(axisConfigById || {}), }); }); }); @@ -87,27 +97,39 @@ export function groupAxesByType( const tablesExist = layers.filter(({ table }) => Boolean(table)).length > 0; series.auto.forEach((currentSeries) => { - if ( - series.left.length === 0 || - (tablesExist && - series.left.every((leftSeries) => - isFormatterCompatible(leftSeries.fieldFormat, currentSeries.fieldFormat) - )) - ) { - series.left.push(currentSeries); - } else if ( - series.right.length === 0 || - (tablesExist && - 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); + let key = Object.keys(series).find( + (seriesKey) => + seriesKey !== 'auto' && + series[seriesKey].length > 0 && + series[seriesKey].every((axisSeries) => + isFormatterCompatible(axisSeries.fieldFormat, currentSeries.fieldFormat) + ) + ); + if (!key) { + if ( + series.left.length === 0 || + (tablesExist && + series.left.every((leftSeries) => + isFormatterCompatible(leftSeries.fieldFormat, currentSeries.fieldFormat) + )) + ) { + key = 'left'; + } else if ( + series.right.length === 0 || + (tablesExist && + series.left.every((leftSeries) => + isFormatterCompatible(leftSeries.fieldFormat, currentSeries.fieldFormat) + )) + ) { + key = 'right'; + } else if (series.right.length >= series.left.length) { + key = 'left'; + } else { + key = 'right'; + } } + + series[key].push(currentSeries); }); return series; } @@ -115,12 +137,24 @@ export function groupAxesByType( export function getAxesConfiguration( layers: Array, shouldRotate: boolean, + axes?: AxisConfig[], formatFactory?: FormatFactory ): GroupsConfiguration { - const series = groupAxesByType(layers); + const series = groupAxesByType(layers, axes); const axisGroups: GroupsConfiguration = []; + axes?.forEach((axis) => { + if (series[axis.id] && series[axis.id].length > 0) { + axisGroups.push({ + groupId: axis.id, + position: axis.position || Position.Left, + formatter: formatFactory?.(series[axis.id][0].fieldFormat), + series: series[axis.id].map(({ fieldFormat, ...currentSeries }) => currentSeries), + }); + } + }); + if (series.left.length > 0) { axisGroups.push({ groupId: 'left', @@ -142,6 +176,14 @@ export function getAxesConfiguration( return axisGroups; } +export const getAxisConfig = (axesGroup?: GroupsConfiguration, yConfig?: YConfigResult) => { + return axesGroup?.find( + (axis) => + (yConfig?.axisId && yConfig.axisId === axis.groupId) || + axis.series.some(({ accessor }) => accessor === yConfig?.forAccessor) + ); +}; + export function validateExtent(hasBarOrArea: boolean, extent?: AxisExtentConfig) { const inclusiveZeroError = extent && diff --git a/src/plugins/chart_expressions/expression_xy/public/plugin.ts b/src/plugins/chart_expressions/expression_xy/public/plugin.ts index 946a346a67789..bf85fa38d5675 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, + axisConfigFunction, legendConfigFunction, gridlinesConfigFunction, axisExtentConfigFunction, @@ -60,6 +61,7 @@ export class ExpressionXyPlugin { expressions.registerFunction(dataLayerFunction); expressions.registerFunction(extendedDataLayerFunction); expressions.registerFunction(axisExtentConfigFunction); + expressions.registerFunction(axisConfigFunction); expressions.registerFunction(tickLabelsConfigFunction); expressions.registerFunction(annotationLayerFunction); expressions.registerFunction(extendedAnnotationLayerFunction); diff --git a/src/plugins/data_view_editor/public/components/empty_prompts/empty_prompts.tsx b/src/plugins/data_view_editor/public/components/empty_prompts/empty_prompts.tsx index ec5ba142df0d4..93ac885e1475c 100644 --- a/src/plugins/data_view_editor/public/components/empty_prompts/empty_prompts.tsx +++ b/src/plugins/data_view_editor/public/components/empty_prompts/empty_prompts.tsx @@ -9,7 +9,7 @@ import React, { useState, FC, useEffect } from 'react'; import useAsync from 'react-use/lib/useAsync'; -import { NoDataViewsComponent } from '@kbn/shared-ux-components'; +//import { NoDataViewsComponent } from '@kbn/shared-ux-components'; import { EuiFlyoutBody } from '@elastic/eui'; import { useKibana } from '../../shared_imports'; @@ -98,12 +98,12 @@ export const EmptyPrompts: FC = ({ allSources, onCancel, children, loadSo return ( <> - setGoToForm(true)} canCreateNewDataView={application.capabilities.indexPatterns.save as boolean} dataViewsDocLink={docLinks.links.indexPatterns.introduction} emptyPromptColor={'subdued'} - /> + /> */} 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..622e286cd15f1 100644 --- a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts +++ b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts @@ -13,6 +13,7 @@ import { EventAnnotationServiceType } from 'src/plugins/event_annotation/public' import { ExpressionAstExpression } from 'src/plugins/expressions'; import { State, + YConfig, XYDataLayerConfig, XYReferenceLineLayerConfig, XYAnnotationLayerConfig, @@ -20,7 +21,7 @@ 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 { AxisConfig } 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'; @@ -198,6 +199,31 @@ export const buildExpression = ( return null; } + const validLayersWithYConfig = [...validDataLayers, ...validReferenceLayers]; + + const isLeftAxis = validLayersWithYConfig.some(({ yConfig }) => + yConfig?.some((config) => config.axisMode === 'left') + ); + const isRightAxis = validLayersWithYConfig.some(({ yConfig }) => + yConfig?.some((config) => config.axisMode === 'right') + ); + + const axes: AxisConfig[] = []; + + if (isLeftAxis) { + axes.push({ + id: 'left', + position: 'left', + }); + } + + if (isRightAxis) { + axes.push({ + id: 'right', + position: 'right', + }); + } + return { type: 'expression', chain: [ @@ -362,10 +388,12 @@ export const buildExpression = ( valueLabels: [state?.valueLabels || 'hide'], hideEndzones: [state?.hideEndzones || false], valuesInLegend: [state?.valuesInLegend || false], + axes: [...axesToExpression(axes)], layers: [ ...validDataLayers.map((layer) => dataLayerToExpression( layer, + axes, datasourceLayers[layer.layerId], metadata, paletteService, @@ -375,6 +403,7 @@ export const buildExpression = ( ...validReferenceLayers.map((layer) => referenceLineLayerToExpression( layer, + axes, datasourceLayers[(layer as XYReferenceLineLayerConfig).layerId], datasourceExpressionsByLayers[layer.layerId] ) @@ -398,8 +427,25 @@ const buildTableExpression = (datasourceExpression: Ast): ExpressionAstExpressio chain: [{ type: 'function', function: 'kibana', arguments: {} }, ...datasourceExpression.chain], }); +const axesToExpression = (axes: AxisConfig[]): Ast => { + return axes.map((axis) => ({ + type: 'expression', + chain: [ + { + type: 'function', + function: 'axisConfig', + arguments: { + id: [axis.id], + position: [axis.position], + }, + }, + ], + })); +}; + const referenceLineLayerToExpression = ( layer: XYReferenceLineLayerConfig, + axes: AxisConfig[], datasourceLayer: DatasourcePublicAPI, datasourceExpression: Ast ): Ast => { @@ -412,7 +458,7 @@ const referenceLineLayerToExpression = ( arguments: { yConfig: layer.yConfig ? layer.yConfig.map((yConfig) => - yConfigToExpression(yConfig, defaultReferenceLineColor) + yConfigToExpression(yConfig, axes, defaultReferenceLineColor) ) : [], accessors: layer.accessors, @@ -461,6 +507,7 @@ const annotationLayerToExpression = ( const dataLayerToExpression = ( layer: ValidXYDataLayerConfig, + axes: AxisConfig[], datasourceLayer: DatasourcePublicAPI, metadata: Record>, paletteService: PaletteRegistry, @@ -493,7 +540,7 @@ const dataLayerToExpression = ( isHistogram: [isHistogramDimension], splitAccessor: layer.splitAccessor ? [layer.splitAccessor] : [], yConfig: layer.yConfig - ? layer.yConfig.map((yConfig) => yConfigToExpression(yConfig)) + ? layer.yConfig.map((yConfig) => yConfigToExpression(yConfig, axes)) : [], seriesType: [layer.seriesType], accessors: layer.accessors, @@ -530,7 +577,7 @@ const dataLayerToExpression = ( }; }; -const yConfigToExpression = (yConfig: YConfig, defaultColor?: string): Ast => { +const yConfigToExpression = (yConfig: YConfig, axes: AxisConfig[], defaultColor?: string): Ast => { return { type: 'expression', chain: [ @@ -538,8 +585,8 @@ const yConfigToExpression = (yConfig: YConfig, defaultColor?: string): Ast => { type: 'function', function: 'yConfig', arguments: { + axisId: [axes.find((axis) => axis.position === yConfig.axisMode)?.id], forAccessor: [yConfig.forAccessor], - axisMode: yConfig.axisMode ? [yConfig.axisMode] : [], color: yConfig.color ? [yConfig.color] : defaultColor ? [defaultColor] : [], lineStyle: [yConfig.lineStyle || 'solid'], lineWidth: [yConfig.lineWidth || 1], diff --git a/x-pack/plugins/lens/public/xy_visualization/types.ts b/x-pack/plugins/lens/public/xy_visualization/types.ts index 901584e785b27..61c7041313a8a 100644 --- a/x-pack/plugins/lens/public/xy_visualization/types.ts +++ b/x-pack/plugins/lens/public/xy_visualization/types.ts @@ -28,13 +28,28 @@ import type { FittingFunction, LabelsOrientationConfig, EndValue, - YConfig, YScaleType, XScaleType, + LineStyle, + IconPosition, + FillStyle, + YAxisMode } from '../../../../../src/plugins/chart_expressions/expression_xy/common'; import { EventAnnotationConfig } from '../../../../../src/plugins/event_annotation/common'; import type { ValueLabelConfig } from '../../common/types'; +export interface YConfig { + forAccessor: string; + color?: string; + icon?: string; + lineWidth?: number; + lineStyle?: LineStyle; + fill?: FillStyle; + iconPosition?: IconPosition; + textVisibility?: boolean; + axisMode?: YAxisMode; +} + export interface XYDataLayerConfig { layerId: string; accessors: string[]; From bb91cf896cf92444e5c5ba5aafa782f9bc19362b Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Tue, 5 Apr 2022 19:16:52 +0300 Subject: [PATCH 086/213] 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 087/213] 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 088/213] 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 089/213] 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 70312b3303a9828489fa73dc3f095c6cc8d15c8f Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Wed, 6 Apr 2022 14:23:37 +0300 Subject: [PATCH 090/213] Some fixes --- .../expression_xy/public/components/reference_lines.tsx | 2 +- .../expression_xy/public/components/xy_chart.tsx | 7 +++++-- .../expression_xy/public/helpers/annotations.tsx | 2 +- .../public/helpers/axes_configuration.test.ts | 7 +++---- .../expression_xy/public/helpers/axes_configuration.ts | 9 ++++----- .../__snapshots__/to_expression.test.ts.snap | 7 +------ x-pack/plugins/lens/public/xy_visualization/types.ts | 2 +- 7 files changed, 16 insertions(+), 20 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 3c77ccc3b4a4f..ae069559411ab 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 @@ -144,7 +144,7 @@ export const ReferenceLineAnnotations = ({ const axisMap = { left: yAxesConfiguration.some((axes) => axes.position === 'left'), right: yAxesConfiguration.some((axes) => axes.position === 'right'), - } + }; // get the position for vertical chart const markerPositionVertical = getBaseIconPlacement( 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 8864b33e3017c..90242f432f9ec 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 @@ -258,7 +258,7 @@ export function XYChart({ right: yAxesConfiguration.find(({ groupId }) => groupId === 'right'), }; - const getYAxesTitles = (axisSeries: Series[], groupId: 'right' | 'left') => { + const getYAxesTitles = (axisSeries: Series[], groupId: string) => { const yTitle = groupId === 'right' ? args.yRightTitle : args.yTitle; return ( yTitle || @@ -286,7 +286,10 @@ export function XYChart({ const visualConfigs = [ ...referenceLineLayers .flatMap(({ yConfig }) => yConfig) - .map((config) => ({ ...config, position: getAxisConfig(yAxesConfiguration, config)?.position })), + .map((config) => ({ + ...config, + position: getAxisConfig(yAxesConfiguration, config)?.position, + })), ...groupedAnnotations, ].filter(Boolean); 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 21f8f7524eaf4..12ab7663c93b2 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 { position, icon, iconPosition, textVisibility } = config; - if (position && hasIcon(icon) || textVisibility) { + if ((position && hasIcon(icon)) || textVisibility) { const placement = getBaseIconPlacement(iconPosition, axesMap, position); paddings[placement] = Math.max( paddings[placement] || 0, 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 2820966050a48..2ca1dd7c56b40 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 @@ -223,9 +223,9 @@ describe('axes_configuration', () => { const axes: AxisConfig[] = [ { id: '1', - position: 'right' - } - ] + position: 'right', + }, + ]; const sampleLayer: DataLayerConfigResult = { type: 'dataLayer', @@ -310,7 +310,6 @@ describe('axes_configuration', () => { axes, formatFactory ); - console.log(groups) expect(groups.length).toEqual(2); expect(groups[0].position).toEqual('right'); expect(groups[0].series[0].accessor).toEqual('yAccessorId'); 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 832b1bf1784e2..1e27b9bad32f6 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 @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import { Position } from '@elastic/charts'; import { FormatFactory } from '../types'; import { AxisConfig, @@ -19,7 +20,6 @@ import type { SerializedFieldFormat, } from '../../../../../plugins/field_formats/common'; import { isDataLayer } from './visualization'; -import { Position } from '@elastic/charts'; export interface Series { layer: number; @@ -36,7 +36,7 @@ interface AxesSeries { } export type GroupsConfiguration = Array<{ - groupId: 'left' | 'right'; + groupId: string; position: 'left' | 'right' | 'bottom' | 'top'; formatter?: IFieldFormat; series: Series[]; @@ -89,7 +89,6 @@ export function groupAxesByType( accessor, fieldFormat: formatter, axisId: yConfigByAccessor?.axisId, - ...(axisConfigById || {}), }); }); }); @@ -99,7 +98,7 @@ export function groupAxesByType( series.auto.forEach((currentSeries) => { let key = Object.keys(series).find( (seriesKey) => - seriesKey !== 'auto' && + seriesKey.includes('axis') && series[seriesKey].length > 0 && series[seriesKey].every((axisSeries) => isFormatterCompatible(axisSeries.fieldFormat, currentSeries.fieldFormat) @@ -147,7 +146,7 @@ export function getAxesConfiguration( axes?.forEach((axis) => { if (series[axis.id] && series[axis.id].length > 0) { axisGroups.push({ - groupId: axis.id, + groupId: `axis-${axis.id}`, position: axis.position || Position.Left, formatter: formatFactory?.(series[axis.id][0].fieldFormat), series: series[axis.id].map(({ fieldFormat, ...currentSeries }) => currentSeries), 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..4fb573199eff6 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 @@ -5,6 +5,7 @@ Object { "chain": Array [ Object { "arguments": Object { + "axes": Array [], "axisTitlesVisibilitySettings": Array [ Object { "chain": Array [ @@ -30,9 +31,6 @@ Object { "curveType": Array [ "LINEAR", ], - "description": Array [ - "", - ], "emphasizeFitting": Array [ true, ], @@ -213,9 +211,6 @@ Object { "type": "expression", }, ], - "title": Array [ - "", - ], "valueLabels": Array [ "hide", ], diff --git a/x-pack/plugins/lens/public/xy_visualization/types.ts b/x-pack/plugins/lens/public/xy_visualization/types.ts index 61c7041313a8a..1170cf1f2886d 100644 --- a/x-pack/plugins/lens/public/xy_visualization/types.ts +++ b/x-pack/plugins/lens/public/xy_visualization/types.ts @@ -33,7 +33,7 @@ import type { LineStyle, IconPosition, FillStyle, - YAxisMode + YAxisMode, } from '../../../../../src/plugins/chart_expressions/expression_xy/common'; import { EventAnnotationConfig } from '../../../../../src/plugins/event_annotation/common'; import type { ValueLabelConfig } from '../../common/types'; From b2b0317f3066e2ae1cb9a4facb87bbc01934c395 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Wed, 6 Apr 2022 12:13:33 +0000 Subject: [PATCH 091/213] [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' --- .../public/components/empty_prompts/empty_prompts.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/data_view_editor/public/components/empty_prompts/empty_prompts.tsx b/src/plugins/data_view_editor/public/components/empty_prompts/empty_prompts.tsx index 93ac885e1475c..cab4b0006aab8 100644 --- a/src/plugins/data_view_editor/public/components/empty_prompts/empty_prompts.tsx +++ b/src/plugins/data_view_editor/public/components/empty_prompts/empty_prompts.tsx @@ -9,7 +9,7 @@ import React, { useState, FC, useEffect } from 'react'; import useAsync from 'react-use/lib/useAsync'; -//import { NoDataViewsComponent } from '@kbn/shared-ux-components'; +// import { NoDataViewsComponent } from '@kbn/shared-ux-components'; import { EuiFlyoutBody } from '@elastic/eui'; import { useKibana } from '../../shared_imports'; From db0a39b6a82fc45fe53a6c919a97d412fa06ebac Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Wed, 6 Apr 2022 15:21:10 +0300 Subject: [PATCH 092/213] Fix CI --- .../expression_xy/public/components/reference_lines.tsx | 6 +----- .../expression_xy/public/components/xy_chart.test.tsx | 7 +++++-- 2 files changed, 6 insertions(+), 7 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 ae069559411ab..562d5b5dca818 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 '../../../../field_formats/common'; -import type { - CommonXYReferenceLineLayerConfigResult, - IconPosition, - YAxisMode, -} from '../../common/types'; +import type { CommonXYReferenceLineLayerConfigResult, IconPosition } from '../../common/types'; import { LINES_MARKER_SIZE, mapVerticalToHorizontalPlacement, 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 f4a3893cc3221..9d915c31125a8 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 @@ -1438,10 +1438,12 @@ describe('XYChart component', () => { ], axes: [ { + type: 'axisConfig', id: '1', position: 'left', }, { + type: 'axisConfig', id: '2', position: 'right', }, @@ -1498,6 +1500,7 @@ describe('XYChart component', () => { ], axes: [ { + type: 'axisConfig', id: '1', position: 'left', }, @@ -1642,8 +1645,8 @@ describe('XYChart component', () => { 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(''); + expect(nameFn({ ...nameFnArgs, seriesKeys: ['a'] }, false)).toEqual(null); + expect(nameFn({ ...nameFnArgs, seriesKeys: ['nonsense'] }, false)).toEqual(null); }); test('simplest xy chart with empty name', () => { From 688f56ada9ae73fd6e1ac67ca1554fc734f2d03d Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Wed, 6 Apr 2022 16:15:32 +0300 Subject: [PATCH 093/213] Fix checks --- .../chart_expressions/expression_xy/common/index.ts | 1 - .../expression_xy/common/types/expression_functions.ts | 2 -- x-pack/plugins/lens/public/index.ts | 2 +- .../public/xy_visualization/reference_line_helpers.tsx | 6 +----- .../lens/public/xy_visualization/to_expression.ts | 5 +++-- x-pack/plugins/lens/public/xy_visualization/types.ts | 10 +++++++++- .../lens/public/xy_visualization/visualization.tsx | 4 +--- .../xy_config_panel/dimension_editor.tsx | 6 +----- .../xy_config_panel/reference_line_panel.tsx | 7 ++----- .../shared/marker_decoration_settings.tsx | 6 ++---- 10 files changed, 20 insertions(+), 29 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/common/index.ts b/src/plugins/chart_expressions/expression_xy/common/index.ts index 5f9b298d5da45..938124f6475b6 100755 --- a/src/plugins/chart_expressions/expression_xy/common/index.ts +++ b/src/plugins/chart_expressions/expression_xy/common/index.ts @@ -34,7 +34,6 @@ export type { EndValue, XYRender, LayerType, - YAxisMode, LineStyle, FillStyle, SeriesType, 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 7b70e92725824..5871c7fcd8e12 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 @@ -23,7 +23,6 @@ import { ValueLabelModes, XScaleTypes, XYCurveTypes, - YAxisModes, YScaleTypes, REFERENCE_LINE_LAYER, Y_CONFIG, @@ -44,7 +43,6 @@ import { export type EndValue = $Values; export type LayerType = $Values; -export type YAxisMode = $Values; export type LineStyle = $Values; export type FillStyle = $Values; export type SeriesType = $Values; diff --git a/x-pack/plugins/lens/public/index.ts b/x-pack/plugins/lens/public/index.ts index f36550c6d325a..71a80c4cb82c7 100644 --- a/x-pack/plugins/lens/public/index.ts +++ b/x-pack/plugins/lens/public/index.ts @@ -18,6 +18,7 @@ export type { ValidLayer, XYDataLayerConfig, XYAnnotationLayerConfig, + YAxisMode, } from './xy_visualization/types'; export type { DatasourcePublicAPI, @@ -74,7 +75,6 @@ export type { YConfig, XYRender, LayerType, - YAxisMode, LineStyle, FillStyle, SeriesType, 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..463ba42610e8a 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,15 +8,11 @@ import { groupBy, partition } from 'lodash'; import { i18n } from '@kbn/i18n'; import { layerTypes } from '../../common'; -import type { - YAxisMode, - YConfig, -} 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, XYDataLayerConfig, XYReferenceLineLayerConfig, YConfig, YAxisMode } from './types'; import { checkScaleOperation, getAxisName, 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 d0448b0878236..eae5a9eddff52 100644 --- a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts +++ b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts @@ -423,7 +423,7 @@ const buildTableExpression = (datasourceExpression: Ast): ExpressionAstExpressio chain: [{ type: 'function', function: 'kibana', arguments: {} }, ...datasourceExpression.chain], }); -const axesToExpression = (axes: AxisConfig[]): Ast => { +const axesToExpression = (axes: AxisConfig[]): Ast[] => { return axes.map((axis) => ({ type: 'expression', chain: [ @@ -574,6 +574,7 @@ const dataLayerToExpression = ( }; const yConfigToExpression = (yConfig: YConfig, axes: AxisConfig[], defaultColor?: string): Ast => { + const axisId = axes.find((axis) => axis.position === yConfig.axisMode)?.id; return { type: 'expression', chain: [ @@ -581,7 +582,7 @@ const yConfigToExpression = (yConfig: YConfig, axes: AxisConfig[], defaultColor? type: 'function', function: 'yConfig', arguments: { - axisId: [axes.find((axis) => axis.position === yConfig.axisMode)?.id], + axisId: axisId ? [axisId] : [], forAccessor: [yConfig.forAccessor], color: yConfig.color ? [yConfig.color] : defaultColor ? [defaultColor] : [], lineStyle: [yConfig.lineStyle || 'solid'], diff --git a/x-pack/plugins/lens/public/xy_visualization/types.ts b/x-pack/plugins/lens/public/xy_visualization/types.ts index 1170cf1f2886d..f6b0aac555497 100644 --- a/x-pack/plugins/lens/public/xy_visualization/types.ts +++ b/x-pack/plugins/lens/public/xy_visualization/types.ts @@ -6,6 +6,7 @@ */ import { i18n } from '@kbn/i18n'; +import { $Values } from '@kbn/utility-types'; import { LensIconChartArea } from '../assets/chart_area'; import { LensIconChartAreaStacked } from '../assets/chart_area_stacked'; import { LensIconChartAreaPercentage } from '../assets/chart_area_percentage'; @@ -33,11 +34,18 @@ import type { LineStyle, IconPosition, FillStyle, - YAxisMode, } from '../../../../../src/plugins/chart_expressions/expression_xy/common'; import { EventAnnotationConfig } from '../../../../../src/plugins/event_annotation/common'; import type { ValueLabelConfig } from '../../common/types'; +export const YAxisModes = { + AUTO: 'auto', + LEFT: 'left', + RIGHT: 'right', + BOTTOM: 'bottom', +} as const; + +export type YAxisMode = $Values; export interface YConfig { forAccessor: string; color?: string; diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx index 04b5661e75b3f..9b1ad728e594e 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx @@ -31,10 +31,8 @@ import type { import { FillStyle, SeriesType, - YAxisMode, - YConfig, } from '../../../../../src/plugins/chart_expressions/expression_xy/common'; -import { State, visualizationTypes, XYSuggestion, XYLayerConfig, XYDataLayerConfig } from './types'; +import { State, visualizationTypes, XYSuggestion, XYLayerConfig, XYDataLayerConfig, YConfig, YAxisMode } 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/xy_config_panel/dimension_editor.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/dimension_editor.tsx index f288cf5841b28..90895df57df06 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,8 @@ 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, XYDataLayerConfig, YConfig, YAxisMode } from '../types'; import { FormatFactory } from '../../../common'; -import { - YAxisMode, - YConfig, -} from '../../../../../../src/plugins/chart_expressions/expression_xy/common'; import { isHorizontalChart } from '../state_helpers'; import { ColorPicker } from './color_picker'; import { PalettePicker, useDebouncedValue } from '../../shared_components'; 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..a4bd415612d38 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,9 @@ 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, XYReferenceLineLayerConfig, YConfig } from '../types'; import { FormatFactory } from '../../../common'; -import { - FillStyle, - YConfig, -} from '../../../../../../src/plugins/chart_expressions/expression_xy/common'; +import { FillStyle } from '../../../../../../src/plugins/chart_expressions/expression_xy/common'; import { ColorPicker } from './color_picker'; import { updateLayer } from '.'; 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..7f7843a1b226c 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,10 +8,8 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { EuiButtonGroup, EuiFormRow } from '@elastic/eui'; -import { - IconPosition, - YAxisMode, -} from '../../../../../../../src/plugins/chart_expressions/expression_xy/common'; +import { YAxisMode } from '../../types'; +import { IconPosition } from '../../../../../../../src/plugins/chart_expressions/expression_xy/common'; import { TooltipWrapper } from '../../../shared_components'; import { hasIcon, IconSelect, IconSet } from './icon_select'; From d2269e7d0701895f4fe36b871906704ede0cd881 Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Wed, 6 Apr 2022 16:31:45 +0300 Subject: [PATCH 094/213] Fix some lint errors --- .../public/xy_visualization/reference_line_helpers.tsx | 8 +++++++- .../lens/public/xy_visualization/visualization.tsx | 10 +++++++++- 2 files changed, 16 insertions(+), 2 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 463ba42610e8a..987912149c27b 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 @@ -12,7 +12,13 @@ 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, YConfig, YAxisMode } from './types'; +import type { + XYState, + XYDataLayerConfig, + XYReferenceLineLayerConfig, + YConfig, + YAxisMode, +} from './types'; import { checkScaleOperation, getAxisName, diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx index 9b1ad728e594e..7372061a24618 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx @@ -32,7 +32,15 @@ import { FillStyle, SeriesType, } from '../../../../../src/plugins/chart_expressions/expression_xy/common'; -import { State, visualizationTypes, XYSuggestion, XYLayerConfig, XYDataLayerConfig, YConfig, YAxisMode } from './types'; +import { + State, + visualizationTypes, + XYSuggestion, + XYLayerConfig, + XYDataLayerConfig, + YConfig, + YAxisMode, +} from './types'; import { layerTypes } from '../../common'; import { isHorizontalChart } from './state_helpers'; import { toExpression, toPreviewExpression, getSortedAccessors } from './to_expression'; From cb58c3b0b8b0006e58f2f8c415d6ba5a019595c1 Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Wed, 6 Apr 2022 16:52:22 +0300 Subject: [PATCH 095/213] Some fixes --- x-pack/plugins/lens/public/xy_visualization/to_expression.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 eae5a9eddff52..d164e86969c83 100644 --- a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts +++ b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts @@ -432,7 +432,7 @@ const axesToExpression = (axes: AxisConfig[]): Ast[] => { function: 'axisConfig', arguments: { id: [axis.id], - position: [axis.position], + position: axis.position ? [axis.position] : [], }, }, ], From 595ace3b2e6fa435a6c429056b15075974099221 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Wed, 6 Apr 2022 21:37:15 +0300 Subject: [PATCH 096/213] 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 097/213] 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 098/213] 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 099/213] 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 601293f7744178819483c0c1696f2c88281ad658 Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Thu, 7 Apr 2022 13:48:55 +0300 Subject: [PATCH 100/213] Some refacroting --- .../expression_functions/axis_config.ts | 4 ++-- .../expression_functions/layered_xy_vis.ts | 2 +- .../common/expression_functions/xy_vis.ts | 2 +- .../__snapshots__/xy_chart.test.tsx.snap | 14 ++++++++++++ .../public/components/xy_chart.tsx | 22 +++++++++---------- .../public/helpers/axes_configuration.ts | 10 +++++++-- 6 files changed, 36 insertions(+), 18 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/axis_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/axis_config.ts index 17ce51ff7bd62..ba37334c29b78 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/axis_config.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/axis_config.ts @@ -20,14 +20,14 @@ export const axisConfigFunction: ExpressionFunctionDefinition< name: AXIS_CONFIG, aliases: [], type: AXIS_CONFIG, - help: i18n.translate('expressionXY.axisExtentConfig.help', { + help: i18n.translate('expressionXY.axisConfig.help', { defaultMessage: `Configure the xy chart's axis config`, }), inputTypes: ['null'], args: { title: { types: ['string'], - help: i18n.translate('expressionXY.axisConfig.id.help', { + help: i18n.translate('expressionXY.axisConfig.title.help', { defaultMessage: 'Title of axis', }), }, 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 8e68c50f20e2b..4c332cb9e0c51 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,7 +174,7 @@ export const layeredXyVisFunction: ExpressionFunctionDefinition< }, axes: { types: [AXIS_CONFIG], - help: i18n.translate('expressionXY.xyVis.ariaLabel.help', { + help: i18n.translate('expressionXY.layeredXyVis.axes.help', { defaultMessage: 'Specifies the configs for axes', }), multi: true, 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 0f4df16d8ffe7..242eac63f3681 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 @@ -192,7 +192,7 @@ export const xyVisFunction: ExpressionFunctionDefinition< }, axes: { types: [AXIS_CONFIG], - help: i18n.translate('expressionXY.xyVis.ariaLabel.help', { + help: i18n.translate('expressionXY.xyVis.axes.help', { defaultMessage: 'Specifies the configs for axes', }), multi: true, 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 61d31788a1179..33fe2fe71f674 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 @@ -598,10 +598,12 @@ exports[`XYChart component it renders area 1`] = ` "series": Array [ Object { "accessor": "a", + "axisId": undefined, "layer": 0, }, Object { "accessor": "b", + "axisId": undefined, "layer": 0, }, ], @@ -997,10 +999,12 @@ exports[`XYChart component it renders bar 1`] = ` "series": Array [ Object { "accessor": "a", + "axisId": undefined, "layer": 0, }, Object { "accessor": "b", + "axisId": undefined, "layer": 0, }, ], @@ -1396,10 +1400,12 @@ exports[`XYChart component it renders horizontal bar 1`] = ` "series": Array [ Object { "accessor": "a", + "axisId": undefined, "layer": 0, }, Object { "accessor": "b", + "axisId": undefined, "layer": 0, }, ], @@ -1795,10 +1801,12 @@ exports[`XYChart component it renders line 1`] = ` "series": Array [ Object { "accessor": "a", + "axisId": undefined, "layer": 0, }, Object { "accessor": "b", + "axisId": undefined, "layer": 0, }, ], @@ -2194,10 +2202,12 @@ exports[`XYChart component it renders stacked area 1`] = ` "series": Array [ Object { "accessor": "a", + "axisId": undefined, "layer": 0, }, Object { "accessor": "b", + "axisId": undefined, "layer": 0, }, ], @@ -2593,10 +2603,12 @@ exports[`XYChart component it renders stacked bar 1`] = ` "series": Array [ Object { "accessor": "a", + "axisId": undefined, "layer": 0, }, Object { "accessor": "b", + "axisId": undefined, "layer": 0, }, ], @@ -2992,10 +3004,12 @@ exports[`XYChart component it renders stacked horizontal bar 1`] = ` "series": Array [ Object { "accessor": "a", + "axisId": undefined, "layer": 0, }, Object { "accessor": "b", + "axisId": undefined, "layer": 0, }, ], 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 28fe77178b7d7..4782acdfc86b8 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 @@ -30,13 +30,6 @@ import { RenderMode } from '../../../../expressions/common'; import { EmptyPlaceholder } from '../../../../../plugins/charts/public'; import type { FilterEvent, BrushEvent, FormatFactory } from '../types'; import type { SeriesType, XYChartProps } from '../../common/types'; -import { - isHorizontalChart, - getAnnotationsLayers, - getDataLayers, - Series, - getAreAlreadyFormattedLayersInfo, -} from '../helpers'; import { EventAnnotationServiceType } from '../../../../event_annotation/public'; import { ChartsPluginSetup, @@ -46,6 +39,11 @@ import { } from '../../../../../plugins/charts/public'; import { MULTILAYER_TIME_AXIS_STYLE } from '../../../../../plugins/charts/common'; import { + isHorizontalChart, + getAnnotationsLayers, + getDataLayers, + AxisConfiguration, + getAreAlreadyFormattedLayersInfo, getFilteredLayers, getReferenceLayers, isDataLayer, @@ -234,11 +232,11 @@ export function XYChart({ right: yAxesConfiguration.find(({ groupId }) => groupId === 'right'), }; - const getYAxesTitles = (axisSeries: Series[], groupId: string) => { - const yTitle = groupId === 'right' ? args.yRightTitle : args.yTitle; + const getYAxesTitles = (axis: AxisConfiguration) => { + const yTitle = axis.title || (axis.groupId === 'right' ? args.yRightTitle : args.yTitle); return ( yTitle || - axisSeries + axis.series .map( (series) => layers[series.layer].table.columns.find((column) => column.id === series.accessor)?.name @@ -580,14 +578,14 @@ export function XYChart({ id={axis.groupId} groupId={axis.groupId} position={axis.position} - title={getYAxesTitles(axis.series, axis.groupId)} + title={getYAxesTitles(axis)} gridLine={{ visible: axis.groupId === 'right' ? gridlinesVisibilitySettings?.yRight : gridlinesVisibilitySettings?.yLeft, }} - hide={dataLayers[0]?.hide} + hide={axis.hide || dataLayers[0]?.hide} tickFormat={(d) => axis.formatter?.convert(d) || ''} style={getYAxesStyle(axis.groupId as 'left' | 'right')} domain={getYAxisDomain(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 1e27b9bad32f6..e3b513e6f7091 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 @@ -35,12 +35,16 @@ interface AxesSeries { [key: string]: FormattedMetric[]; } -export type GroupsConfiguration = Array<{ +export interface AxisConfiguration { groupId: string; position: 'left' | 'right' | 'bottom' | 'top'; formatter?: IFieldFormat; series: Series[]; -}>; + title?: string; + hide?: boolean; +} + +export type GroupsConfiguration = Array; export function isFormatterCompatible( formatter1: SerializedFieldFormat, @@ -150,6 +154,8 @@ export function getAxesConfiguration( position: axis.position || Position.Left, formatter: formatFactory?.(series[axis.id][0].fieldFormat), series: series[axis.id].map(({ fieldFormat, ...currentSeries }) => currentSeries), + title: axis.title, + hide: axis.hide, }); } }); From 99bd7cc629ef7e332fa137f51ca45d3bb3db2791 Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Thu, 7 Apr 2022 14:06:13 +0300 Subject: [PATCH 101/213] Fix types --- .../expression_xy/public/helpers/axes_configuration.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 e3b513e6f7091..bbcdf5eaac64f 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 @@ -44,7 +44,7 @@ export interface AxisConfiguration { hide?: boolean; } -export type GroupsConfiguration = Array; +export type GroupsConfiguration = AxisConfiguration[]; export function isFormatterCompatible( formatter1: SerializedFieldFormat, From b43a793402da6491fcd74c7a7f199983db72f43c Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Thu, 7 Apr 2022 18:12:55 +0300 Subject: [PATCH 102/213] 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 98f871a45559308e6ce766c716e81f161e01de93 Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Thu, 7 Apr 2022 18:39:13 +0300 Subject: [PATCH 103/213] Some fixes --- .../public/helpers/axes_configuration.ts | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) 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 bbcdf5eaac64f..c3ef6cde50e51 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 @@ -137,6 +137,27 @@ export function groupAxesByType( return series; } +function getPosition(position: Position, shouldRotate: boolean) { + if (shouldRotate) { + switch (position) { + case Position.Bottom: { + return Position.Right; + } + case Position.Right: { + return Position.Top; + } + case Position.Top: { + return Position.Left; + } + case Position.Left: { + return Position.Bottom; + } + } + } + + return position; +} + export function getAxesConfiguration( layers: Array, shouldRotate: boolean, @@ -151,7 +172,7 @@ export function getAxesConfiguration( if (series[axis.id] && series[axis.id].length > 0) { axisGroups.push({ groupId: `axis-${axis.id}`, - position: axis.position || Position.Left, + position: getPosition(axis.position || Position.Left, shouldRotate), formatter: formatFactory?.(series[axis.id][0].fieldFormat), series: series[axis.id].map(({ fieldFormat, ...currentSeries }) => currentSeries), title: axis.title, From 5f661b0b07bfa18cb7c8bd0b4f371239900debab Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Fri, 8 Apr 2022 09:02:57 +0300 Subject: [PATCH 104/213] 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 105/213] 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 caa5616e2ba893b40f70386eb9496c0a6e0400cf Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Fri, 8 Apr 2022 13:39:57 +0300 Subject: [PATCH 106/213] Fix CI --- .../public/components/empty_prompts/empty_prompts.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/data_view_editor/public/components/empty_prompts/empty_prompts.tsx b/src/plugins/data_view_editor/public/components/empty_prompts/empty_prompts.tsx index cab4b0006aab8..ec5ba142df0d4 100644 --- a/src/plugins/data_view_editor/public/components/empty_prompts/empty_prompts.tsx +++ b/src/plugins/data_view_editor/public/components/empty_prompts/empty_prompts.tsx @@ -9,7 +9,7 @@ import React, { useState, FC, useEffect } from 'react'; import useAsync from 'react-use/lib/useAsync'; -// import { NoDataViewsComponent } from '@kbn/shared-ux-components'; +import { NoDataViewsComponent } from '@kbn/shared-ux-components'; import { EuiFlyoutBody } from '@elastic/eui'; import { useKibana } from '../../shared_imports'; @@ -98,12 +98,12 @@ export const EmptyPrompts: FC = ({ allSources, onCancel, children, loadSo return ( <> - {/* setGoToForm(true)} canCreateNewDataView={application.capabilities.indexPatterns.save as boolean} dataViewsDocLink={docLinks.links.indexPatterns.introduction} emptyPromptColor={'subdued'} - /> */} + /> From c17026f01a51cd30064f94d7fce71fd223220db0 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Tue, 12 Apr 2022 15:48:28 +0300 Subject: [PATCH 107/213] 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 108/213] 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 109/213] 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 110/213] 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 111/213] 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 112/213] 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 113/213] 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 114/213] 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 115/213] 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 116/213] 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 117/213] 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 3bd2e22fe5755fdde4b2c55b8fa5c057f351fd89 Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Thu, 14 Apr 2022 12:54:23 +0300 Subject: [PATCH 118/213] Some updates --- .../expression_xy/common/constants.ts | 7 ++ .../expression_functions/axis_config.ts | 60 +++++++++++++++- .../common/expression_functions/data_layer.ts | 13 ++++ .../extended_data_layer.ts | 7 ++ .../common/expression_functions/xy_vis.ts | 38 +++++++++- .../expression_xy/common/index.ts | 1 + .../common/types/expression_functions.ts | 16 +++++ .../public/components/data_layers.tsx | 9 ++- .../public/components/xy_chart.tsx | 71 +++++++++++++------ .../public/helpers/axes_configuration.test.ts | 1 + .../public/helpers/axes_configuration.ts | 15 ++-- .../public/helpers/color_assignment.test.ts | 2 + .../public/helpers/data_layers.tsx | 9 ++- .../public/xy_visualization/to_expression.ts | 1 + 14 files changed, 213 insertions(+), 37 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/common/constants.ts b/src/plugins/chart_expressions/expression_xy/common/constants.ts index ef97b60e7aa4c..3c7fcede74bac 100644 --- a/src/plugins/chart_expressions/expression_xy/common/constants.ts +++ b/src/plugins/chart_expressions/expression_xy/common/constants.ts @@ -114,3 +114,10 @@ export const ValueLabelModes = { INSIDE: 'inside', OUTSIDE: 'outside', } as const; + +export const AxisModes = { + NORMAL: 'normal', + PERCENTAGE: 'percentage', + WIGGLE: 'wiggle', + SILHOUETTE: 'silhouette', +} as const; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/axis_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/axis_config.ts index ba37334c29b78..6f5888cd8d84d 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/axis_config.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/axis_config.ts @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import type { ExpressionFunctionDefinition } from '../../../../expressions/common'; import { AxisConfig, AxisConfigResult } from '../types'; -import { AXIS_CONFIG } from '../constants'; +import { AXIS_CONFIG, AxisModes } from '../constants'; export const axisConfigFunction: ExpressionFunctionDefinition< typeof AXIS_CONFIG, @@ -51,6 +51,64 @@ export const axisConfigFunction: ExpressionFunctionDefinition< defaultMessage: 'Hide the specified axis', }), }, + mode: { + types: ['string'], + options: [...Object.values(AxisModes)], + help: i18n.translate('expressionXY.axisConfig.mode.help', { + defaultMessage: 'Scale mode. Can be normal, percentage, wiggle or silhouette', + }), + }, + boundsMargin: { + types: ['number'], + help: i18n.translate('expressionXY.axisConfig.boundsMargin.help', { + defaultMessage: 'Margin of bounds', + }), + }, + labelColor: { + types: ['string'], + help: i18n.translate('expressionXY.axisConfig.labelColor.help', { + defaultMessage: 'Color of the axis labels', + }), + }, + showOverlappingLabels: { + types: ['boolean'], + help: i18n.translate('expressionXY.axisConfig.showOverlappingLabels.help', { + defaultMessage: 'Show overlapping labels', + }), + }, + showDuplicates: { + types: ['boolean'], + help: i18n.translate('expressionXY.axisConfig.showDuplicates.help', { + defaultMessage: 'Show duplicated ticks', + }), + }, + labelsOrientation: { + types: ['number'], + options: [0, -90, -45], + help: i18n.translate('expressionXY.axisConfig.labelsOrientation.help', { + defaultMessage: 'Specifies the labels orientation of the axis.', + }), + }, + showLabels: { + types: ['boolean'], + help: i18n.translate('expressionXY.axisConfig.showLabels.help', { + defaultMessage: 'Show labels', + }), + default: true, + }, + showTitle: { + types: ['boolean'], + help: i18n.translate('expressionXY.axisConfig.showTitle.help', { + defaultMessage: 'Show title of the axis', + }), + default: true, + }, + truncate: { + types: ['number'], + help: i18n.translate('expressionXY.axisConfig.truncate.help', { + defaultMessage: 'The number of symbols before truncating', + }), + }, }, fn(input, args) { return { 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 dcef42eaef0b2..97f5d1ddde40a 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 @@ -54,6 +54,13 @@ export const dataLayerFunction: ExpressionFunctionDefinition< required: true, strict: true, }, + isPercentage: { + types: ['boolean'], + default: false, + help: i18n.translate('expressionXY.dataLayer.isPercentage.help', { + defaultMessage: 'Whether to layout the chart has percentage mode.', + }), + }, xScaleType: { options: [...Object.values(XScaleTypes)], help: i18n.translate('expressionXY.dataLayer.xScaleType.help', { @@ -110,6 +117,12 @@ export const dataLayerFunction: ExpressionFunctionDefinition< }), default: '{palette}', }, + xAxisId: { + types: ['string'], + help: i18n.translate('expressionXY.dataLayer.xAxisId.help', { + defaultMessage: 'Id of x-axis', + }), + }, }, fn(table, args) { return { 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..d5a1dde8b69ca 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 @@ -62,6 +62,13 @@ export const extendedDataLayerFunction: ExpressionFunctionDefinition< default: XScaleTypes.ORDINAL, strict: true, }, + isPercentage: { + types: ['boolean'], + default: false, + help: i18n.translate('expressionXY.dataLayer.isPercentage.help', { + defaultMessage: 'Whether to layout the chart has percentage mode', + }), + }, isHistogram: { types: ['boolean'], default: 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 242eac63f3681..75fc499ecc93d 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 { XYArgs, XYLayerConfigResult, XYRender } from '../types'; +import { + XYArgs, + XYLayerConfigResult, + DataLayerConfigResult, + XYRender, + AxisConfigResult, +} from '../types'; import { XY_VIS, DATA_LAYER, @@ -27,10 +33,30 @@ import { ANNOTATION_LAYER, LayerTypes, AXIS_CONFIG, + AxisModes, } from '../constants'; import { Dimension, prepareLogTable } from '../../../../visualizations/common/utils'; import { getLayerDimensions } from '../utils'; +function validateLayerMode(layers: DataLayerConfigResult[], axes: AxisConfigResult[]) { + const isError = layers.some( + (layer) => + layer.isPercentage && + axes.some( + (axis) => + axis.mode !== AxisModes.PERCENTAGE && + layer.yConfig?.some((config) => config.axisId === axis.id) + ) + ); + if (isError) { + throw new Error( + i18n.translate('expressionXY.xyVis.errors.conflictAxisModeError', { + defaultMessage: 'Layer has percentage mode but related axes has another one', + }) + ); + } +} + export const xyVisFunction: ExpressionFunctionDefinition< typeof XY_VIS, Datatable, @@ -190,10 +216,16 @@ export const xyVisFunction: ExpressionFunctionDefinition< defaultMessage: 'Specifies the aria label of the xy chart', }), }, + xAxisConfig: { + types: [AXIS_CONFIG], + help: i18n.translate('expressionXY.xyVis.xAxisConfig.help', { + defaultMessage: 'Specifies the configs for x-axis', + }), + }, axes: { types: [AXIS_CONFIG], help: i18n.translate('expressionXY.xyVis.axes.help', { - defaultMessage: 'Specifies the configs for axes', + defaultMessage: 'Specifies the configs for y-axes', }), multi: true, }, @@ -210,6 +242,8 @@ export const xyVisFunction: ExpressionFunctionDefinition< (layer): layer is XYLayerConfigResult => layer !== undefined ); + validateLayerMode(dataLayers, args.axes || []); + 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/index.ts b/src/plugins/chart_expressions/expression_xy/common/index.ts index 938124f6475b6..fb3d814bc8007 100755 --- a/src/plugins/chart_expressions/expression_xy/common/index.ts +++ b/src/plugins/chart_expressions/expression_xy/common/index.ts @@ -39,6 +39,7 @@ export type { SeriesType, YScaleType, XScaleType, + AxisMode, AxisConfig, ValidLayer, XYLayerArgs, 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 5871c7fcd8e12..b7e3d1c63e804 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 @@ -24,6 +24,7 @@ import { XScaleTypes, XYCurveTypes, YScaleTypes, + AxisModes, REFERENCE_LINE_LAYER, Y_CONFIG, AXIS_TITLES_VISIBILITY_CONFIG, @@ -48,6 +49,7 @@ export type FillStyle = $Values; export type SeriesType = $Values; export type YScaleType = $Values; export type XScaleType = $Values; +export type AxisMode = $Values; export type XYCurveType = $Values; export type IconPosition = $Values; export type ValueLabelMode = $Values; @@ -71,6 +73,15 @@ export interface AxisConfig { hide?: boolean; id: string; position?: Position; + mode?: AxisMode; + boundsMargin?: number; + labelColor?: string; + showOverlappingLabels?: boolean; + showDuplicates?: boolean; + labelsOrientation?: number; + truncate?: number; + showLabels?: boolean; + showTitle?: boolean; } export interface YConfig { @@ -95,9 +106,11 @@ export interface DataLayerArgs { yScaleType: YScaleType; xScaleType: XScaleType; isHistogram: boolean; + isPercentage: boolean; // palette will always be set on the expression palette: PaletteOutput; yConfig?: YConfigResult[]; + xAxisId?: string; } export interface ValidLayer extends DataLayerConfigResult { @@ -114,6 +127,7 @@ export interface ExtendedDataLayerArgs { yScaleType: YScaleType; xScaleType: XScaleType; isHistogram: boolean; + isPercentage: boolean; // palette will always be set on the expression palette: PaletteOutput; // palette will always be set on the expression @@ -198,6 +212,7 @@ export interface XYArgs { valuesInLegend?: boolean; ariaLabel?: string; axes?: AxisConfigResult[]; + xAxisConfig?: AxisConfigResult; } export interface LayeredXYArgs { @@ -246,6 +261,7 @@ export interface XYProps { valuesInLegend?: boolean; ariaLabel?: string; axes?: AxisConfigResult[]; + xAxisConfig?: AxisConfigResult; } export interface AnnotationLayerArgs { 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..5226bf0077d58 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, AxisModes } from '../../common/constants'; import { getColorAssignments, getFitOptions, @@ -88,8 +88,6 @@ export const DataLayers: FC = ({ 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( @@ -114,6 +112,11 @@ export const DataLayers: FC = ({ axisConfiguration.series.find((currentSeries) => currentSeries.accessor === accessor) ); + let isPercentage = layer.isPercentage; + if (yAxis?.mode) { + isPercentage = yAxis?.mode === AxisModes.PERCENTAGE; + } + const seriesProps = getSeriesProps({ layer, layerId: layerIndex, 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 4782acdfc86b8..d4a2ac85deeea 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,6 +43,7 @@ import { getAnnotationsLayers, getDataLayers, AxisConfiguration, + getAxisPosition, getAreAlreadyFormattedLayersInfo, getFilteredLayers, getReferenceLayers, @@ -144,6 +145,7 @@ export function XYChart({ yRightExtent, valuesInLegend, axes, + xAxisConfig, } = args; const chartRef = useRef(null); const chartTheme = chartsThemeService.useChartsTheme(); @@ -269,36 +271,37 @@ export function XYChart({ const linesPaddings = getLinesCausedPaddings(visualConfigs, yAxesMap); - const getYAxesStyle = (groupId: 'left' | 'right') => { + const getYAxesStyle = (axis: AxisConfiguration) => { const tickVisible = - groupId === 'right' + axis.showLabels ?? axis.groupId === 'right' ? tickLabelsVisibilitySettings?.yRight : tickLabelsVisibilitySettings?.yLeft; const style = { tickLabel: { + fill: axis.labelColor, visible: tickVisible, rotation: - groupId === 'right' + axis.labelsOrientation ?? axis.groupId === 'right' ? args.labelsOrientation?.yRight || 0 : args.labelsOrientation?.yLeft || 0, padding: - linesPaddings[groupId] != null + linesPaddings[axis.position] != null ? { - inner: linesPaddings[groupId], + inner: linesPaddings[axis.position], } : undefined, }, axisTitle: { visible: - groupId === 'right' + axis.showTitle ?? axis.groupId === 'right' ? axisTitlesVisibilitySettings?.yRight : axisTitlesVisibilitySettings?.yLeft, // if labels are not visible add the padding to the title padding: - !tickVisible && linesPaddings[groupId] != null + !tickVisible && linesPaddings[axis.position] != null ? { - inner: linesPaddings[groupId], + inner: linesPaddings[axis.position], } : undefined, }, @@ -319,6 +322,7 @@ export function XYChart({ }) ); const fit = !hasBarOrArea && extent.mode === 'dataBounds'; + const padding = axis.boundsMargin || undefined; let min: number = NaN; let max: number = NaN; @@ -358,7 +362,7 @@ export function XYChart({ } } - return { fit, min, max }; + return { fit, min, max, padding }; }; const shouldShowValueLabels = @@ -471,6 +475,8 @@ export function XYChart({ const shouldUseNewTimeAxis = isTimeViz && isHistogramModeEnabled && !useLegacyTimeAxis && !shouldRotate; + const defaultXAxisPosition = shouldRotate ? Position.Left : Position.Bottom; + const gridLineStyle = { visible: gridlinesVisibilitySettings?.x, strokeWidth: 1, @@ -480,26 +486,29 @@ export function XYChart({ ...MULTILAYER_TIME_AXIS_STYLE, tickLabel: { ...MULTILAYER_TIME_AXIS_STYLE.tickLabel, - visible: Boolean(tickLabelsVisibilitySettings?.x), + visible: Boolean(xAxisConfig?.showLabels ?? tickLabelsVisibilitySettings?.x), + fill: xAxisConfig?.labelColor, }, tickLine: { ...MULTILAYER_TIME_AXIS_STYLE.tickLine, - visible: Boolean(tickLabelsVisibilitySettings?.x), + visible: Boolean(xAxisConfig?.showLabels ?? tickLabelsVisibilitySettings?.x), }, axisTitle: { - visible: axisTitlesVisibilitySettings.x, + visible: xAxisConfig?.showTitle ?? axisTitlesVisibilitySettings.x, }, } : { tickLabel: { - visible: tickLabelsVisibilitySettings?.x, - rotation: labelsOrientation?.x, + visible: xAxisConfig?.showLabels ?? tickLabelsVisibilitySettings?.x, + rotation: xAxisConfig?.labelsOrientation ?? labelsOrientation?.x, padding: linesPaddings.bottom != null ? { inner: linesPaddings.bottom } : undefined, + fill: xAxisConfig?.labelColor, }, axisTitle: { - visible: axisTitlesVisibilitySettings.x, + visible: xAxisConfig?.showTitle ?? axisTitlesVisibilitySettings.x, padding: - !tickLabelsVisibilitySettings?.x && linesPaddings.bottom != null + !(xAxisConfig?.showLabels ?? tickLabelsVisibilitySettings?.x) && + linesPaddings.bottom != null ? { inner: linesPaddings.bottom } : undefined, }, @@ -562,12 +571,24 @@ export function XYChart({ safeXAccessorLabelRenderer(d)} + hide={xAxisConfig?.hide || dataLayers[0]?.hide || !dataLayers[0]?.xAccessor} + tickFormat={(d) => { + let value = safeXAccessorLabelRenderer(d) || ''; + if (xAxisConfig?.truncate && value.length > xAxisConfig.truncate) { + value = `${value.slice(0, xAxisConfig.truncate)}...`; + } + return value; + }} style={xAxisStyle} + showOverlappingLabels={xAxisConfig?.showOverlappingLabels} + showDuplicatedTicks={xAxisConfig?.showDuplicates} timeAxisLayerCount={shouldUseNewTimeAxis ? 3 : 0} /> @@ -586,9 +607,17 @@ export function XYChart({ : gridlinesVisibilitySettings?.yLeft, }} hide={axis.hide || dataLayers[0]?.hide} - tickFormat={(d) => axis.formatter?.convert(d) || ''} - style={getYAxesStyle(axis.groupId as 'left' | 'right')} + tickFormat={(d) => { + let value = axis.formatter?.convert(d) || ''; + if (axis.truncate && value.length > axis.truncate) { + value = `${value.slice(0, axis.truncate)}...`; + } + return value; + }} + style={getYAxesStyle(axis)} domain={getYAxisDomain(axis)} + showOverlappingLabels={axis.showOverlappingLabels} + showDuplicatedTicks={axis.showDuplicates} ticks={5} /> ); 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 2ca1dd7c56b40..382e6a2b89f23 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 @@ -238,6 +238,7 @@ describe('axes_configuration', () => { xScaleType: 'ordinal', yScaleType: 'linear', isHistogram: false, + isPercentage: false, palette: { type: 'palette', name: 'default' }, table: tables.first, }; 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 c3ef6cde50e51..6ffe2c47cad9e 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 @@ -10,11 +10,13 @@ import { Position } from '@elastic/charts'; import { FormatFactory } from '../types'; import { AxisConfig, + AxisMode, YConfigResult, AxisExtentConfig, CommonXYDataLayerConfigResult, CommonXYReferenceLineLayerConfigResult, } from '../../common'; +import { AxisModes } from '../../common/constants'; import type { IFieldFormat, SerializedFieldFormat, @@ -35,13 +37,11 @@ interface AxesSeries { [key: string]: FormattedMetric[]; } -export interface AxisConfiguration { +export interface AxisConfiguration extends Omit { groupId: string; position: 'left' | 'right' | 'bottom' | 'top'; formatter?: IFieldFormat; series: Series[]; - title?: string; - hide?: boolean; } export type GroupsConfiguration = AxisConfiguration[]; @@ -75,7 +75,7 @@ export function groupAxesByType( ?.meta?.params || { id: 'number' }; if ( isDataLayer(layer) && - layer.seriesType.includes('percentage') && + (layer.isPercentage || axisConfigById?.mode === AxisModes.PERCENTAGE) && formatter.id !== 'percent' ) { formatter = { @@ -137,7 +137,7 @@ export function groupAxesByType( return series; } -function getPosition(position: Position, shouldRotate: boolean) { +export function getAxisPosition(position: Position, shouldRotate: boolean) { if (shouldRotate) { switch (position) { case Position.Bottom: { @@ -172,11 +172,10 @@ export function getAxesConfiguration( if (series[axis.id] && series[axis.id].length > 0) { axisGroups.push({ groupId: `axis-${axis.id}`, - position: getPosition(axis.position || Position.Left, shouldRotate), + position: getAxisPosition(axis.position || Position.Left, shouldRotate), formatter: formatFactory?.(series[axis.id][0].fieldFormat), series: series[axis.id].map(({ fieldFormat, ...currentSeries }) => currentSeries), - title: axis.title, - hide: axis.hide, + ...axis, }); } }); 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..2163378cb801f 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 @@ -54,6 +54,7 @@ describe('color_assignment', () => { yScaleType: 'linear', xScaleType: 'linear', isHistogram: true, + isPercentage: false, seriesType: 'bar', palette: { type: 'palette', name: 'palette1' }, layerType: LayerTypes.DATA, @@ -66,6 +67,7 @@ describe('color_assignment', () => { yScaleType: 'linear', xScaleType: 'linear', isHistogram: true, + isPercentage: false, seriesType: 'bar', palette: { type: 'palette', name: 'palette2' }, layerType: LayerTypes.DATA, 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..2bb668807d3e3 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,6 +24,7 @@ import { } from 'src/plugins/field_formats/common'; import { Datatable, DatatableRow } from '../../../../expressions'; import { CommonXYDataLayerConfigResult, XScaleType } from '../../common'; +import { AxisModes } from '../../common/constants'; import { FormatFactory } from '../types'; import { PaletteRegistry, SeriesLayer } from '../../../../charts/public'; import { getSeriesColor } from './state'; @@ -221,7 +222,11 @@ export const getSeriesProps: GetSeriesPropsFn = ({ }): SeriesSpec => { const { table } = layer; const isStacked = layer.seriesType.includes('stacked'); - const isPercentage = layer.seriesType.includes('percentage'); + const isPercentage = layer.isPercentage; + let stackMode: StackMode | undefined = isPercentage ? AxisModes.PERCENTAGE : undefined; + if (yAxis?.mode) { + stackMode = yAxis?.mode === AxisModes.NORMAL ? undefined : yAxis?.mode; + } const isBarChart = layer.seriesType.includes('bar'); const enableHistogramMode = layer.isHistogram && @@ -285,7 +290,7 @@ export const getSeriesProps: GetSeriesPropsFn = ({ }), groupId: yAxis?.groupId, enableHistogramMode, - stackMode: isPercentage ? StackMode.Percentage : undefined, + stackMode, timeZone, areaSeriesStyle: { point: getPointConfig(layer.xAccessor, emphasizeFitting), 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 d164e86969c83..1727fe211456f 100644 --- a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts +++ b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts @@ -534,6 +534,7 @@ const dataLayerToExpression = ( ], xScaleType: [getScaleType(metadata[layer.layerId][layer.xAccessor], ScaleType.Linear)], isHistogram: [isHistogramDimension], + isPercentage: layer.seriesType.includes('percentage') ? [true] : [], splitAccessor: layer.splitAccessor ? [layer.splitAccessor] : [], yConfig: layer.yConfig ? layer.yConfig.map((yConfig) => yConfigToExpression(yConfig, axes)) From ec42fc09e7fd9b95479081ff28dc07e73b81840f Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Fri, 15 Apr 2022 17:57:42 +0300 Subject: [PATCH 119/213] Some refactoring --- .../expression_xy/common/constants.ts | 14 +- .../axis_titles_visibility_config.ts | 53 -- .../common/expression_functions/data_layer.ts | 14 + .../extended_data_layer.ts | 16 +- ..._y_axis_config.ts => extended_y_config.ts} | 17 +- .../grid_lines_config.test.ts | 20 - .../expression_functions/grid_lines_config.ts | 53 -- .../common/expression_functions/index.ts | 9 +- .../labels_orientation_config.test.ts | 20 - .../labels_orientation_config.ts | 56 --- .../expression_functions/layered_xy_vis.ts | 70 +-- .../tick_labels_config.test.ts | 20 - .../tick_labels_config.ts | 53 -- .../{axis_config.ts => x_axis_config.ts} | 37 +- .../common/expression_functions/xy_vis.ts | 120 ++--- .../expression_functions/y_axis_config.ts | 124 ++++- .../common/expression_functions/y_config.ts | 53 ++ .../expression_xy/common/index.ts | 19 +- .../common/types/expression_functions.ts | 83 +--- .../common/types/expression_renderers.ts | 2 +- .../expression_xy/public/__mocks__/index.tsx | 5 +- .../__snapshots__/xy_chart.test.tsx.snap | 146 +++--- .../public/components/data_layers.tsx | 17 +- .../public/components/legend_action.test.tsx | 3 + .../public/components/xy_chart.test.tsx | 468 ++++++++++++------ .../public/components/xy_chart.tsx | 123 ++--- .../public/definitions/visualizations.ts | 14 +- .../public/helpers/annotations.tsx | 2 +- .../public/helpers/axes_configuration.test.ts | 2 + .../public/helpers/axes_configuration.ts | 53 +- .../public/helpers/color_assignment.test.ts | 4 + .../public/helpers/data_layers.tsx | 12 +- .../public/helpers/reference_lines.ts | 3 +- .../expression_xy/public/helpers/state.ts | 16 +- .../expression_xy/public/plugin.ts | 18 +- .../expression_xy/server/plugin.ts | 18 +- x-pack/plugins/lens/public/index.ts | 5 - .../shared_components/axis_title_settings.tsx | 2 +- .../__snapshots__/to_expression.test.ts.snap | 192 ++++--- .../reference_line_helpers.tsx | 5 +- .../public/xy_visualization/state_helpers.ts | 9 +- .../xy_visualization/to_expression.test.ts | 97 ++-- .../public/xy_visualization/to_expression.ts | 208 ++++---- .../lens/public/xy_visualization/types.ts | 33 +- .../xy_visualization/visualization.test.ts | 2 +- .../public/xy_visualization/visualization.tsx | 10 +- .../visualization_helpers.tsx | 2 +- .../xy_config_panel/axis_settings_popover.tsx | 7 +- .../xy_config_panel/layer_header.tsx | 3 +- .../public/xy_visualization/xy_suggestions.ts | 10 +- 50 files changed, 1105 insertions(+), 1237 deletions(-) delete mode 100644 src/plugins/chart_expressions/expression_xy/common/expression_functions/axis_titles_visibility_config.ts rename src/plugins/chart_expressions/expression_xy/common/expression_functions/{extended_y_axis_config.ts => extended_y_config.ts} (90%) delete mode 100644 src/plugins/chart_expressions/expression_xy/common/expression_functions/grid_lines_config.test.ts delete mode 100644 src/plugins/chart_expressions/expression_xy/common/expression_functions/grid_lines_config.ts delete mode 100644 src/plugins/chart_expressions/expression_xy/common/expression_functions/labels_orientation_config.test.ts delete mode 100644 src/plugins/chart_expressions/expression_xy/common/expression_functions/labels_orientation_config.ts delete mode 100644 src/plugins/chart_expressions/expression_xy/common/expression_functions/tick_labels_config.test.ts delete mode 100644 src/plugins/chart_expressions/expression_xy/common/expression_functions/tick_labels_config.ts rename src/plugins/chart_expressions/expression_xy/common/expression_functions/{axis_config.ts => x_axis_config.ts} (79%) create mode 100644 src/plugins/chart_expressions/expression_xy/common/expression_functions/y_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 4dc9312348a84..a8be79a1597a2 100644 --- a/src/plugins/chart_expressions/expression_xy/common/constants.ts +++ b/src/plugins/chart_expressions/expression_xy/common/constants.ts @@ -9,22 +9,19 @@ export const XY_VIS = 'xyVis'; export const LAYERED_XY_VIS = 'layeredXyVis'; export const Y_CONFIG = 'yConfig'; -export const AXIS_CONFIG = 'axisConfig'; +export const X_AXIS_CONFIG = 'xAxisConfig'; +export const Y_AXIS_CONFIG = 'yAxisConfig'; export const EXTENDED_Y_CONFIG = 'extendedYConfig'; 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 ANNOTATION_LAYER = 'annotationLayer'; -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'; export const LayerTypes = { DATA: 'data', @@ -83,13 +80,6 @@ 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 = { 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 deleted file mode 100644 index 50302214fc37c..0000000000000 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/axis_titles_visibility_config.ts +++ /dev/null @@ -1,53 +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 { 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: i18n.translate('expressionXY.axisTitlesVisibilityConfig.help', { - defaultMessage: `Configure the xy chart's axis titles appearance`, - }), - inputTypes: ['null'], - args: { - x: { - types: ['boolean'], - 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('expressionXY.axisTitlesVisibilityConfig.yLeft.help', { - defaultMessage: 'Specifies whether or not the title of the left y-axis are visible.', - }), - }, - yRight: { - types: ['boolean'], - help: i18n.translate('expressionXY.axisTitlesVisibilityConfig.yRight.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.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_layer.ts index 97f5d1ddde40a..a1301ec2ff25a 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 @@ -61,6 +61,20 @@ export const dataLayerFunction: ExpressionFunctionDefinition< defaultMessage: 'Whether to layout the chart has percentage mode.', }), }, + isStacked: { + types: ['boolean'], + default: false, + help: i18n.translate('expressionXY.dataLayer.isPercentage.help', { + defaultMessage: 'Layout of the chart in stacked mode.', + }), + }, + isHorizontal: { + types: ['boolean'], + default: false, + help: i18n.translate('expressionXY.dataLayer.isPercentage.help', { + defaultMessage: 'Layout of the chart is horizontal', + }), + }, xScaleType: { options: [...Object.values(XScaleTypes)], help: i18n.translate('expressionXY.dataLayer.xScaleType.help', { 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 d5a1dde8b69ca..70db89ab6b716 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 @@ -66,7 +66,21 @@ export const extendedDataLayerFunction: ExpressionFunctionDefinition< types: ['boolean'], default: false, help: i18n.translate('expressionXY.dataLayer.isPercentage.help', { - defaultMessage: 'Whether to layout the chart has percentage mode', + defaultMessage: 'Layout of the chart in percentage mode', + }), + }, + isStacked: { + types: ['boolean'], + default: false, + help: i18n.translate('expressionXY.dataLayer.isPercentage.help', { + defaultMessage: 'Layout of the chart in stacked mode', + }), + }, + isHorizontal: { + types: ['boolean'], + default: false, + help: i18n.translate('expressionXY.dataLayer.isPercentage.help', { + defaultMessage: 'Layout of the chart is horizontal', }), }, isHistogram: { 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_config.ts similarity index 90% rename from src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_y_axis_config.ts rename to src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_y_config.ts index 08e8cbf576cf5..3f6520ad45d67 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_config.ts @@ -14,11 +14,10 @@ import { FillStyles, IconPositions, LineStyles, - YAxisModes, } from '../constants'; import { ExtendedYConfig, ExtendedYConfigResult } from '../types'; -export const extendedYAxisConfigFunction: ExpressionFunctionDefinition< +export const extendedYConfigFunction: ExpressionFunctionDefinition< typeof EXTENDED_Y_CONFIG, null, ExtendedYConfig, @@ -38,14 +37,6 @@ export const extendedYAxisConfigFunction: ExpressionFunctionDefinition< 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', { @@ -96,6 +87,12 @@ export const extendedYAxisConfigFunction: ExpressionFunctionDefinition< }), strict: true, }, + axisId: { + types: ['string'], + help: i18n.translate('expressionXY.yConfig.axisId.help', { + defaultMessage: 'An optional id of axis', + }), + }, }, fn(input, args) { return { 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 deleted file mode 100644 index 91bfbc8fbe6f0..0000000000000 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/grid_lines_config.test.ts +++ /dev/null @@ -1,20 +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 { 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/grid_lines_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/grid_lines_config.ts deleted file mode 100644 index b94b8b5709c03..0000000000000 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/grid_lines_config.ts +++ /dev/null @@ -1,53 +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 '../../../../../../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: i18n.translate('expressionXY.gridlinesConfig.help', { - defaultMessage: `Configure the xy chart's gridlines appearance`, - }), - inputTypes: ['null'], - args: { - x: { - types: ['boolean'], - 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('expressionXY.gridlinesConfig.yLeft.help', { - defaultMessage: 'Specifies whether or not the gridlines of the left y-axis are visible.', - }), - }, - yRight: { - types: ['boolean'], - help: i18n.translate('expressionXY.gridlinesConfig.yRight.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 index 090695b17c07a..efa0144220699 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 @@ -11,15 +11,12 @@ export * from './layered_xy_vis'; export * from './legend_config'; export * from './annotation_layer'; export * from './extended_annotation_layer'; +export * from './y_config'; export * from './y_axis_config'; -export * from './axis_config'; -export * from './extended_y_axis_config'; +export * from './x_axis_config'; +export * from './extended_y_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'; export * from './extended_reference_line_layer'; -export * from './axis_titles_visibility_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 deleted file mode 100644 index 2d54a729d3e5a..0000000000000 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/labels_orientation_config.test.ts +++ /dev/null @@ -1,20 +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 { 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/labels_orientation_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/labels_orientation_config.ts deleted file mode 100644 index 94d726c56f3b2..0000000000000 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/labels_orientation_config.ts +++ /dev/null @@ -1,56 +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 '../../../../../../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: 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('expressionXY.labelsOrientationConfig.x.help', { - defaultMessage: 'Specifies the labels orientation of the x-axis.', - }), - }, - yLeft: { - types: ['number'], - options: [0, -90, -45], - 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('expressionXY.labelsOrientationConfig.yRight.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/layered_xy_vis.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis.ts index 4c332cb9e0c51..19ac4d698c6f3 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 @@ -14,13 +14,9 @@ import { LEGEND_CONFIG, ValueLabelModes, FittingFunctions, - GRID_LINES_CONFIG, XY_VIS_RENDERER, - AXIS_CONFIG, - AXIS_EXTENT_CONFIG, - TICK_LABELS_CONFIG, - LABELS_ORIENTATION_CONFIG, - AXIS_TITLES_VISIBILITY_CONFIG, + X_AXIS_CONFIG, + Y_AXIS_CONFIG, EXTENDED_DATA_LAYER, EXTENDED_REFERENCE_LINE_LAYER, LAYERED_XY_VIS, @@ -42,36 +38,6 @@ export const layeredXyVisFunction: ExpressionFunctionDefinition< defaultMessage: 'An X/Y chart', }), 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', { @@ -106,30 +72,6 @@ export const layeredXyVisFunction: ExpressionFunctionDefinition< }), 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', - }), - }, layers: { types: [EXTENDED_DATA_LAYER, EXTENDED_REFERENCE_LINE_LAYER, EXTENDED_ANNOTATION_LAYER], help: i18n.translate('expressionXY.layeredXyVis.layers.help', { @@ -173,12 +115,18 @@ export const layeredXyVisFunction: ExpressionFunctionDefinition< required: false, }, axes: { - types: [AXIS_CONFIG], + types: [Y_AXIS_CONFIG], help: i18n.translate('expressionXY.layeredXyVis.axes.help', { defaultMessage: 'Specifies the configs for axes', }), multi: true, }, + xAxisConfig: { + types: [X_AXIS_CONFIG], + help: i18n.translate('expressionXY.xyVis.xAxisConfig.help', { + defaultMessage: 'Specifies the configs for x-axis', + }), + }, }, fn(data, args, handlers) { const layers = (args.layers ?? []).filter( 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 deleted file mode 100644 index 8b31258377dd9..0000000000000 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/tick_labels_config.test.ts +++ /dev/null @@ -1,20 +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 { 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/tick_labels_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/tick_labels_config.ts deleted file mode 100644 index 6a882094a56d2..0000000000000 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/tick_labels_config.ts +++ /dev/null @@ -1,53 +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 '../../../../../../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: i18n.translate('expressionXY.tickLabelsConfig.help', { - defaultMessage: `Configure the xy chart's tick labels appearance`, - }), - inputTypes: ['null'], - args: { - x: { - types: ['boolean'], - 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('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('expressionXY.tickLabelsConfig.yRight.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/axis_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/x_axis_config.ts similarity index 79% rename from src/plugins/chart_expressions/expression_xy/common/expression_functions/axis_config.ts rename to src/plugins/chart_expressions/expression_xy/common/expression_functions/x_axis_config.ts index 6f5888cd8d84d..9d16ad15e0cc7 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/axis_config.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/x_axis_config.ts @@ -8,18 +8,18 @@ import { i18n } from '@kbn/i18n'; import type { ExpressionFunctionDefinition } from '../../../../expressions/common'; -import { AxisConfig, AxisConfigResult } from '../types'; -import { AXIS_CONFIG, AxisModes } from '../constants'; +import { AxisConfig, XAxisConfigResult } from '../types'; +import { X_AXIS_CONFIG } from '../constants'; -export const axisConfigFunction: ExpressionFunctionDefinition< - typeof AXIS_CONFIG, +export const xAxisConfigFunction: ExpressionFunctionDefinition< + typeof X_AXIS_CONFIG, null, AxisConfig, - AxisConfigResult + XAxisConfigResult > = { - name: AXIS_CONFIG, + name: X_AXIS_CONFIG, aliases: [], - type: AXIS_CONFIG, + type: X_AXIS_CONFIG, help: i18n.translate('expressionXY.axisConfig.help', { defaultMessage: `Configure the xy chart's axis config`, }), @@ -36,7 +36,6 @@ export const axisConfigFunction: ExpressionFunctionDefinition< help: i18n.translate('expressionXY.axisConfig.id.help', { defaultMessage: 'Id of axis', }), - required: true, }, position: { types: ['string'], @@ -51,19 +50,6 @@ export const axisConfigFunction: ExpressionFunctionDefinition< defaultMessage: 'Hide the specified axis', }), }, - mode: { - types: ['string'], - options: [...Object.values(AxisModes)], - help: i18n.translate('expressionXY.axisConfig.mode.help', { - defaultMessage: 'Scale mode. Can be normal, percentage, wiggle or silhouette', - }), - }, - boundsMargin: { - types: ['number'], - help: i18n.translate('expressionXY.axisConfig.boundsMargin.help', { - defaultMessage: 'Margin of bounds', - }), - }, labelColor: { types: ['string'], help: i18n.translate('expressionXY.axisConfig.labelColor.help', { @@ -82,6 +68,13 @@ export const axisConfigFunction: ExpressionFunctionDefinition< defaultMessage: 'Show duplicated ticks', }), }, + showGridLines: { + types: ['boolean'], + help: i18n.translate('expressionXY.axisConfig.showGridLines.help', { + defaultMessage: 'Specifies whether or not the gridlines of the axis are visible.', + }), + default: false, + }, labelsOrientation: { types: ['number'], options: [0, -90, -45], @@ -112,7 +105,7 @@ export const axisConfigFunction: ExpressionFunctionDefinition< }, fn(input, args) { return { - type: AXIS_CONFIG, + type: X_AXIS_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 77dd61a965891..9812f6dc9b8bb 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 @@ -14,7 +14,7 @@ import { XYArgs, XYLayerConfigResult, XYRender, - AxisConfigResult, + YAxisConfigResult, } from '../types'; import { XY_VIS, @@ -23,24 +23,21 @@ import { 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, - AXIS_CONFIG, + X_AXIS_CONFIG, + Y_AXIS_CONFIG, AxisModes, AxisExtentModes, + SeriesTypes, } from '../constants'; import { Dimension, prepareLogTable } from '../../../../visualizations/common/utils'; import { getLayerDimensions } from '../utils'; -function validateLayerMode(layers: DataLayerConfigResult[], axes: AxisConfigResult[]) { +function validateLayerMode(layers: DataLayerConfigResult[], axes: YAxisConfigResult[]) { const isError = layers.some( (layer) => layer.isPercentage && @@ -82,26 +79,34 @@ const errors = { }), }; -const validateExtent = ( - extent: AxisExtentConfigResult, - hasBarOrArea: boolean, - dataLayers: DataLayerConfigResult[] -) => { +function areValidBounds(extent: AxisExtentConfigResult) { 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; + return isValidLowerBound && isValidUpperBound; +} - if (hasBarOrArea && extent.mode === AxisExtentModes.CUSTOM && !areValidBounds) { - throw new Error(errors.extendBoundsAreInvalidError()); - } +const validateExtents = (dataLayers: DataLayerConfigResult[], axes?: YAxisConfigResult[]) => { + const lineSeries = dataLayers.filter(({ seriesType }) => seriesType === SeriesTypes.LINE); + const hasBarOrArea = + dataLayers.filter( + ({ seriesType }) => seriesType === SeriesTypes.BAR || seriesType === SeriesTypes.AREA + ).length > 0; + axes?.forEach((axis) => { + if ( + hasBarOrArea && + axis.extent?.mode === AxisExtentModes.CUSTOM && + !areValidBounds(axis.extent) + ) { + 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()); - } + if (!lineSeries.length && axis.extent?.mode === AxisExtentModes.DATA_BOUNDS) { + throw new Error(errors.dataBoundsForNotLineChartError()); + } + }); }; export const xyVisFunction: ExpressionFunctionDefinition< @@ -117,38 +122,6 @@ export const xyVisFunction: ExpressionFunctionDefinition< defaultMessage: 'An X/Y chart', }), 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', { @@ -186,30 +159,6 @@ export const xyVisFunction: ExpressionFunctionDefinition< 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', - }), - }, dataLayers: { types: [DATA_LAYER], help: i18n.translate('expressionXY.xyVis.dataLayer.help', { @@ -265,13 +214,13 @@ export const xyVisFunction: ExpressionFunctionDefinition< }), }, xAxisConfig: { - types: [AXIS_CONFIG], + types: [X_AXIS_CONFIG], help: i18n.translate('expressionXY.xyVis.xAxisConfig.help', { defaultMessage: 'Specifies the configs for x-axis', }), }, axes: { - types: [AXIS_CONFIG], + types: [Y_AXIS_CONFIG], help: i18n.translate('expressionXY.xyVis.axes.help', { defaultMessage: 'Specifies the configs for y-axes', }), @@ -305,19 +254,20 @@ export const xyVisFunction: ExpressionFunctionDefinition< 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; + const hasBar = dataLayers.filter(({ seriesType }) => seriesType === SeriesTypes.BAR).length > 0; + const hasArea = + dataLayers.filter(({ seriesType }) => seriesType === SeriesTypes.AREA).length > 0; - validateExtent(args.yLeftExtent, hasBar || hasArea, dataLayers); - validateExtent(args.yRightExtent, hasBar || hasArea, dataLayers); + validateExtents(dataLayers, args.axes); if (!hasArea && args.fillOpacity !== undefined) { throw new Error(errors.notUsedFillOpacityError()); } const hasNotHistogramBars = - dataLayers.filter(({ seriesType, isHistogram }) => seriesType.includes('bar') && !isHistogram) - .length > 0; + dataLayers.filter( + ({ seriesType, isHistogram }) => seriesType === SeriesTypes.BAR && !isHistogram + ).length > 0; if ((!hasBar || !hasNotHistogramBars) && args.valueLabels !== ValueLabelModes.HIDE) { throw new Error(errors.valueLabelsForNotBarsOrHistogramBarsChartsError()); 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 f6021fde70723..4154d5da987fc 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,39 +8,131 @@ import { i18n } from '@kbn/i18n'; import type { ExpressionFunctionDefinition } from '../../../../expressions/common'; -import { Y_CONFIG } from '../constants'; -import { YConfig, YConfigResult } from '../types'; +import { YAxisConfig, YAxisConfigResult } from '../types'; +import { Y_AXIS_CONFIG, AxisModes, AXIS_EXTENT_CONFIG, YScaleTypes } from '../constants'; export const yAxisConfigFunction: ExpressionFunctionDefinition< - typeof Y_CONFIG, + typeof Y_AXIS_CONFIG, null, - YConfig, - YConfigResult + YAxisConfig, + YAxisConfigResult > = { - name: Y_CONFIG, + name: Y_AXIS_CONFIG, aliases: [], - type: Y_CONFIG, - help: i18n.translate('expressionXY.yConfig.help', { - defaultMessage: `Configure the behavior of a xy chart's y axis metric`, + type: Y_AXIS_CONFIG, + help: i18n.translate('expressionXY.axisConfig.help', { + defaultMessage: `Configure the xy chart's axis config`, }), inputTypes: ['null'], args: { - forAccessor: { + title: { types: ['string'], - help: i18n.translate('expressionXY.yConfig.forAccessor.help', { - defaultMessage: 'The accessor this configuration is for', + help: i18n.translate('expressionXY.axisConfig.title.help', { + defaultMessage: 'Title of axis', }), }, - color: { + id: { types: ['string'], - help: i18n.translate('expressionXY.yConfig.color.help', { - defaultMessage: 'The color of the series', + help: i18n.translate('expressionXY.axisConfig.id.help', { + defaultMessage: 'Id of axis', }), }, + position: { + types: ['string'], + help: i18n.translate('expressionXY.axisConfig.position.help', { + defaultMessage: 'Position of the axis', + }), + default: 'left', + }, + hide: { + types: ['boolean'], + help: i18n.translate('expressionXY.axisConfig.boolean.help', { + defaultMessage: 'Hide the specified axis', + }), + }, + mode: { + types: ['string'], + options: [...Object.values(AxisModes)], + help: i18n.translate('expressionXY.axisConfig.mode.help', { + defaultMessage: 'Scale mode. Can be normal, percentage, wiggle or silhouette', + }), + }, + boundsMargin: { + types: ['number'], + help: i18n.translate('expressionXY.axisConfig.boundsMargin.help', { + defaultMessage: 'Margin of bounds', + }), + }, + labelColor: { + types: ['string'], + help: i18n.translate('expressionXY.axisConfig.labelColor.help', { + defaultMessage: 'Color of the axis labels', + }), + }, + showOverlappingLabels: { + types: ['boolean'], + help: i18n.translate('expressionXY.axisConfig.showOverlappingLabels.help', { + defaultMessage: 'Show overlapping labels', + }), + }, + showDuplicates: { + types: ['boolean'], + help: i18n.translate('expressionXY.axisConfig.showDuplicates.help', { + defaultMessage: 'Show duplicated ticks', + }), + }, + showGridLines: { + types: ['boolean'], + help: i18n.translate('expressionXY.axisConfig.showGridLines.help', { + defaultMessage: 'Specifies whether or not the gridlines of the axis are visible.', + }), + default: false, + }, + labelsOrientation: { + types: ['number'], + options: [0, -90, -45], + help: i18n.translate('expressionXY.axisConfig.labelsOrientation.help', { + defaultMessage: 'Specifies the labels orientation of the axis.', + }), + }, + showLabels: { + types: ['boolean'], + help: i18n.translate('expressionXY.axisConfig.showLabels.help', { + defaultMessage: 'Show labels', + }), + default: true, + }, + showTitle: { + types: ['boolean'], + help: i18n.translate('expressionXY.axisConfig.showTitle.help', { + defaultMessage: 'Show title of the axis', + }), + default: true, + }, + truncate: { + types: ['number'], + help: i18n.translate('expressionXY.axisConfig.truncate.help', { + defaultMessage: 'The number of symbols before truncating', + }), + }, + extent: { + types: [AXIS_EXTENT_CONFIG], + help: i18n.translate('expressionXY.yAxisConfig.extent.help', { + defaultMessage: 'Axis extents', + }), + default: `{${AXIS_EXTENT_CONFIG}}`, + }, + scaleType: { + options: [...Object.values(YScaleTypes)], + help: i18n.translate('expressionXY.yAxisConfig.scaleType.help', { + defaultMessage: 'The scale type of the axis', + }), + default: YScaleTypes.LINEAR, + }, }, fn(input, args) { return { - type: Y_CONFIG, + type: Y_AXIS_CONFIG, ...args, }; }, diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/y_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/y_config.ts new file mode 100644 index 0000000000000..ebeefd4902229 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/y_config.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 { ExpressionFunctionDefinition } from '../../../../expressions/common'; +import { Y_CONFIG } from '../constants'; +import { YConfig, YConfigResult } from '../types'; + +export const yConfigFunction: ExpressionFunctionDefinition< + typeof Y_CONFIG, + null, + YConfig, + YConfigResult +> = { + 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`, + }), + inputTypes: ['null'], + args: { + forAccessor: { + types: ['string'], + help: i18n.translate('expressionXY.yConfig.forAccessor.help', { + defaultMessage: 'The accessor this configuration is for', + }), + }, + color: { + types: ['string'], + help: i18n.translate('expressionXY.yConfig.color.help', { + defaultMessage: 'The color of the series', + }), + }, + axisId: { + types: ['string'], + help: i18n.translate('expressionXY.yConfig.axisId.help', { + defaultMessage: 'An optional id of axis', + }), + }, + }, + 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 8747c2e6a1145..36ef76cf3e9f8 100755 --- a/src/plugins/chart_expressions/expression_xy/common/index.ts +++ b/src/plugins/chart_expressions/expression_xy/common/index.ts @@ -11,22 +11,19 @@ export const PLUGIN_NAME = 'expressionXy'; export { xyVisFunction, - axisConfigFunction, - layeredXyVisFunction, + xAxisConfigFunction, yAxisConfigFunction, - extendedYAxisConfigFunction, + layeredXyVisFunction, + yConfigFunction, + extendedYConfigFunction, legendConfigFunction, - gridlinesConfigFunction, dataLayerFunction, annotationLayerFunction, extendedAnnotationLayerFunction, extendedDataLayerFunction, axisExtentConfigFunction, - tickLabelsConfigFunction, - labelsOrientationConfigFunction, referenceLineLayerFunction, extendedReferenceLineLayerFunction, - axisTitlesVisibilityConfigFunction, } from './expression_functions'; export type { @@ -42,6 +39,7 @@ export type { XScaleType, AxisMode, AxisConfig, + YAxisConfig, ValidLayer, XYLayerArgs, XYCurveType, @@ -58,25 +56,22 @@ export type { CollectiveConfig, LegendConfigResult, AxesSettingsConfig, + XAxisConfigResult, + YAxisConfigResult, AnnotationLayerArgs, XYLayerConfigResult, ExtendedYConfigResult, - GridlinesConfigResult, DataLayerConfigResult, - TickLabelsConfigResult, AxisExtentConfigResult, ReferenceLineLayerArgs, - LabelsOrientationConfig, CommonXYLayerConfigResult, AvailableReferenceLineIcon, XYExtendedLayerConfigResult, AnnotationLayerConfigResult, ExtendedAnnotationLayerArgs, ExtendedDataLayerConfigResult, - LabelsOrientationConfigResult, CommonXYDataLayerConfigResult, ReferenceLineLayerConfigResult, - AxisTitlesVisibilityConfigResult, ExtendedAnnotationLayerConfigResult, CommonXYAnnotationLayerConfigResult, ExtendedReferenceLineLayerConfigResult, 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 29c2f55c8a2ad..2158b56561b66 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 @@ -27,10 +27,6 @@ import { AxisModes, 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, @@ -39,7 +35,8 @@ import { ANNOTATION_LAYER, EndValues, EXTENDED_ANNOTATION_LAYER, - AXIS_CONFIG, + X_AXIS_CONFIG, + Y_AXIS_CONFIG, EXTENDED_Y_CONFIG, AvailableReferenceLineIcons, } from '../constants'; @@ -60,7 +57,6 @@ export type FittingFunction = $Values; export type AvailableReferenceLineIcon = $Values; export interface AxesSettingsConfig { - x: boolean; yLeft: boolean; yRight: boolean; } @@ -74,10 +70,8 @@ export interface AxisExtentConfig { export interface AxisConfig { title?: string; hide?: boolean; - id: string; + id?: string; position?: Position; - mode?: AxisMode; - boundsMargin?: number; labelColor?: string; showOverlappingLabels?: boolean; showDuplicates?: boolean; @@ -85,6 +79,14 @@ export interface AxisConfig { truncate?: number; showLabels?: boolean; showTitle?: boolean; + showGridLines?: boolean; +} + +export interface YAxisConfig extends AxisConfig { + mode?: AxisMode; + boundsMargin?: number; + extent?: AxisExtentConfigResult; + scaleType?: YScaleType; } export interface ExtendedYConfig extends YConfig { @@ -94,12 +96,12 @@ export interface ExtendedYConfig extends YConfig { fill?: FillStyle; iconPosition?: IconPosition; textVisibility?: boolean; - axisId?: string; } export interface YConfig { forAccessor: string; color?: string; + axisId?: string; } export interface DataLayerArgs { @@ -113,6 +115,8 @@ export interface DataLayerArgs { xScaleType: XScaleType; isHistogram: boolean; isPercentage: boolean; + isStacked: boolean; + isHorizontal: boolean; // palette will always be set on the expression palette: PaletteOutput; yConfig?: YConfigResult[]; @@ -134,6 +138,8 @@ export interface ExtendedDataLayerArgs { xScaleType: XScaleType; isHistogram: boolean; isPercentage: boolean; + isStacked: boolean; + isHorizontal: boolean; // palette will always be set on the expression palette: PaletteOutput; // palette will always be set on the expression @@ -187,19 +193,8 @@ export interface LegendConfig { legendSize?: number; } -export interface LabelsOrientationConfig { - x: number; - yLeft: number; - yRight: number; -} - // Arguments to XY chart expression, with computed properties export interface XYArgs { - xTitle: string; - yTitle: string; - yRightTitle: string; - yLeftExtent: AxisExtentConfigResult; - yRightExtent: AxisExtentConfigResult; legend: LegendConfigResult; endValue?: EndValue; emphasizeFitting?: boolean; @@ -208,66 +203,45 @@ export interface XYArgs { referenceLineLayers: ReferenceLineLayerConfigResult[]; annotationLayers: AnnotationLayerConfigResult[]; fittingFunction?: FittingFunction; - axisTitlesVisibilitySettings?: AxisTitlesVisibilityConfigResult; - tickLabelsVisibilitySettings?: TickLabelsConfigResult; - gridlinesVisibilitySettings?: GridlinesConfigResult; - labelsOrientation?: LabelsOrientationConfigResult; curveType?: XYCurveType; fillOpacity?: number; hideEndzones?: boolean; valuesInLegend?: boolean; ariaLabel?: string; - axes?: AxisConfigResult[]; - xAxisConfig?: AxisConfigResult; + axes?: YAxisConfigResult[]; + xAxisConfig?: XAxisConfigResult; } export interface LayeredXYArgs { - xTitle: string; - yTitle: string; - yRightTitle: string; - yLeftExtent: AxisExtentConfigResult; - yRightExtent: AxisExtentConfigResult; legend: LegendConfigResult; endValue?: EndValue; emphasizeFitting?: boolean; valueLabels: ValueLabelMode; layers?: XYExtendedLayerConfigResult[]; fittingFunction?: FittingFunction; - axisTitlesVisibilitySettings?: AxisTitlesVisibilityConfigResult; - tickLabelsVisibilitySettings?: TickLabelsConfigResult; - gridlinesVisibilitySettings?: GridlinesConfigResult; - labelsOrientation?: LabelsOrientationConfigResult; curveType?: XYCurveType; fillOpacity?: number; hideEndzones?: boolean; valuesInLegend?: boolean; ariaLabel?: string; - axes?: AxisConfigResult[]; + axes?: YAxisConfigResult[]; + xAxisConfig?: XAxisConfigResult; } export interface XYProps { - xTitle: string; - yTitle: string; - yRightTitle: string; - yLeftExtent: AxisExtentConfigResult; - yRightExtent: AxisExtentConfigResult; legend: LegendConfigResult; valueLabels: ValueLabelMode; layers: CommonXYLayerConfigResult[]; endValue?: EndValue; emphasizeFitting?: boolean; fittingFunction?: FittingFunction; - axisTitlesVisibilitySettings?: AxisTitlesVisibilityConfigResult; - tickLabelsVisibilitySettings?: TickLabelsConfigResult; - gridlinesVisibilitySettings?: GridlinesConfigResult; - labelsOrientation?: LabelsOrientationConfigResult; curveType?: XYCurveType; fillOpacity?: number; hideEndzones?: boolean; valuesInLegend?: boolean; ariaLabel?: string; - axes?: AxisConfigResult[]; - xAxisConfig?: AxisConfigResult; + axes?: YAxisConfigResult[]; + xAxisConfig?: XAxisConfigResult; } export interface AnnotationLayerArgs { @@ -356,20 +330,11 @@ export type ExtendedDataLayerConfigResult = Omit { roundedTimestamp: number; - axisMode: 'bottom'; + position: 'bottom'; icon?: AvailableAnnotationIcon | string; customTooltipDetails?: AnnotationTooltipFormatter | undefined; } 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 f5a52ce8329c5..1959385435e72 100644 --- a/src/plugins/chart_expressions/expression_xy/public/__mocks__/index.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/__mocks__/index.tsx @@ -176,8 +176,11 @@ export const dateHistogramLayer: DataLayerConfigResult = { yScaleType: 'linear', xScaleType: 'time', isHistogram: true, + isStacked: true, + isPercentage: false, + isHorizontal: false, splitAccessor: 'splitAccessorId', - seriesType: 'bar_stacked', + seriesType: 'bar', accessors: ['yAccessorId'], palette: mockPaletteOutput, table: dateHistogramData.tables.timeLayer, 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 a1e89a790cd28..d2269bbff4eb4 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 @@ -261,7 +261,7 @@ exports[`XYChart component it renders area 1`] = ` gridLine={ Object { "strokeWidth": 1, - "visible": true, + "visible": undefined, } } hide={false} @@ -271,12 +271,13 @@ exports[`XYChart component it renders area 1`] = ` Object { "axisTitle": Object { "padding": undefined, - "visible": true, + "visible": undefined, }, "tickLabel": Object { + "fill": undefined, "padding": undefined, - "rotation": 0, - "visible": true, + "rotation": undefined, + "visible": undefined, }, } } @@ -290,11 +291,12 @@ exports[`XYChart component it renders area 1`] = ` "fit": false, "max": NaN, "min": NaN, + "padding": undefined, } } gridLine={ Object { - "visible": false, + "visible": undefined, } } groupId="left" @@ -305,12 +307,13 @@ exports[`XYChart component it renders area 1`] = ` Object { "axisTitle": Object { "padding": undefined, - "visible": true, + "visible": undefined, }, "tickLabel": Object { + "fill": undefined, "padding": undefined, - "rotation": -90, - "visible": false, + "rotation": undefined, + "visible": undefined, }, } } @@ -662,7 +665,7 @@ exports[`XYChart component it renders bar 1`] = ` gridLine={ Object { "strokeWidth": 1, - "visible": true, + "visible": undefined, } } hide={false} @@ -672,12 +675,13 @@ exports[`XYChart component it renders bar 1`] = ` Object { "axisTitle": Object { "padding": undefined, - "visible": true, + "visible": undefined, }, "tickLabel": Object { + "fill": undefined, "padding": undefined, - "rotation": 0, - "visible": true, + "rotation": undefined, + "visible": undefined, }, } } @@ -691,11 +695,12 @@ exports[`XYChart component it renders bar 1`] = ` "fit": false, "max": NaN, "min": NaN, + "padding": undefined, } } gridLine={ Object { - "visible": false, + "visible": undefined, } } groupId="left" @@ -706,12 +711,13 @@ exports[`XYChart component it renders bar 1`] = ` Object { "axisTitle": Object { "padding": undefined, - "visible": true, + "visible": undefined, }, "tickLabel": Object { + "fill": undefined, "padding": undefined, - "rotation": -90, - "visible": false, + "rotation": undefined, + "visible": undefined, }, } } @@ -1063,7 +1069,7 @@ exports[`XYChart component it renders horizontal bar 1`] = ` gridLine={ Object { "strokeWidth": 1, - "visible": true, + "visible": undefined, } } hide={false} @@ -1073,12 +1079,13 @@ exports[`XYChart component it renders horizontal bar 1`] = ` Object { "axisTitle": Object { "padding": undefined, - "visible": true, + "visible": undefined, }, "tickLabel": Object { + "fill": undefined, "padding": undefined, - "rotation": 0, - "visible": true, + "rotation": undefined, + "visible": undefined, }, } } @@ -1092,11 +1099,12 @@ exports[`XYChart component it renders horizontal bar 1`] = ` "fit": false, "max": NaN, "min": NaN, + "padding": undefined, } } gridLine={ Object { - "visible": false, + "visible": undefined, } } groupId="left" @@ -1107,12 +1115,13 @@ exports[`XYChart component it renders horizontal bar 1`] = ` Object { "axisTitle": Object { "padding": undefined, - "visible": true, + "visible": undefined, }, "tickLabel": Object { + "fill": undefined, "padding": undefined, - "rotation": -90, - "visible": false, + "rotation": undefined, + "visible": undefined, }, } } @@ -1276,13 +1285,14 @@ exports[`XYChart component it renders horizontal bar 1`] = ` ], "columnToLabel": "{\\"a\\": \\"Label A\\", \\"b\\": \\"Label B\\", \\"d\\": \\"Label D\\"}", "isHistogram": false, + "isHorizontal": true, "layerType": "data", "palette": Object { "name": "mock", "params": Object {}, "type": "palette", }, - "seriesType": "bar_horizontal", + "seriesType": "bar", "splitAccessor": "d", "table": Object { "columns": Array [ @@ -1464,7 +1474,7 @@ exports[`XYChart component it renders line 1`] = ` gridLine={ Object { "strokeWidth": 1, - "visible": true, + "visible": undefined, } } hide={false} @@ -1474,12 +1484,13 @@ exports[`XYChart component it renders line 1`] = ` Object { "axisTitle": Object { "padding": undefined, - "visible": true, + "visible": undefined, }, "tickLabel": Object { + "fill": undefined, "padding": undefined, - "rotation": 0, - "visible": true, + "rotation": undefined, + "visible": undefined, }, } } @@ -1493,11 +1504,12 @@ exports[`XYChart component it renders line 1`] = ` "fit": false, "max": NaN, "min": NaN, + "padding": undefined, } } gridLine={ Object { - "visible": false, + "visible": undefined, } } groupId="left" @@ -1508,12 +1520,13 @@ exports[`XYChart component it renders line 1`] = ` Object { "axisTitle": Object { "padding": undefined, - "visible": true, + "visible": undefined, }, "tickLabel": Object { + "fill": undefined, "padding": undefined, - "rotation": -90, - "visible": false, + "rotation": undefined, + "visible": undefined, }, } } @@ -1865,7 +1878,7 @@ exports[`XYChart component it renders stacked area 1`] = ` gridLine={ Object { "strokeWidth": 1, - "visible": true, + "visible": undefined, } } hide={false} @@ -1875,12 +1888,13 @@ exports[`XYChart component it renders stacked area 1`] = ` Object { "axisTitle": Object { "padding": undefined, - "visible": true, + "visible": undefined, }, "tickLabel": Object { + "fill": undefined, "padding": undefined, - "rotation": 0, - "visible": true, + "rotation": undefined, + "visible": undefined, }, } } @@ -1894,11 +1908,12 @@ exports[`XYChart component it renders stacked area 1`] = ` "fit": false, "max": NaN, "min": NaN, + "padding": undefined, } } gridLine={ Object { - "visible": false, + "visible": undefined, } } groupId="left" @@ -1909,12 +1924,13 @@ exports[`XYChart component it renders stacked area 1`] = ` Object { "axisTitle": Object { "padding": undefined, - "visible": true, + "visible": undefined, }, "tickLabel": Object { + "fill": undefined, "padding": undefined, - "rotation": -90, - "visible": false, + "rotation": undefined, + "visible": undefined, }, } } @@ -2078,13 +2094,14 @@ exports[`XYChart component it renders stacked area 1`] = ` ], "columnToLabel": "{\\"a\\": \\"Label A\\", \\"b\\": \\"Label B\\", \\"d\\": \\"Label D\\"}", "isHistogram": false, + "isStacked": true, "layerType": "data", "palette": Object { "name": "mock", "params": Object {}, "type": "palette", }, - "seriesType": "area_stacked", + "seriesType": "area", "splitAccessor": "d", "table": Object { "columns": Array [ @@ -2266,7 +2283,7 @@ exports[`XYChart component it renders stacked bar 1`] = ` gridLine={ Object { "strokeWidth": 1, - "visible": true, + "visible": undefined, } } hide={false} @@ -2276,12 +2293,13 @@ exports[`XYChart component it renders stacked bar 1`] = ` Object { "axisTitle": Object { "padding": undefined, - "visible": true, + "visible": undefined, }, "tickLabel": Object { + "fill": undefined, "padding": undefined, - "rotation": 0, - "visible": true, + "rotation": undefined, + "visible": undefined, }, } } @@ -2295,11 +2313,12 @@ exports[`XYChart component it renders stacked bar 1`] = ` "fit": false, "max": NaN, "min": NaN, + "padding": undefined, } } gridLine={ Object { - "visible": false, + "visible": undefined, } } groupId="left" @@ -2310,12 +2329,13 @@ exports[`XYChart component it renders stacked bar 1`] = ` Object { "axisTitle": Object { "padding": undefined, - "visible": true, + "visible": undefined, }, "tickLabel": Object { + "fill": undefined, "padding": undefined, - "rotation": -90, - "visible": false, + "rotation": undefined, + "visible": undefined, }, } } @@ -2479,13 +2499,14 @@ exports[`XYChart component it renders stacked bar 1`] = ` ], "columnToLabel": "{\\"a\\": \\"Label A\\", \\"b\\": \\"Label B\\", \\"d\\": \\"Label D\\"}", "isHistogram": false, + "isStacked": true, "layerType": "data", "palette": Object { "name": "mock", "params": Object {}, "type": "palette", }, - "seriesType": "bar_stacked", + "seriesType": "bar", "splitAccessor": "d", "table": Object { "columns": Array [ @@ -2667,7 +2688,7 @@ exports[`XYChart component it renders stacked horizontal bar 1`] = ` gridLine={ Object { "strokeWidth": 1, - "visible": true, + "visible": undefined, } } hide={false} @@ -2677,12 +2698,13 @@ exports[`XYChart component it renders stacked horizontal bar 1`] = ` Object { "axisTitle": Object { "padding": undefined, - "visible": true, + "visible": undefined, }, "tickLabel": Object { + "fill": undefined, "padding": undefined, - "rotation": 0, - "visible": true, + "rotation": undefined, + "visible": undefined, }, } } @@ -2696,11 +2718,12 @@ exports[`XYChart component it renders stacked horizontal bar 1`] = ` "fit": false, "max": NaN, "min": NaN, + "padding": undefined, } } gridLine={ Object { - "visible": false, + "visible": undefined, } } groupId="left" @@ -2711,12 +2734,13 @@ exports[`XYChart component it renders stacked horizontal bar 1`] = ` Object { "axisTitle": Object { "padding": undefined, - "visible": true, + "visible": undefined, }, "tickLabel": Object { + "fill": undefined, "padding": undefined, - "rotation": -90, - "visible": false, + "rotation": undefined, + "visible": undefined, }, } } @@ -2880,13 +2904,15 @@ exports[`XYChart component it renders stacked horizontal bar 1`] = ` ], "columnToLabel": "{\\"a\\": \\"Label A\\", \\"b\\": \\"Label B\\", \\"d\\": \\"Label D\\"}", "isHistogram": false, + "isHorizontal": true, + "isStacked": true, "layerType": "data", "palette": Object { "name": "mock", "params": Object {}, "type": "palette", }, - "seriesType": "bar_horizontal_stacked", + "seriesType": "bar", "splitAccessor": "d", "table": Object { "columns": Array [ 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 eec9aa2c25359..427bdde458315 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 @@ -149,11 +149,6 @@ export const DataLayers: FC = ({ /> ); 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 @@ -170,22 +165,12 @@ export const DataLayers: FC = ({ }, }; 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.test.tsx b/src/plugins/chart_expressions/expression_xy/public/components/legend_action.test.tsx index c62d0fda94960..0c786df18b8ba 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 @@ -157,6 +157,9 @@ const sampleLayer: DataLayerConfigResult = { type: 'dataLayer', layerType: LayerTypes.DATA, seriesType: 'line', + isStacked: false, + isPercentage: false, + isHorizontal: false, xAccessor: 'c', accessors: ['a', 'b'], splitAccessor: 'splitAccessorId', 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 3e937ddf222aa..f3fd10cf49b7c 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 @@ -140,6 +140,9 @@ describe('XYChart component', () => { type: 'dataLayer', layerType: LayerTypes.DATA, seriesType: 'line', + isPercentage: false, + isHorizontal: false, + isStacked: false, xAccessor: 'c', accessors: ['a', 'b'], splitAccessor: 'd', @@ -236,6 +239,9 @@ describe('XYChart component', () => { type: 'dataLayer', layerType: LayerTypes.DATA, seriesType: 'line', + isHorizontal: false, + isStacked: false, + isPercentage: false, xAccessor: 'c', accessors: ['a', 'b'], splitAccessor: 'd', @@ -321,7 +327,8 @@ describe('XYChart component', () => { test('it should enable the new time axis for a stacked vertical bar with break down dimension', () => { const timeLayer: DataLayerConfigResult = { ...defaultTimeLayer, - seriesType: 'bar_stacked', + seriesType: 'bar', + isStacked: true, }; const timeLayerArgs = createArgsWithLayers([timeLayer]); @@ -488,12 +495,18 @@ describe('XYChart component', () => { {...defaultProps} args={{ ...args, - yLeftExtent: { - type: 'axisExtentConfig', - mode: 'custom', - lowerBound: 123, - upperBound: 456, - }, + axes: [ + { + type: 'yAxisConfig', + position: 'left', + extent: { + type: 'axisExtentConfig', + mode: 'custom', + lowerBound: 123, + upperBound: 456, + }, + }, + ], }} /> ); @@ -510,10 +523,16 @@ describe('XYChart component', () => { {...defaultProps} args={{ ...args, - yLeftExtent: { - type: 'axisExtentConfig', - mode: 'dataBounds', - }, + axes: [ + { + type: 'yAxisConfig', + position: 'left', + extent: { + type: 'axisExtentConfig', + mode: 'dataBounds', + }, + }, + ], }} /> ); @@ -530,10 +549,16 @@ describe('XYChart component', () => { {...defaultProps} args={{ ...args, - yLeftExtent: { - type: 'axisExtentConfig', - mode: 'dataBounds', - }, + axes: [ + { + type: 'yAxisConfig', + position: 'left', + extent: { + type: 'axisExtentConfig', + mode: 'dataBounds', + }, + }, + ], layers: [ { ...(args.layers[0] as DataLayerConfigResult), @@ -569,12 +594,18 @@ describe('XYChart component', () => { {...defaultProps} args={{ ...refArgs, - yLeftExtent: { - type: 'axisExtentConfig', - mode: 'custom', - lowerBound: 123, - upperBound: 456, - }, + axes: [ + { + type: 'yAxisConfig', + position: 'left', + extent: { + type: 'axisExtentConfig', + mode: 'custom', + lowerBound: 123, + upperBound: 456, + }, + }, + ], }} /> ); @@ -720,7 +751,9 @@ describe('XYChart component', () => { {...defaultProps} args={{ ...args, - layers: [{ ...(args.layers[0] as DataLayerConfigResult), seriesType: 'bar_horizontal' }], + layers: [ + { ...(args.layers[0] as DataLayerConfigResult), isHorizontal: true, seriesType: 'bar' }, + ], }} /> ); @@ -815,7 +848,10 @@ describe('XYChart component', () => { yScaleType: 'linear', xScaleType: 'linear', isHistogram: true, - seriesType: 'bar_stacked', + isHorizontal: false, + isStacked: true, + seriesType: 'bar', + isPercentage: false, accessors: ['yAccessorId'], palette: mockPaletteOutput, table: numberHistogramData, @@ -885,7 +921,10 @@ describe('XYChart component', () => { type: 'dataLayer', layerType: LayerTypes.DATA, isHistogram: true, - seriesType: 'bar_stacked', + seriesType: 'bar', + isStacked: true, + isHorizontal: false, + isPercentage: false, xAccessor: 'b', yScaleType: 'linear', xScaleType: 'time', @@ -1011,7 +1050,10 @@ describe('XYChart component', () => { yScaleType: 'linear', xScaleType: 'linear', isHistogram: true, - seriesType: 'bar_stacked', + isPercentage: false, + seriesType: 'bar', + isStacked: true, + isHorizontal: false, accessors: ['yAccessorId'], palette: mockPaletteOutput, table: numberHistogramData, @@ -1083,6 +1125,9 @@ describe('XYChart component', () => { type: 'dataLayer', layerType: LayerTypes.DATA, seriesType: 'line', + isHorizontal: false, + isStacked: false, + isPercentage: false, xAccessor: 'd', accessors: ['a', 'b'], columnToLabel: '{"a": "Label A", "b": "Label B", "d": "Label D"}', @@ -1141,6 +1186,9 @@ describe('XYChart component', () => { type: 'dataLayer', layerType: LayerTypes.DATA, seriesType: 'line', + isHorizontal: false, + isStacked: false, + isPercentage: false, xAccessor: 'd', accessors: ['a', 'b'], columnToLabel: '{"a": "Label A", "b": "Label B", "d": "Label D"}', @@ -1171,6 +1219,9 @@ describe('XYChart component', () => { type: 'dataLayer', layerType: LayerTypes.DATA, seriesType: 'line', + isHorizontal: false, + isStacked: false, + isPercentage: false, xAccessor: 'd', accessors: ['a', 'b'], columnToLabel: '{"a": "Label A", "b": "Label B", "d": "Label D"}', @@ -1211,7 +1262,9 @@ describe('XYChart component', () => { {...defaultProps} args={{ ...args, - layers: [{ ...(args.layers[0] as DataLayerConfigResult), seriesType: 'bar_stacked' }], + layers: [ + { ...(args.layers[0] as DataLayerConfigResult), seriesType: 'bar', isStacked: true }, + ], }} /> ); @@ -1230,7 +1283,9 @@ describe('XYChart component', () => { {...defaultProps} args={{ ...args, - layers: [{ ...(args.layers[0] as DataLayerConfigResult), seriesType: 'area_stacked' }], + layers: [ + { ...(args.layers[0] as DataLayerConfigResult), seriesType: 'area', isStacked: true }, + ], }} /> ); @@ -1250,7 +1305,12 @@ describe('XYChart component', () => { args={{ ...args, layers: [ - { ...(args.layers[0] as DataLayerConfigResult), seriesType: 'bar_horizontal_stacked' }, + { + ...(args.layers[0] as DataLayerConfigResult), + seriesType: 'bar', + isStacked: true, + isHorizontal: true, + }, ], }} /> @@ -1277,7 +1337,8 @@ describe('XYChart component', () => { ...(args.layers[0] as DataLayerConfigResult), xAccessor: undefined, splitAccessor: 'e', - seriesType: 'bar_stacked', + seriesType: 'bar', + isStacked: true, }, ], }} @@ -1364,7 +1425,8 @@ describe('XYChart component', () => { layers: [ { ...(args.layers[0] as DataLayerConfigResult), - seriesType: 'bar_stacked', + seriesType: 'bar', + isStacked: true, isHistogram: true, }, ], @@ -1437,12 +1499,12 @@ describe('XYChart component', () => { ], axes: [ { - type: 'axisConfig', + type: 'yAxisConfig', id: '1', position: 'left', }, { - type: 'axisConfig', + type: 'yAxisConfig', id: '2', position: 'right', }, @@ -1503,7 +1565,7 @@ describe('XYChart component', () => { ], axes: [ { - type: 'axisConfig', + type: 'yAxisConfig', id: '1', position: 'left', }, @@ -1932,11 +1994,20 @@ describe('XYChart component', () => { test('it should set the tickLabel visibility on the x axis if the tick labels is hidden', () => { const { args } = sampleArgs(); - args.tickLabelsVisibilitySettings = { - x: false, - yLeft: true, - yRight: true, - type: 'tickLabelsConfig', + args.axes = [{ + type: 'yAxisConfig', + position: 'left', + showLabels: true + }, { + type: 'yAxisConfig', + position: 'right', + showLabels: true + }]; + + args.xAxisConfig = { + type: 'xAxisConfig', + id: 'x', + showLabels: false, }; const instance = shallow(); @@ -1953,12 +2024,15 @@ describe('XYChart component', () => { test('it should set the tickLabel visibility on the y axis if the tick labels is hidden', () => { const { args } = sampleArgs(); - args.tickLabelsVisibilitySettings = { - x: true, - yLeft: false, - yRight: false, - type: 'tickLabelsConfig', - }; + args.axes = [{ + type: 'yAxisConfig', + position: 'left', + showLabels: false + }, { + type: 'yAxisConfig', + position: 'right', + showLabels: false + }]; const instance = shallow(); @@ -1974,11 +2048,20 @@ describe('XYChart component', () => { test('it should set the tickLabel visibility on the x axis if the tick labels is shown', () => { const { args } = sampleArgs(); - args.tickLabelsVisibilitySettings = { - x: true, - yLeft: true, - yRight: true, - type: 'tickLabelsConfig', + args.axes = [{ + type: 'yAxisConfig', + position: 'left', + showLabels: true + }, { + type: 'yAxisConfig', + position: 'right', + showLabels: true + }]; + + args.xAxisConfig = { + type: 'xAxisConfig', + id: 'x', + showLabels: true, }; const instance = shallow(); @@ -1995,11 +2078,21 @@ describe('XYChart component', () => { test('it should set the tickLabel orientation on the x axis', () => { const { args } = sampleArgs(); - args.labelsOrientation = { - x: -45, - yLeft: 0, - yRight: -90, - type: 'labelsOrientationConfig', + args.axes = [{ + type: 'yAxisConfig', + position: 'left', + labelsOrientation: 0 + }, { + type: 'yAxisConfig', + position: 'right', + labelsOrientation: -90 + }]; + + args.xAxisConfig = { + type: 'xAxisConfig', + id: 'x', + showLabels: true, + labelsOrientation: -45, }; const instance = shallow(); @@ -2016,11 +2109,20 @@ describe('XYChart component', () => { test('it should set the tickLabel visibility on the y axis if the tick labels is shown', () => { const { args } = sampleArgs(); - args.tickLabelsVisibilitySettings = { - x: false, - yLeft: true, - yRight: true, - type: 'tickLabelsConfig', + args.axes = [{ + type: 'yAxisConfig', + position: 'left', + showLabels: true + }, { + type: 'yAxisConfig', + position: 'right', + showLabels: true + }]; + + args.xAxisConfig = { + type: 'xAxisConfig', + id: 'x', + showLabels: true, }; const instance = shallow(); @@ -2037,11 +2139,21 @@ describe('XYChart component', () => { test('it should set the tickLabel orientation on the y axis', () => { const { args } = sampleArgs(); - args.labelsOrientation = { - x: -45, - yLeft: -90, - yRight: -90, - type: 'labelsOrientationConfig', + args.axes = [{ + type: 'yAxisConfig', + position: 'left', + labelsOrientation: -90 + }, { + type: 'yAxisConfig', + position: 'right', + labelsOrientation: -90 + }]; + + args.xAxisConfig = { + type: 'xAxisConfig', + id: 'x', + showLabels: true, + labelsOrientation: -45, }; const instance = shallow(); @@ -2083,42 +2195,46 @@ describe('XYChart component', () => { }; const args: XYProps = { - xTitle: '', - yTitle: '', - yRightTitle: '', legend: { type: 'legendConfig', isVisible: false, position: Position.Top }, valueLabels: 'hide', - tickLabelsVisibilitySettings: { - type: 'tickLabelsConfig', - x: true, - yLeft: true, - yRight: true, - }, - gridlinesVisibilitySettings: { - type: 'gridlinesConfig', - x: true, - yLeft: false, - yRight: false, - }, - labelsOrientation: { - type: 'labelsOrientationConfig', - x: 0, - yLeft: 0, - yRight: 0, - }, - yLeftExtent: { - mode: 'full', + axes: [{ + type: 'yAxisConfig', + position: 'left', + labelsOrientation: 0, + showGridLines: false, + showLabels: true, + title: '', + extent: { + mode: 'full', type: 'axisExtentConfig', - }, - yRightExtent: { - mode: 'full', + } + }, { + type: 'yAxisConfig', + position: 'right', + labelsOrientation: 0, + showGridLines: false, + showLabels: true, + title: '', + extent: { + mode: 'full', type: 'axisExtentConfig', + } + }], + xAxisConfig: { + type: 'xAxisConfig', + id: 'x', + title: '', + showLabels: true, + showGridLines: true, + labelsOrientation: 0, }, layers: [ { type: 'dataLayer', layerType: LayerTypes.DATA, seriesType: 'line', + isStacked: false, + isHorizontal: false, xAccessor: 'a', accessors: ['c'], splitAccessor: 'b', @@ -2126,6 +2242,7 @@ describe('XYChart component', () => { xScaleType: 'ordinal', yScaleType: 'linear', isHistogram: false, + isPercentage: false, palette: mockPaletteOutput, table: data1, }, @@ -2140,6 +2257,9 @@ describe('XYChart component', () => { xScaleType: 'ordinal', yScaleType: 'linear', isHistogram: false, + isStacked: false, + isHorizontal: false, + isPercentage: false, palette: mockPaletteOutput, table: data2, }, @@ -2170,36 +2290,38 @@ describe('XYChart component', () => { }; const args: XYProps = { - xTitle: '', - yTitle: '', - yRightTitle: '', legend: { type: 'legendConfig', isVisible: false, position: Position.Top }, valueLabels: 'hide', - tickLabelsVisibilitySettings: { - type: 'tickLabelsConfig', - x: true, - yLeft: false, - yRight: false, - }, - gridlinesVisibilitySettings: { - type: 'gridlinesConfig', - x: true, - yLeft: false, - yRight: false, - }, - labelsOrientation: { - type: 'labelsOrientationConfig', - x: 0, - yLeft: 0, - yRight: 0, - }, - yLeftExtent: { - mode: 'full', + axes: [{ + type: 'yAxisConfig', + position: 'left', + labelsOrientation: 0, + showGridLines: false, + showLabels: false, + title: '', + extent: { + mode: 'full', type: 'axisExtentConfig', - }, - yRightExtent: { - mode: 'full', + } + }, { + type: 'yAxisConfig', + position: 'right', + labelsOrientation: 0, + showGridLines: false, + showLabels: false, + title: '', + extent: { + mode: 'full', type: 'axisExtentConfig', + } + }], + xAxisConfig: { + type: 'xAxisConfig', + id: 'x', + title: '', + showLabels: true, + showGridLines: true, + labelsOrientation: 0, }, layers: [ { @@ -2213,6 +2335,9 @@ describe('XYChart component', () => { xScaleType: 'ordinal', yScaleType: 'linear', isHistogram: false, + isStacked: false, + isHorizontal: false, + isPercentage: false, palette: mockPaletteOutput, table: data, }, @@ -2241,36 +2366,38 @@ describe('XYChart component', () => { }; const args: XYProps = { - xTitle: '', - yTitle: '', - yRightTitle: '', legend: { type: 'legendConfig', isVisible: true, position: Position.Top }, valueLabels: 'hide', - tickLabelsVisibilitySettings: { - type: 'tickLabelsConfig', - x: true, - yLeft: false, - yRight: false, - }, - gridlinesVisibilitySettings: { - type: 'gridlinesConfig', - x: true, - yLeft: false, - yRight: false, - }, - labelsOrientation: { - type: 'labelsOrientationConfig', - x: 0, - yLeft: 0, - yRight: 0, - }, - yLeftExtent: { - mode: 'full', + axes: [{ + type: 'yAxisConfig', + position: 'left', + labelsOrientation: 0, + showGridLines: false, + showLabels: false, + title: '', + extent: { + mode: 'full', type: 'axisExtentConfig', - }, - yRightExtent: { - mode: 'full', + } + }, { + type: 'yAxisConfig', + position: 'right', + labelsOrientation: 0, + showGridLines: false, + showLabels: false, + title: '', + extent: { + mode: 'full', type: 'axisExtentConfig', + } + }], + xAxisConfig: { + type: 'xAxisConfig', + id: 'x', + showLabels: true, + showGridLines: true, + labelsOrientation: 0, + title: '', }, layers: [ { @@ -2284,6 +2411,9 @@ describe('XYChart component', () => { xScaleType: 'ordinal', yScaleType: 'linear', isHistogram: false, + isStacked: false, + isHorizontal: false, + isPercentage: false, palette: mockPaletteOutput, table: data, }, @@ -2389,7 +2519,7 @@ describe('XYChart component', () => { { ...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 }, + { ...sampleLayer, seriesType: 'area', isStacked: true, accessors: ['a'], table: data }, ]); const component = shallow( @@ -2419,7 +2549,12 @@ describe('XYChart component', () => { test('it should apply the xTitle if is specified', () => { const { args } = sampleArgs(); - args.xTitle = 'My custom x-axis title'; + args.xAxisConfig = { + type: 'xAxisConfig', + id: 'x', + showLabels: true, + title: 'My custom x-axis title', + }; const component = shallow(); @@ -2429,11 +2564,22 @@ describe('XYChart component', () => { test('it should hide the X axis title if the corresponding switch is off', () => { const { args } = sampleArgs(); - args.axisTitlesVisibilitySettings = { - x: false, - yLeft: true, - yRight: true, - type: 'axisTitlesVisibilityConfig', + args.axes = [{ + type: 'yAxisConfig', + position: 'left', + showTitle: true + }, { + type: 'yAxisConfig', + position: 'right', + showTitle: true + }]; + + args.xAxisConfig = { + type: 'xAxisConfig', + id: 'x', + showLabels: true, + showTitle: false, + title: 'My custom x-axis title', }; const component = shallow(); @@ -2450,11 +2596,22 @@ describe('XYChart component', () => { test('it should show the X axis gridlines if the setting is on', () => { const { args } = sampleArgs(); - args.gridlinesVisibilitySettings = { - x: true, - yLeft: false, - yRight: false, - type: 'gridlinesConfig', + args.axes = [{ + type: 'yAxisConfig', + position: 'left', + showGridLines: false + }, { + type: 'yAxisConfig', + position: 'right', + showGridLines: false + }]; + + args.xAxisConfig = { + type: 'xAxisConfig', + id: 'x', + showLabels: true, + showGridLines: true, + title: 'My custom x-axis title', }; const component = shallow(); @@ -2497,11 +2654,14 @@ describe('XYChart component', () => { type: 'dataLayer', layerType: LayerTypes.DATA, seriesType: 'line', + isStacked: false, + isHorizontal: false, xAccessor: 'c', accessors: ['a', 'b'], xScaleType: 'ordinal', yScaleType: 'linear', isHistogram: false, + isPercentage: false, palette: mockPaletteOutput, table: data, }; 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 b792d0ac1d05e..39dac025c2df5 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 @@ -30,7 +30,7 @@ import { PaletteRegistry } from '@kbn/coloring'; import { RenderMode } from '../../../../expressions/common'; import { EmptyPlaceholder } from '../../../../../plugins/charts/public'; import type { FilterEvent, BrushEvent, FormatFactory } from '../types'; -import type { SeriesType, XYChartProps } from '../../common/types'; +import type { XYChartProps } from '../../common/types'; import { EventAnnotationServiceType } from '../../../../event_annotation/public'; import { ChartsPluginSetup, @@ -52,7 +52,7 @@ import { GroupsConfiguration, computeOverallDataDomain, getLinesCausedPaddings, - getAxisConfig, + getAxisGroupConfig, } from '../helpers'; import { getXDomain, XyEndzones } from './x_domain'; import { getLegendAction } from './legend_action'; @@ -111,8 +111,16 @@ function getValueLabelsStyling(isHorizontal: boolean): { }; } -function getIconForSeriesType(seriesType: SeriesType): IconType { - return visualizationDefinitions.find((c) => c.id === seriesType)!.icon || 'empty'; +function getIconForSeriesType(layer: CommonXYDataLayerConfigResult): IconType { + return ( + visualizationDefinitions.find( + (c) => + c.id === + `${layer.seriesType}${layer.isHorizontal ? '_horizontal' : ''}${ + layer.isPercentage ? '_percentage' : '' + }${layer.isStacked ? '_stacked' : ''}` + )!.icon || 'empty' + ); } export const XYChartReportable = React.memo(XYChart); @@ -137,11 +145,8 @@ export function XYChart({ fittingFunction, endValue, emphasizeFitting, - gridlinesVisibilitySettings, valueLabels, hideEndzones, - yLeftExtent, - yRightExtent, valuesInLegend, axes, xAxisConfig, @@ -164,9 +169,7 @@ export function XYChart({ }); if (filteredLayers.length === 0) { - const icon: IconType = getIconForSeriesType( - getDataLayers(layers)?.[0]?.seriesType || SeriesTypes.BAR - ); + const icon: IconType = getIconForSeriesType(getDataLayers(layers)?.[0]); return ; } @@ -197,21 +200,9 @@ export function XYChart({ formatFactory ); - const xTitle = args.xTitle || (xAxisColumn && xAxisColumn.name); - const axisTitlesVisibilitySettings = args.axisTitlesVisibilitySettings || { - x: true, - yLeft: true, - yRight: true, - }; - const tickLabelsVisibilitySettings = args.tickLabelsVisibilitySettings || { - x: true, - yLeft: true, - yRight: true, - }; - - const labelsOrientation = args.labelsOrientation || { x: 0, yLeft: 0, yRight: 0 }; + const xTitle = xAxisConfig?.title || (xAxisColumn && xAxisColumn.name); - const filteredBarLayers = dataLayers.filter((layer) => layer.seriesType.includes('bar')); + const filteredBarLayers = dataLayers.filter(({ seriesType }) => seriesType === SeriesTypes.BAR); const chartHasMoreThanOneBarSeries = filteredBarLayers.length > 1 || @@ -229,14 +220,22 @@ export function XYChart({ ); const yAxesMap = { - left: yAxesConfiguration.find(({ groupId }) => groupId === 'left'), - right: yAxesConfiguration.find(({ groupId }) => groupId === 'right'), + left: yAxesConfiguration.find(({ position }) => position === 'left'), + right: yAxesConfiguration.find(({ position }) => position === 'right'), + }; + + const axisTitlesVisibilitySettings = { + yLeft: yAxesMap?.left?.showTitle ?? true, + yRight: yAxesMap?.right?.showTitle ?? true, + }; + const tickLabelsVisibilitySettings = { + yLeft: yAxesMap?.left?.showLabels ?? true, + yRight: yAxesMap?.right?.showLabels ?? true, }; const getYAxesTitles = (axis: AxisConfiguration) => { - const yTitle = axis.title || (axis.groupId === 'right' ? args.yRightTitle : args.yTitle); return ( - yTitle || + axis.title || axis.series .map( (series) => @@ -263,7 +262,7 @@ export function XYChart({ .flatMap(({ yConfig }) => yConfig) .map((config) => ({ ...config, - position: getAxisConfig(yAxesConfiguration, config)?.position, + position: getAxisGroupConfig(yAxesConfiguration, config)?.position, })), ...groupedAnnotations, ].filter(Boolean); @@ -271,19 +270,13 @@ export function XYChart({ const linesPaddings = getLinesCausedPaddings(visualConfigs, yAxesMap); const getYAxesStyle = (axis: AxisConfiguration) => { - const tickVisible = - axis.showLabels ?? axis.groupId === 'right' - ? tickLabelsVisibilitySettings?.yRight - : tickLabelsVisibilitySettings?.yLeft; + const tickVisible = axis.showLabels; const style = { tickLabel: { fill: axis.labelColor, visible: tickVisible, - rotation: - axis.labelsOrientation ?? axis.groupId === 'right' - ? args.labelsOrientation?.yRight || 0 - : args.labelsOrientation?.yLeft || 0, + rotation: axis.labelsOrientation, padding: linesPaddings[axis.position] != null ? { @@ -292,10 +285,7 @@ export function XYChart({ : undefined, }, axisTitle: { - visible: - axis.showTitle ?? axis.groupId === 'right' - ? axisTitlesVisibilitySettings?.yRight - : axisTitlesVisibilitySettings?.yLeft, + visible: axis.showTitle, // if labels are not visible add the padding to the title padding: !tickVisible && linesPaddings[axis.position] != null @@ -309,7 +299,9 @@ export function XYChart({ }; const getYAxisDomain = (axis: GroupsConfiguration[number]) => { - const extent = axis.groupId === 'left' ? yLeftExtent : yRightExtent; + const extent = axis.extent || { + mode: 'full', + }; const hasBarOrArea = Boolean( axis.series.some((series) => { const layer = layersById[series.layer]; @@ -317,7 +309,7 @@ export function XYChart({ return false; } - return layer.seriesType.includes('bar') || layer.seriesType.includes('area'); + return layer.seriesType === SeriesTypes.BAR || layer.seriesType === SeriesTypes.AREA; }) ); const fit = !hasBarOrArea && extent.mode === 'dataBounds'; @@ -329,7 +321,7 @@ export function XYChart({ max = extent.upperBound ?? NaN; } else { const axisHasReferenceLine = referenceLineLayers.some(({ yConfig }) => - yConfig?.some((config) => Boolean(getAxisConfig([axis], config))) + yConfig?.some((config) => Boolean(getAxisGroupConfig([axis], config))) ); if (!fit && axisHasReferenceLine) { // Remove this once the chart will support automatic annotation fit for other type of charts @@ -344,7 +336,7 @@ export function XYChart({ } for (const { yConfig, table } of referenceLineLayers) { for (const config of yConfig || []) { - if (Boolean(getAxisConfig([axis], config))) { + if (Boolean(getAxisGroupConfig([axis], config))) { for (const row of table.rows) { const value = row[config.forAccessor]; // keep the 0 in view @@ -362,7 +354,7 @@ export function XYChart({ const shouldShowValueLabels = // No stacked bar charts - dataLayers.every((layer) => !layer.seriesType.includes('stacked')) && + dataLayers.every((layer) => !layer.isStacked) && // No histogram charts !isHistogramViz; @@ -462,11 +454,8 @@ export function XYChart({ } as LegendPositionConfig; const isHistogramModeEnabled = dataLayers.some( - ({ isHistogram, seriesType }) => - isHistogram && - (seriesType.includes('stacked') || - !seriesType.includes('bar') || - !chartHasMoreThanOneBarSeries) + ({ isHistogram, seriesType, isStacked }) => + isHistogram && (isStacked || seriesType !== SeriesTypes.BAR || !chartHasMoreThanOneBarSeries) ); const shouldUseNewTimeAxis = @@ -475,7 +464,7 @@ export function XYChart({ const defaultXAxisPosition = shouldRotate ? Position.Left : Position.Bottom; const gridLineStyle = { - visible: gridlinesVisibilitySettings?.x, + visible: xAxisConfig?.showGridLines, strokeWidth: 1, }; const xAxisStyle: RecursivePartial = shouldUseNewTimeAxis @@ -483,29 +472,28 @@ export function XYChart({ ...MULTILAYER_TIME_AXIS_STYLE, tickLabel: { ...MULTILAYER_TIME_AXIS_STYLE.tickLabel, - visible: Boolean(xAxisConfig?.showLabels ?? tickLabelsVisibilitySettings?.x), + visible: Boolean(xAxisConfig?.showLabels), fill: xAxisConfig?.labelColor, }, tickLine: { ...MULTILAYER_TIME_AXIS_STYLE.tickLine, - visible: Boolean(xAxisConfig?.showLabels ?? tickLabelsVisibilitySettings?.x), + visible: Boolean(xAxisConfig?.showLabels), }, axisTitle: { - visible: xAxisConfig?.showTitle ?? axisTitlesVisibilitySettings.x, + visible: xAxisConfig?.showTitle, }, } : { tickLabel: { - visible: xAxisConfig?.showLabels ?? tickLabelsVisibilitySettings?.x, - rotation: xAxisConfig?.labelsOrientation ?? labelsOrientation?.x, + visible: xAxisConfig?.showLabels, + rotation: xAxisConfig?.labelsOrientation, padding: linesPaddings.bottom != null ? { inner: linesPaddings.bottom } : undefined, fill: xAxisConfig?.labelColor, }, axisTitle: { - visible: xAxisConfig?.showTitle ?? axisTitlesVisibilitySettings.x, + visible: xAxisConfig?.showTitle, padding: - !(xAxisConfig?.showLabels ?? tickLabelsVisibilitySettings?.x) && - linesPaddings.bottom != null + !xAxisConfig?.showLabels && linesPaddings.bottom != null ? { inner: linesPaddings.bottom } : undefined, }, @@ -539,8 +527,8 @@ export function XYChart({ ...chartTheme.chartPaddings, ...computeChartMargins( linesPaddings, - tickLabelsVisibilitySettings, - axisTitlesVisibilitySettings, + { ...tickLabelsVisibilitySettings, x: xAxisConfig?.showLabels }, + { ...axisTitlesVisibilitySettings, x: xAxisConfig?.showTitle }, yAxesMap, shouldRotate ), @@ -598,10 +586,7 @@ export function XYChart({ position={axis.position} title={getYAxesTitles(axis)} gridLine={{ - visible: - axis.groupId === 'right' - ? gridlinesVisibilitySettings?.yRight - : gridlinesVisibilitySettings?.yLeft, + visible: axis.showGridLines, }} hide={axis.hide || dataLayers[0]?.hide} tickFormat={(d) => { @@ -628,9 +613,9 @@ export function XYChart({ histogramMode={dataLayers.every( (layer) => layer.isHistogram && - (layer.seriesType.includes('stacked') || !layer.splitAccessor) && - (layer.seriesType.includes('stacked') || - !layer.seriesType.includes('bar') || + (layer.isStacked || !layer.splitAccessor) && + (layer.isStacked || + layer.seriesType !== SeriesTypes.BAR || !chartHasMoreThanOneBarSeries) )} /> 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 9cd1540c7bbeb..d16d8204846fc 100644 --- a/src/plugins/chart_expressions/expression_xy/public/definitions/visualizations.ts +++ b/src/plugins/chart_expressions/expression_xy/public/definitions/visualizations.ts @@ -22,13 +22,13 @@ import { export const visualizationDefinitions = [ { id: SeriesTypes.BAR, icon: BarIcon }, - { id: SeriesTypes.BAR_STACKED, icon: BarStackedIcon }, - { id: SeriesTypes.BAR_HORIZONTAL, icon: BarHorizontalIcon }, - { id: SeriesTypes.BAR_PERCENTAGE_STACKED, icon: BarPercentageIcon }, - { id: SeriesTypes.BAR_HORIZONTAL_STACKED, icon: BarHorizontalStackedIcon }, - { id: SeriesTypes.BAR_HORIZONTAL_PERCENTAGE_STACKED, icon: BarHorizontalPercentageIcon }, + { id: `${SeriesTypes.BAR}_stacked`, icon: BarStackedIcon }, + { id: `${SeriesTypes.BAR}_horizontal`, icon: BarHorizontalIcon }, + { id: `${SeriesTypes.BAR}_percentage_stacked`, icon: BarPercentageIcon }, + { id: `${SeriesTypes.BAR}_horizontal_stacked`, icon: BarHorizontalStackedIcon }, + { id: `${SeriesTypes.BAR}_horizontal_percentage_stacked`, icon: BarHorizontalPercentageIcon }, { id: SeriesTypes.LINE, icon: LineIcon }, { id: SeriesTypes.AREA, icon: AreaIcon }, - { id: SeriesTypes.AREA_STACKED, icon: AreaStackedIcon }, - { id: SeriesTypes.AREA_PERCENTAGE_STACKED, icon: AreaPercentageIcon }, + { id: `${SeriesTypes.AREA}_stacked`, icon: AreaStackedIcon }, + { id: `${SeriesTypes.AREA}_percentage_stacked`, icon: AreaPercentageIcon }, ]; 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 3b649d097365d..ae07e0b6ef530 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/annotations.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/annotations.tsx @@ -16,7 +16,7 @@ import { hasIcon, iconSet } from './icon'; export const LINES_MARKER_SIZE = 20; type PartialExtendedYConfig = Pick & { - position: Position; + position?: Position; }; type PartialCollectiveConfig = Pick; 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 382e6a2b89f23..a0788a11845fb 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 @@ -239,6 +239,8 @@ describe('axes_configuration', () => { yScaleType: 'linear', isHistogram: false, isPercentage: false, + isStacked: false, + isHorizontal: false, palette: { type: 'palette', name: 'default' }, table: tables.first, }; 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 3d352caebcf04..9c59dc316eca4 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,8 +9,8 @@ import { Position } from '@elastic/charts'; import { FormatFactory } from '../types'; import { - AxisConfig, - AxisExtentConfig, + YAxisConfig, + ExtendedYConfigResult, CommonXYDataLayerConfigResult, CommonXYReferenceLineLayerConfigResult, ExtendedYConfig, @@ -37,7 +37,7 @@ interface AxesSeries { [key: string]: FormattedMetric[]; } -export interface AxisConfiguration extends Omit { +export interface AxisConfiguration extends Omit { groupId: string; position: 'left' | 'right' | 'bottom' | 'top'; formatter?: IFieldFormat; @@ -55,7 +55,7 @@ export function isFormatterCompatible( export function groupAxesByType( layers: Array, - axes?: AxisConfig[] + axes?: YAxisConfig[] ) { const series: AxesSeries = { auto: [], @@ -69,7 +69,7 @@ export function groupAxesByType( const yConfig: Array | undefined = layer.yConfig; const yConfigByAccessor = yConfig?.find((config) => config.forAccessor === accessor); const axisConfigById = axes?.find( - (axis) => yConfigByAccessor?.axisId && axis.id === yConfigByAccessor?.axisId + (axis) => yConfigByAccessor?.axisId && axis.id && axis.id === yConfigByAccessor?.axisId ); const key = axisConfigById?.id || 'auto'; let formatter: SerializedFieldFormat = table.columns?.find((column) => column.id === accessor) @@ -159,70 +159,67 @@ export function getAxisPosition(position: Position, shouldRotate: boolean) { return position; } +function axisGlobalConfig(position: Position, axes?: YAxisConfig[]) { + return axes?.find((axis) => !axis.id && axis.position === position) || {}; +} + export function getAxesConfiguration( layers: Array, shouldRotate: boolean, - axes?: AxisConfig[], + axes?: YAxisConfig[], formatFactory?: FormatFactory ): GroupsConfiguration { const series = groupAxesByType(layers, axes); const axisGroups: GroupsConfiguration = []; + let position: Position; axes?.forEach((axis) => { - if (series[axis.id] && series[axis.id].length > 0) { + if (axis.id && series[axis.id] && series[axis.id].length > 0) { + position = getAxisPosition(axis.position || Position.Left, shouldRotate); axisGroups.push({ groupId: `axis-${axis.id}`, - position: getAxisPosition(axis.position || Position.Left, shouldRotate), + position, formatter: formatFactory?.(series[axis.id][0].fieldFormat), series: series[axis.id].map(({ fieldFormat, ...currentSeries }) => currentSeries), + ...axisGlobalConfig(position, axes), ...axis, }); } }); if (series.left.length > 0) { + position = shouldRotate ? 'bottom' : 'left'; axisGroups.push({ groupId: 'left', - position: shouldRotate ? 'bottom' : 'left', + position, formatter: formatFactory?.(series.left[0].fieldFormat), series: series.left.map(({ fieldFormat, ...currentSeries }) => currentSeries), + ...axisGlobalConfig(position, axes), }); } if (series.right.length > 0) { + position = shouldRotate ? 'top' : 'right'; axisGroups.push({ groupId: 'right', - position: shouldRotate ? 'top' : 'right', + position, formatter: formatFactory?.(series.right[0].fieldFormat), series: series.right.map(({ fieldFormat, ...currentSeries }) => currentSeries), + ...axisGlobalConfig(position, axes), }); } return axisGroups; } -<<<<<<< HEAD -export const getAxisConfig = (axesGroup?: GroupsConfiguration, yConfig?: YConfigResult) => { +export const getAxisGroupConfig = ( + axesGroup?: GroupsConfiguration, + yConfig?: ExtendedYConfigResult +) => { return axesGroup?.find( (axis) => (yConfig?.axisId && yConfig.axisId === axis.groupId) || axis.series.some(({ accessor }) => accessor === yConfig?.forAccessor) ); }; - -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 }; -} -======= ->>>>>>> Kunzetsov/chart_expressions-xy-extended_layers 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 2163378cb801f..ed38a90d0a7ed 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 @@ -56,6 +56,8 @@ describe('color_assignment', () => { isHistogram: true, isPercentage: false, seriesType: 'bar', + isStacked: false, + isHorizontal: false, palette: { type: 'palette', name: 'palette1' }, layerType: LayerTypes.DATA, splitAccessor: 'split1', @@ -69,6 +71,8 @@ describe('color_assignment', () => { isHistogram: true, isPercentage: false, seriesType: 'bar', + isStacked: false, + isHorizontal: false, palette: { type: 'palette', name: 'palette2' }, layerType: LayerTypes.DATA, splitAccessor: 'split2', 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 c1874aa313be8..54913271c248f 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 'src/plugins/field_formats/common'; import { Datatable, DatatableRow } from '../../../../expressions'; import { CommonXYDataLayerConfigResult, XScaleType } from '../../common'; -import { AxisModes } from '../../common/constants'; +import { AxisModes, SeriesTypes } from '../../common/constants'; import { FormatFactory } from '../types'; import { PaletteRegistry, SeriesLayer } from '../../../../charts/public'; import { getSeriesColor } from './state'; @@ -220,14 +220,14 @@ export const getSeriesProps: GetSeriesPropsFn = ({ emphasizeFitting, fillOpacity, }): SeriesSpec => { - const { table } = layer; - const isStacked = layer.seriesType.includes('stacked'); + const { table, isStacked } = layer; const isPercentage = layer.isPercentage; let stackMode: StackMode | undefined = isPercentage ? AxisModes.PERCENTAGE : undefined; if (yAxis?.mode) { stackMode = yAxis?.mode === AxisModes.NORMAL ? undefined : yAxis?.mode; } - const isBarChart = layer.seriesType.includes('bar'); + const scaleType = yAxis?.scaleType || layer.yScaleType; + const isBarChart = layer.seriesType === SeriesTypes.BAR; const enableHistogramMode = layer.isHistogram && (isStacked || !layer.splitAccessor) && @@ -275,9 +275,9 @@ export const getSeriesProps: GetSeriesPropsFn = ({ data: rows, xScaleType: layer.xAccessor ? layer.xScaleType : 'ordinal', yScaleType: - formatter?.id === 'bytes' && layer.yScaleType === ScaleType.Linear + formatter?.id === 'bytes' && scaleType === ScaleType.Linear ? ScaleType.LinearBinary - : layer.yScaleType, + : scaleType, color: (series) => getColor(series, { layer, 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 50bc39bccb066..760d64f8fa649 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 @@ -8,7 +8,6 @@ import { partition } from 'lodash'; import type { CommonXYDataLayerConfigResult, CommonXYLayerConfigResult } from '../../common'; -import { isStackedChart } from './state'; import { isAnnotationsLayer, isDataLayer } from './visualization'; export function computeOverallDataDomain( @@ -21,7 +20,7 @@ export function computeOverallDataDomain( let max: number | undefined; const [stacked, unstacked] = partition( layers, - (layer) => isDataLayer(layer) && isStackedChart(layer.seriesType) && allowStacking + (layer) => isDataLayer(layer) && layer.isStacked && allowStacking ); for (const layer of unstacked) { 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..6157467ee0fe6 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/state.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/state.ts @@ -6,23 +6,11 @@ * Side Public License, v 1. */ -import type { CommonXYLayerConfigResult, SeriesType, ExtendedYConfig, YConfig } from '../../common'; +import type { CommonXYLayerConfigResult, ExtendedYConfig, YConfig } from '../../common'; import { getDataLayers, isAnnotationsLayer, isDataLayer } from './visualization'; -export function isHorizontalSeries(seriesType: SeriesType) { - return ( - seriesType === 'bar_horizontal' || - seriesType === 'bar_horizontal_stacked' || - seriesType === 'bar_horizontal_percentage_stacked' - ); -} - -export function isStackedChart(seriesType: SeriesType) { - return seriesType.includes('stacked'); -} - export function isHorizontalChart(layers: CommonXYLayerConfigResult[]) { - return getDataLayers(layers).every((l) => isHorizontalSeries(l.seriesType)); + return getDataLayers(layers).every((l) => l.isHorizontal); } export const getSeriesColor = (layer: CommonXYLayerConfigResult, accessor: string) => { diff --git a/src/plugins/chart_expressions/expression_xy/public/plugin.ts b/src/plugins/chart_expressions/expression_xy/public/plugin.ts index 669d536d301e6..c8360fb847621 100755 --- a/src/plugins/chart_expressions/expression_xy/public/plugin.ts +++ b/src/plugins/chart_expressions/expression_xy/public/plugin.ts @@ -18,19 +18,16 @@ import { layeredXyVisFunction, dataLayerFunction, extendedDataLayerFunction, + yConfigFunction, + xAxisConfigFunction, yAxisConfigFunction, - axisConfigFunction, - extendedYAxisConfigFunction, + extendedYConfigFunction, legendConfigFunction, - gridlinesConfigFunction, axisExtentConfigFunction, - tickLabelsConfigFunction, referenceLineLayerFunction, extendedReferenceLineLayerFunction, annotationLayerFunction, extendedAnnotationLayerFunction, - labelsOrientationConfigFunction, - axisTitlesVisibilityConfigFunction, } from '../common'; import { GetStartDepsFn, getXyChartRenderer } from './expression_renderers'; import { EventAnnotationPluginSetup } from '../../../event_annotation/public'; @@ -57,20 +54,17 @@ export class ExpressionXyPlugin { { expressions, charts }: SetupDeps ): ExpressionXyPluginSetup { expressions.registerFunction(yAxisConfigFunction); - expressions.registerFunction(extendedYAxisConfigFunction); + expressions.registerFunction(yConfigFunction); + expressions.registerFunction(extendedYConfigFunction); expressions.registerFunction(legendConfigFunction); - expressions.registerFunction(gridlinesConfigFunction); expressions.registerFunction(dataLayerFunction); expressions.registerFunction(extendedDataLayerFunction); expressions.registerFunction(axisExtentConfigFunction); - expressions.registerFunction(axisConfigFunction); - expressions.registerFunction(tickLabelsConfigFunction); + expressions.registerFunction(xAxisConfigFunction); expressions.registerFunction(annotationLayerFunction); expressions.registerFunction(extendedAnnotationLayerFunction); - expressions.registerFunction(labelsOrientationConfigFunction); expressions.registerFunction(referenceLineLayerFunction); expressions.registerFunction(extendedReferenceLineLayerFunction); - expressions.registerFunction(axisTitlesVisibilityConfigFunction); expressions.registerFunction(xyVisFunction); expressions.registerFunction(layeredXyVisFunction); diff --git a/src/plugins/chart_expressions/expression_xy/server/plugin.ts b/src/plugins/chart_expressions/expression_xy/server/plugin.ts index e6e4228e56858..7e2b6386bfdd9 100755 --- a/src/plugins/chart_expressions/expression_xy/server/plugin.ts +++ b/src/plugins/chart_expressions/expression_xy/server/plugin.ts @@ -11,17 +11,15 @@ import { CoreSetup, CoreStart, Plugin } from '../../../../core/server'; import { ExpressionXyPluginSetup, ExpressionXyPluginStart } from './types'; import { xyVisFunction, - yAxisConfigFunction, - extendedYAxisConfigFunction, legendConfigFunction, - gridlinesConfigFunction, dataLayerFunction, + yConfigFunction, + xAxisConfigFunction, + yAxisConfigFunction, + extendedYConfigFunction, axisExtentConfigFunction, - tickLabelsConfigFunction, annotationLayerFunction, - labelsOrientationConfigFunction, referenceLineLayerFunction, - axisTitlesVisibilityConfigFunction, extendedDataLayerFunction, extendedReferenceLineLayerFunction, layeredXyVisFunction, @@ -34,19 +32,17 @@ export class ExpressionXyPlugin { public setup(core: CoreSetup, { expressions }: SetupDeps) { expressions.registerFunction(yAxisConfigFunction); - expressions.registerFunction(extendedYAxisConfigFunction); + expressions.registerFunction(yConfigFunction); + expressions.registerFunction(xAxisConfigFunction); + expressions.registerFunction(extendedYConfigFunction); expressions.registerFunction(legendConfigFunction); - expressions.registerFunction(gridlinesConfigFunction); expressions.registerFunction(dataLayerFunction); expressions.registerFunction(extendedDataLayerFunction); expressions.registerFunction(axisExtentConfigFunction); - expressions.registerFunction(tickLabelsConfigFunction); expressions.registerFunction(annotationLayerFunction); expressions.registerFunction(extendedAnnotationLayerFunction); - expressions.registerFunction(labelsOrientationConfigFunction); expressions.registerFunction(referenceLineLayerFunction); expressions.registerFunction(extendedReferenceLineLayerFunction); - expressions.registerFunction(axisTitlesVisibilityConfigFunction); expressions.registerFunction(xyVisFunction); expressions.registerFunction(layeredXyVisFunction); } diff --git a/x-pack/plugins/lens/public/index.ts b/x-pack/plugins/lens/public/index.ts index 7ea45600d52f6..f883e4ad32b16 100644 --- a/x-pack/plugins/lens/public/index.ts +++ b/x-pack/plugins/lens/public/index.ts @@ -94,15 +94,10 @@ export type { AxisExtentConfig, LegendConfigResult, AxesSettingsConfig, - GridlinesConfigResult, DataLayerConfigResult, - TickLabelsConfigResult, AxisExtentConfigResult, ReferenceLineLayerArgs, - LabelsOrientationConfig, - 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/shared_components/axis_title_settings.tsx b/x-pack/plugins/lens/public/shared_components/axis_title_settings.tsx index ff7b058873bbc..a1f8416254b77 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 @@ -8,7 +8,7 @@ import React, { useCallback, useState } from 'react'; import { EuiSpacer, EuiFormRow } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { AxesSettingsConfig } from '../../../../../src/plugins/chart_expressions/expression_xy/common'; +import { AxesSettingsConfig } from '../xy_visualization/types'; import { LabelMode, useDebouncedValue, VisLabel } from './'; type AxesSettingsConfigKeys = keyof AxesSettingsConfig; 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 4fb573199eff6..198883908933a 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 @@ -5,90 +5,104 @@ Object { "chain": Array [ Object { "arguments": Object { - "axes": Array [], - "axisTitlesVisibilitySettings": Array [ + "axes": Array [ Object { "chain": Array [ Object { "arguments": Object { - "x": Array [ + "extent": Array [], + "id": Array [], + "labelsOrientation": Array [ + -90, + ], + "position": Array [ + "left", + ], + "showGridLines": Array [ true, ], - "yLeft": Array [ + "showLabels": Array [ true, ], - "yRight": Array [ + "showTitle": Array [ true, ], + "title": Array [], }, - "function": "axisTitlesVisibilityConfig", + "function": "yAxisConfig", "type": "function", }, ], "type": "expression", }, - ], - "curveType": Array [ - "LINEAR", - ], - "emphasizeFitting": Array [ - true, - ], - "endValue": Array [ - "Nearest", - ], - "fillOpacity": Array [ - 0.3, - ], - "fittingFunction": Array [ - "Carry", - ], - "gridlinesVisibilitySettings": Array [ Object { "chain": Array [ Object { "arguments": Object { - "x": Array [ - false, + "extent": Array [ + Object { + "chain": Array [ + Object { + "arguments": Object { + "lowerBound": Array [ + 123, + ], + "mode": Array [ + "custom", + ], + "upperBound": Array [ + 456, + ], + }, + "function": "axisExtentConfig", + "type": "function", + }, + ], + "type": "expression", + }, + ], + "id": Array [], + "labelsOrientation": Array [ + -45, + ], + "position": Array [ + "right", ], - "yLeft": Array [ + "showGridLines": Array [ true, ], - "yRight": Array [ + "showLabels": Array [ true, ], + "showTitle": Array [ + true, + ], + "title": Array [], }, - "function": "gridlinesConfig", + "function": "yAxisConfig", "type": "function", }, ], "type": "expression", }, ], - "hideEndzones": Array [ + "curveType": Array [ + "LINEAR", + ], + "emphasizeFitting": Array [ true, ], - "labelsOrientation": Array [ - Object { - "chain": Array [ - Object { - "arguments": Object { - "x": Array [ - 0, - ], - "yLeft": Array [ - -90, - ], - "yRight": Array [ - -45, - ], - }, - "function": "labelsOrientationConfig", - "type": "function", - }, - ], - "type": "expression", - }, + "endValue": Array [ + "Nearest", + ], + "fillOpacity": Array [ + 0.3, + ], + "fittingFunction": Array [ + "Carry", + ], + "hideEndzones": Array [ + true, ], "layers": Array [ Object { @@ -108,6 +122,9 @@ Object { "isHistogram": Array [ false, ], + "isHorizontal": Array [], + "isPercentage": Array [], + "isStacked": Array [], "palette": Array [ Object { "chain": Array [ @@ -189,83 +206,46 @@ Object { "type": "expression", }, ], - "tickLabelsVisibilitySettings": Array [ - Object { - "chain": Array [ - Object { - "arguments": Object { - "x": Array [ - false, - ], - "yLeft": Array [ - true, - ], - "yRight": Array [ - true, - ], - }, - "function": "tickLabelsConfig", - "type": "function", - }, - ], - "type": "expression", - }, - ], "valueLabels": Array [ "hide", ], "valuesInLegend": Array [ false, ], - "xTitle": Array [ - "", - ], - "yLeftExtent": Array [ + "xAxisConfig": Array [ Object { "chain": Array [ Object { "arguments": Object { - "lowerBound": Array [], - "mode": Array [ - "full", + "id": Array [ + "x", ], - "upperBound": Array [], - }, - "function": "axisExtentConfig", - "type": "function", - }, - ], - "type": "expression", - }, - ], - "yRightExtent": Array [ - Object { - "chain": Array [ - Object { - "arguments": Object { - "lowerBound": Array [ - 123, + "labelsOrientation": Array [ + 0, + ], + "position": Array [ + "bottom", ], - "mode": Array [ - "custom", + "showGridLines": Array [ + false, + ], + "showLabels": Array [ + false, ], - "upperBound": Array [ - 456, + "showTitle": Array [ + true, + ], + "title": Array [ + "", ], }, - "function": "axisExtentConfig", + "function": "xAxisConfig", "type": "function", }, ], "type": "expression", }, ], - "yRightTitle": Array [ - "", - ], - "yTitle": Array [ - "", - ], }, "function": "layeredXyVis", "type": "function", 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 d3c1ee028dfc4..f782f9b945c4f 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,12 +8,11 @@ import { groupBy, partition } from 'lodash'; import { i18n } from '@kbn/i18n'; import { layerTypes } from '../../common'; -import type { ExtendedYConfig } from '../../../../../src/plugins/chart_expressions/expression_xy/common'; import { Datatable } from '../../../../../src/plugins/expressions/public'; import type { DatasourceLayers, FramePublicAPI, Visualization } from '../types'; import { groupAxesByType } from './axes_configuration'; import { isHorizontalChart, isPercentageSeries, isStackedChart } from './state_helpers'; -import type { XYState, XYDataLayerConfig, XYReferenceLineLayerConfig, YAxisMode } from './types'; +import type { XYState, XYDataLayerConfig, XYReferenceLineLayerConfig, YAxisMode, YConfig } from './types'; import { checkScaleOperation, getAxisName, @@ -34,7 +33,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: DatasourceLayers, 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 bb10f28f07365..975676e241b76 100644 --- a/x-pack/plugins/lens/public/xy_visualization/state_helpers.ts +++ b/x-pack/plugins/lens/public/xy_visualization/state_helpers.ts @@ -7,15 +7,13 @@ import { EuiIconType } from '@elastic/eui/src/components/icon/icon'; import type { FramePublicAPI, DatasourcePublicAPI } from '../types'; -import type { - SeriesType, - ExtendedYConfig, -} from '../../../../../src/plugins/chart_expressions/expression_xy/common'; import { visualizationTypes, XYLayerConfig, XYDataLayerConfig, XYReferenceLineLayerConfig, + SeriesType, + YConfig, } from './types'; import { getDataLayers, isAnnotationsLayer, isDataLayer } from './visualization_helpers'; @@ -61,8 +59,7 @@ export const getSeriesColor = (layer: XYLayerConfig, accessor: string) => { return null; } return ( - layer?.yConfig?.find((yConfig: ExtendedYConfig) => yConfig.forAccessor === accessor)?.color || - null + layer?.yConfig?.find((yConfig: YConfig) => yConfig.forAccessor === accessor)?.color || null ); }; 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 5f98fa5381e91..89de6f7346e85 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 @@ -156,13 +156,23 @@ describe('#toExpression', () => { undefined, datasourceExpressionsByLayers ) as Ast; - expect( - (expression.chain[0].arguments.axisTitlesVisibilitySettings[0] as Ast).chain[0].arguments - ).toEqual({ - x: [true], - yLeft: [true], - yRight: [true], - }); + expect((expression.chain[0].arguments.axes[0] as Ast).chain[0].arguments).toEqual( + expect.objectContaining({ + showTitle: [true], + position: ['left'], + }) + ); + expect((expression.chain[0].arguments.axes[1] as Ast).chain[0].arguments).toEqual( + expect.objectContaining({ + showTitle: [true], + position: ['right'], + }) + ); + expect((expression.chain[0].arguments.xAxisConfig[0] as Ast).chain[0].arguments).toEqual( + expect.objectContaining({ + showTitle: [true], + }) + ); }); it('should generate an expression without x accessor', () => { @@ -241,9 +251,6 @@ describe('#toExpression', () => { expect(mockDatasource.publicAPIMock.getOperationForColumnId).toHaveBeenCalledWith('b'); expect(mockDatasource.publicAPIMock.getOperationForColumnId).toHaveBeenCalledWith('c'); expect(mockDatasource.publicAPIMock.getOperationForColumnId).toHaveBeenCalledWith('d'); - expect(expression.chain[0].arguments.xTitle).toEqual(['']); - expect(expression.chain[0].arguments.yTitle).toEqual(['']); - expect(expression.chain[0].arguments.yRightTitle).toEqual(['']); expect( (expression.chain[0].arguments.layers[0] as Ast).chain[0].arguments.columnToLabel ).toEqual([ @@ -276,13 +283,23 @@ describe('#toExpression', () => { undefined, datasourceExpressionsByLayers ) as Ast; - expect( - (expression.chain[0].arguments.tickLabelsVisibilitySettings[0] as Ast).chain[0].arguments - ).toEqual({ - x: [true], - yLeft: [true], - yRight: [true], - }); + expect((expression.chain[0].arguments.axes[0] as Ast).chain[0].arguments).toEqual( + expect.objectContaining({ + showLabels: [true], + position: ['left'], + }) + ); + expect((expression.chain[0].arguments.axes[1] as Ast).chain[0].arguments).toEqual( + expect.objectContaining({ + showLabels: [true], + position: ['right'], + }) + ); + expect((expression.chain[0].arguments.xAxisConfig[0] as Ast).chain[0].arguments).toEqual( + expect.objectContaining({ + showLabels: [true], + }) + ); }); it('should default the tick labels orientation settings to 0', () => { @@ -306,11 +323,23 @@ describe('#toExpression', () => { undefined, datasourceExpressionsByLayers ) as Ast; - expect((expression.chain[0].arguments.labelsOrientation[0] as Ast).chain[0].arguments).toEqual({ - x: [0], - yLeft: [0], - yRight: [0], - }); + expect((expression.chain[0].arguments.axes[0] as Ast).chain[0].arguments).toEqual( + expect.objectContaining({ + labelsOrientation: [0], + position: ['left'], + }) + ); + expect((expression.chain[0].arguments.axes[1] as Ast).chain[0].arguments).toEqual( + expect.objectContaining({ + labelsOrientation: [0], + position: ['right'], + }) + ); + expect((expression.chain[0].arguments.xAxisConfig[0] as Ast).chain[0].arguments).toEqual( + expect.objectContaining({ + labelsOrientation: [0], + }) + ); }); it('should default the gridlines visibility settings to true', () => { @@ -334,13 +363,23 @@ describe('#toExpression', () => { undefined, datasourceExpressionsByLayers ) as Ast; - expect( - (expression.chain[0].arguments.gridlinesVisibilitySettings[0] as Ast).chain[0].arguments - ).toEqual({ - x: [true], - yLeft: [true], - yRight: [true], - }); + expect((expression.chain[0].arguments.axes[0] as Ast).chain[0].arguments).toEqual( + expect.objectContaining({ + showGridLines: [true], + position: ['left'], + }) + ); + expect((expression.chain[0].arguments.axes[1] as Ast).chain[0].arguments).toEqual( + expect.objectContaining({ + showGridLines: [true], + position: ['right'], + }) + ); + expect((expression.chain[0].arguments.xAxisConfig[0] as Ast).chain[0].arguments).toEqual( + expect.objectContaining({ + showGridLines: [true], + }) + ); }); it('should correctly report the valueLabels visibility settings', () => { 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 9b32db52f0759..f5306b594fdf0 100644 --- a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts +++ b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts @@ -21,11 +21,7 @@ import { import type { ValidXYDataLayerConfig } from './types'; import { OperationMetadata, DatasourcePublicAPI, DatasourceLayers } from '../types'; import { getColumnToLabelMap } from './state_helpers'; -import type { - AxisConfig, - ExtendedYConfig, - YConfig, -} from '../../../../../src/plugins/chart_expressions/expression_xy/common'; +import type { YAxisConfig } 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'; @@ -210,7 +206,28 @@ export const buildExpression = ( yConfig?.some((config) => config.axisMode === 'right') ); - const axes: AxisConfig[] = []; + const axes: YAxisConfig[] = [ + { + position: 'left', + extent: state?.yLeftExtent ? { ...state?.yLeftExtent, type: 'axisExtentConfig' } : undefined, + showTitle: state?.axisTitlesVisibilitySettings?.yLeft ?? true, + title: state.yTitle || '', + showLabels: state?.tickLabelsVisibilitySettings?.yLeft ?? true, + showGridLines: state?.gridlinesVisibilitySettings?.yLeft ?? true, + labelsOrientation: state?.labelsOrientation?.yLeft ?? 0, + }, + { + position: 'right', + extent: state?.yRightExtent + ? { ...state?.yRightExtent, type: 'axisExtentConfig' } + : undefined, + showTitle: state?.axisTitlesVisibilitySettings?.yRight ?? true, + title: state.yRightTitle || '', + showLabels: state?.tickLabelsVisibilitySettings?.yRight ?? true, + showGridLines: state?.gridlinesVisibilitySettings?.yRight ?? true, + labelsOrientation: state?.labelsOrientation?.yRight ?? 0, + }, + ]; if (isLeftAxis) { axes.push({ @@ -233,9 +250,6 @@ export const buildExpression = ( type: 'function', function: 'layeredXyVis', arguments: { - xTitle: [state.xTitle || ''], - yTitle: [state.yTitle || ''], - yRightTitle: [state.yRightTitle || ''], legend: [ { type: 'expression', @@ -282,118 +296,30 @@ export const buildExpression = ( emphasizeFitting: [state.emphasizeFitting || false], curveType: [state.curveType || 'LINEAR'], fillOpacity: [state.fillOpacity || 0.3], - yLeftExtent: [ - { - type: 'expression', - chain: [ - { - type: 'function', - function: 'axisExtentConfig', - arguments: { - mode: [state?.yLeftExtent?.mode || 'full'], - lowerBound: - state?.yLeftExtent?.lowerBound !== undefined - ? [state?.yLeftExtent?.lowerBound] - : [], - upperBound: - state?.yLeftExtent?.upperBound !== undefined - ? [state?.yLeftExtent?.upperBound] - : [], - }, - }, - ], - }, - ], - yRightExtent: [ - { - type: 'expression', - chain: [ - { - type: 'function', - function: 'axisExtentConfig', - arguments: { - mode: [state?.yRightExtent?.mode || 'full'], - lowerBound: - state?.yRightExtent?.lowerBound !== undefined - ? [state?.yRightExtent?.lowerBound] - : [], - upperBound: - state?.yRightExtent?.upperBound !== undefined - ? [state?.yRightExtent?.upperBound] - : [], - }, - }, - ], - }, - ], - axisTitlesVisibilitySettings: [ - { - type: 'expression', - chain: [ - { - type: 'function', - function: 'axisTitlesVisibilityConfig', - arguments: { - x: [state?.axisTitlesVisibilitySettings?.x ?? true], - yLeft: [state?.axisTitlesVisibilitySettings?.yLeft ?? true], - yRight: [state?.axisTitlesVisibilitySettings?.yRight ?? true], - }, - }, - ], - }, - ], - tickLabelsVisibilitySettings: [ - { - type: 'expression', - chain: [ - { - type: 'function', - function: 'tickLabelsConfig', - arguments: { - x: [state?.tickLabelsVisibilitySettings?.x ?? true], - yLeft: [state?.tickLabelsVisibilitySettings?.yLeft ?? true], - yRight: [state?.tickLabelsVisibilitySettings?.yRight ?? true], - }, - }, - ], - }, - ], - gridlinesVisibilitySettings: [ - { - type: 'expression', - chain: [ - { - type: 'function', - function: 'gridlinesConfig', - arguments: { - x: [state?.gridlinesVisibilitySettings?.x ?? true], - yLeft: [state?.gridlinesVisibilitySettings?.yLeft ?? true], - yRight: [state?.gridlinesVisibilitySettings?.yRight ?? true], - }, - }, - ], - }, - ], - labelsOrientation: [ + valueLabels: [state?.valueLabels || 'hide'], + hideEndzones: [state?.hideEndzones || false], + valuesInLegend: [state?.valuesInLegend || false], + axes: [...axesToExpression(axes)], + xAxisConfig: [ { type: 'expression', chain: [ { type: 'function', - function: 'labelsOrientationConfig', + function: 'xAxisConfig', arguments: { - x: [state?.labelsOrientation?.x ?? 0], - yLeft: [state?.labelsOrientation?.yLeft ?? 0], - yRight: [state?.labelsOrientation?.yRight ?? 0], + id: ['x'], + position: ['bottom'], + title: [state.xTitle || ''], + showTitle: [state?.axisTitlesVisibilitySettings?.x ?? true], + showLabels: [state?.tickLabelsVisibilitySettings?.x ?? true], + showGridLines: [state?.gridlinesVisibilitySettings?.x ?? true], + labelsOrientation: [state?.labelsOrientation?.x ?? 0], }, }, ], }, ], - valueLabels: [state?.valueLabels || 'hide'], - hideEndzones: [state?.hideEndzones || false], - valuesInLegend: [state?.valuesInLegend || false], - axes: [...axesToExpression(axes)], layers: [ ...validDataLayers.map((layer) => dataLayerToExpression( @@ -432,16 +358,41 @@ const buildTableExpression = (datasourceExpression: Ast): ExpressionAstExpressio chain: [{ type: 'function', function: 'kibana', arguments: {} }, ...datasourceExpression.chain], }); -const axesToExpression = (axes: AxisConfig[]): Ast[] => { +const axesToExpression = (axes: YAxisConfig[]): Ast[] => { return axes.map((axis) => ({ type: 'expression', chain: [ { type: 'function', - function: 'axisConfig', + function: 'yAxisConfig', arguments: { - id: [axis.id], + id: axis.id ? [axis.id] : [], position: axis.position ? [axis.position] : [], + extent: axis.extent + ? [ + { + type: 'expression', + chain: [ + { + type: 'function', + function: 'axisExtentConfig', + arguments: { + mode: [axis.extent?.mode || 'full'], + lowerBound: + axis.extent?.lowerBound !== undefined ? [axis.extent?.lowerBound] : [], + upperBound: + axis.extent?.upperBound !== undefined ? [axis.extent?.upperBound] : [], + }, + }, + ], + }, + ] + : [], + showTitle: axis.showTitle ? [axis.showTitle] : [], + title: axis.title ? [axis.title] : [], + showLabels: axis.showLabels ? [axis.showLabels] : [], + showGridLines: axis.showGridLines ? [axis.showGridLines] : [], + labelsOrientation: axis.labelsOrientation !== undefined ? [axis.labelsOrientation] : [], }, }, ], @@ -450,7 +401,7 @@ const axesToExpression = (axes: AxisConfig[]): Ast[] => { const referenceLineLayerToExpression = ( layer: XYReferenceLineLayerConfig, - axes: AxisConfig[], + axes: YAxisConfig[], datasourceLayer: DatasourcePublicAPI, datasourceExpression: Ast ): Ast => { @@ -463,7 +414,7 @@ const referenceLineLayerToExpression = ( arguments: { yConfig: layer.yConfig ? layer.yConfig.map((yConfig) => - extendedYConfigToExpression(yConfig, defaultReferenceLineColor) + extendedYConfigToExpression(yConfig, axes, defaultReferenceLineColor) ) : [], accessors: layer.accessors, @@ -512,7 +463,7 @@ const annotationLayerToExpression = ( const dataLayerToExpression = ( layer: ValidXYDataLayerConfig, - axes: AxisConfig[], + axes: YAxisConfig[], datasourceLayer: DatasourcePublicAPI, metadata: Record>, paletteService: PaletteRegistry, @@ -529,6 +480,12 @@ const dataLayerToExpression = ( xAxisOperation.scale !== 'ordinal' ); + const dataFromType = layer.seriesType.split('_'); + const seriesType = dataFromType[0]; + const isPercentage = dataFromType.includes('percentage'); + const isStacked = dataFromType.includes('stacked'); + const isHorizontal = dataFromType.includes('horizontal'); + return { type: 'expression', chain: [ @@ -543,12 +500,14 @@ const dataLayerToExpression = ( ], xScaleType: [getScaleType(metadata[layer.layerId][layer.xAccessor], ScaleType.Linear)], isHistogram: [isHistogramDimension], - isPercentage: layer.seriesType.includes('percentage') ? [true] : [], + isPercentage: isPercentage ? [isPercentage] : [], + isStacked: isStacked ? [isStacked] : [], + isHorizontal: isHorizontal ? [isHorizontal] : [], splitAccessor: layer.splitAccessor ? [layer.splitAccessor] : [], yConfig: layer.yConfig ? layer.yConfig.map((yConfig) => yConfigToExpression(yConfig, axes)) : [], - seriesType: [layer.seriesType], + seriesType: [seriesType], accessors: layer.accessors, columnToLabel: [JSON.stringify(columnToLabel)], ...(datasourceExpression ? { table: [buildTableExpression(datasourceExpression)] } : {}), @@ -583,8 +542,8 @@ const dataLayerToExpression = ( }; }; -const yConfigToExpression = (yConfig: YConfig, axes: AxisConfig[], defaultColor?: string): Ast => { - const axisId = axes.find((axis) => axis.position === yConfig.axisMode)?.id; +const yConfigToExpression = (yConfig: YConfig, axes: YAxisConfig[], defaultColor?: string): Ast => { + const axisId = axes.find((axis) => axis.id && axis.position === yConfig.axisMode)?.id; return { type: 'expression', chain: [ @@ -601,7 +560,12 @@ const yConfigToExpression = (yConfig: YConfig, axes: AxisConfig[], defaultColor? }; }; -const extendedYConfigToExpression = (yConfig: ExtendedYConfig, defaultColor?: string): Ast => { +const extendedYConfigToExpression = ( + yConfig: YConfig, + axes: YAxisConfig[], + defaultColor?: string +): Ast => { + const axisId = axes.find((axis) => axis.id && axis.position === yConfig.axisMode)?.id; return { type: 'expression', chain: [ @@ -609,8 +573,8 @@ const extendedYConfigToExpression = (yConfig: ExtendedYConfig, defaultColor?: st type: 'function', function: 'extendedYConfig', arguments: { + axisId: axisId ? [axisId] : [], forAccessor: [yConfig.forAccessor], - axisMode: yConfig.axisMode ? [yConfig.axisMode] : [], color: yConfig.color ? [yConfig.color] : defaultColor ? [defaultColor] : [], lineStyle: [yConfig.lineStyle || 'solid'], lineWidth: [yConfig.lineWidth || 1], diff --git a/x-pack/plugins/lens/public/xy_visualization/types.ts b/x-pack/plugins/lens/public/xy_visualization/types.ts index 951a591537a36..931585d4ebcd7 100644 --- a/x-pack/plugins/lens/public/xy_visualization/types.ts +++ b/x-pack/plugins/lens/public/xy_visualization/types.ts @@ -21,16 +21,11 @@ import { LensIconChartLine } from '../assets/chart_line'; import type { VisualizationType, Suggestion } from '../types'; import type { - SeriesType, LegendConfig, AxisExtentConfig, XYCurveType, - AxesSettingsConfig, FittingFunction, - LabelsOrientationConfig, EndValue, - ExtendedYConfig, - YConfig, YScaleType, XScaleType, LineStyle, @@ -47,7 +42,33 @@ export const YAxisModes = { BOTTOM: 'bottom', } 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 type YAxisMode = $Values; +export type SeriesType = $Values; +export interface AxesSettingsConfig { + x: boolean; + yRight: boolean; + yLeft: boolean; +} + +export interface LabelsOrientationConfig { + x: number; + yLeft: number; + yRight: number; +} + export interface YConfig { forAccessor: string; color?: string; @@ -79,7 +100,7 @@ export interface XYDataLayerConfig { export interface XYReferenceLineLayerConfig { layerId: string; accessors: string[]; - yConfig?: ExtendedYConfig[]; + yConfig?: YConfig[]; palette?: PaletteOutput; layerType: '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 282efa30297bb..8c89f53423ac8 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts @@ -15,8 +15,8 @@ import type { XYLayerConfig, XYDataLayerConfig, XYReferenceLineLayerConfig, + SeriesType, } from './types'; -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'; diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx index 65504539e7018..2a16867ad0ee6 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx @@ -28,12 +28,7 @@ import type { VisualizationConfigProps, VisualizationToolbarProps, } from '../types'; -import { - FillStyle, - SeriesType, - YAxisMode, - ExtendedYConfig, -} from '../../../../../src/plugins/chart_expressions/expression_xy/common'; +import { FillStyle } from '../../../../../src/plugins/chart_expressions/expression_xy/common'; import { State, visualizationTypes, @@ -42,6 +37,7 @@ import { XYDataLayerConfig, YConfig, YAxisMode, + SeriesType, } from './types'; import { layerTypes } from '../../common'; import { isHorizontalChart } from './state_helpers'; @@ -401,7 +397,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/visualization_helpers.tsx b/x-pack/plugins/lens/public/xy_visualization/visualization_helpers.tsx index 84e769b5e2623..c042741c5c224 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization_helpers.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/visualization_helpers.tsx @@ -16,9 +16,9 @@ import { XYLayerConfig, XYDataLayerConfig, XYReferenceLineLayerConfig, + SeriesType, } 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 e9a970cb82987..9a316864828ed 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 @@ -17,11 +17,8 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { isEqual } from 'lodash'; -import { - AxesSettingsConfig, - AxisExtentConfig, -} from '../../../../../../src/plugins/chart_expressions/expression_xy/common'; -import { XYLayerConfig } from '../types'; +import { AxisExtentConfig } from '../../../../../../src/plugins/chart_expressions/expression_xy/common'; +import { XYLayerConfig, AxesSettingsConfig } from '../types'; import { ToolbarPopover, useDebouncedValue, AxisTitleSettings } from '../../shared_components'; import { isHorizontalChart } from '../state_helpers'; import { EuiIconAxisBottom } from '../../assets/axis_bottom'; 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 ffdd33d45963d..699dd055796de 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,8 +9,7 @@ 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 { SeriesType } from '../../../../../../src/plugins/chart_expressions/expression_xy/common'; +import { State, visualizationTypes, SeriesType } from '../types'; import { isHorizontalChart, isHorizontalSeries } from '../state_helpers'; import { trackUiEvent } from '../../lens_ui_telemetry'; import { StaticHeader } from '../../shared_components'; 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 671e225ef894b..4cfb8a59fe404 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,14 @@ 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, + XYLayerConfig, + XYDataLayerConfig, + SeriesType, +} from './types'; import { layerTypes } from '../../common'; import { getIconForSeries } from './state_helpers'; import { getDataLayers, isDataLayer } from './visualization_helpers'; From a5ac5c4483b5151d9e6f68c2c703ea8666acd6c2 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Fri, 15 Apr 2022 15:51:57 +0000 Subject: [PATCH 120/213] [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' --- .../public/components/xy_chart.test.tsx | 315 ++++++++++-------- .../reference_line_helpers.tsx | 8 +- 2 files changed, 181 insertions(+), 142 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 f3fd10cf49b7c..d95a68fbd4e7e 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,15 +1994,18 @@ describe('XYChart component', () => { test('it should set the tickLabel visibility on the x axis if the tick labels is hidden', () => { const { args } = sampleArgs(); - args.axes = [{ - type: 'yAxisConfig', - position: 'left', - showLabels: true - }, { - type: 'yAxisConfig', - position: 'right', - showLabels: true - }]; + args.axes = [ + { + type: 'yAxisConfig', + position: 'left', + showLabels: true, + }, + { + type: 'yAxisConfig', + position: 'right', + showLabels: true, + }, + ]; args.xAxisConfig = { type: 'xAxisConfig', @@ -2024,15 +2027,18 @@ describe('XYChart component', () => { test('it should set the tickLabel visibility on the y axis if the tick labels is hidden', () => { const { args } = sampleArgs(); - args.axes = [{ - type: 'yAxisConfig', - position: 'left', - showLabels: false - }, { - type: 'yAxisConfig', - position: 'right', - showLabels: false - }]; + args.axes = [ + { + type: 'yAxisConfig', + position: 'left', + showLabels: false, + }, + { + type: 'yAxisConfig', + position: 'right', + showLabels: false, + }, + ]; const instance = shallow(); @@ -2048,15 +2054,18 @@ describe('XYChart component', () => { test('it should set the tickLabel visibility on the x axis if the tick labels is shown', () => { const { args } = sampleArgs(); - args.axes = [{ - type: 'yAxisConfig', - position: 'left', - showLabels: true - }, { - type: 'yAxisConfig', - position: 'right', - showLabels: true - }]; + args.axes = [ + { + type: 'yAxisConfig', + position: 'left', + showLabels: true, + }, + { + type: 'yAxisConfig', + position: 'right', + showLabels: true, + }, + ]; args.xAxisConfig = { type: 'xAxisConfig', @@ -2078,15 +2087,18 @@ describe('XYChart component', () => { test('it should set the tickLabel orientation on the x axis', () => { const { args } = sampleArgs(); - args.axes = [{ - type: 'yAxisConfig', - position: 'left', - labelsOrientation: 0 - }, { - type: 'yAxisConfig', - position: 'right', - labelsOrientation: -90 - }]; + args.axes = [ + { + type: 'yAxisConfig', + position: 'left', + labelsOrientation: 0, + }, + { + type: 'yAxisConfig', + position: 'right', + labelsOrientation: -90, + }, + ]; args.xAxisConfig = { type: 'xAxisConfig', @@ -2109,15 +2121,18 @@ describe('XYChart component', () => { test('it should set the tickLabel visibility on the y axis if the tick labels is shown', () => { const { args } = sampleArgs(); - args.axes = [{ - type: 'yAxisConfig', - position: 'left', - showLabels: true - }, { - type: 'yAxisConfig', - position: 'right', - showLabels: true - }]; + args.axes = [ + { + type: 'yAxisConfig', + position: 'left', + showLabels: true, + }, + { + type: 'yAxisConfig', + position: 'right', + showLabels: true, + }, + ]; args.xAxisConfig = { type: 'xAxisConfig', @@ -2139,15 +2154,18 @@ describe('XYChart component', () => { test('it should set the tickLabel orientation on the y axis', () => { const { args } = sampleArgs(); - args.axes = [{ - type: 'yAxisConfig', - position: 'left', - labelsOrientation: -90 - }, { - type: 'yAxisConfig', - position: 'right', - labelsOrientation: -90 - }]; + args.axes = [ + { + type: 'yAxisConfig', + position: 'left', + labelsOrientation: -90, + }, + { + type: 'yAxisConfig', + position: 'right', + labelsOrientation: -90, + }, + ]; args.xAxisConfig = { type: 'xAxisConfig', @@ -2197,29 +2215,32 @@ describe('XYChart component', () => { const args: XYProps = { legend: { type: 'legendConfig', isVisible: false, position: Position.Top }, valueLabels: 'hide', - axes: [{ - type: 'yAxisConfig', - position: 'left', - labelsOrientation: 0, - showGridLines: false, - showLabels: true, - title: '', - extent: { - mode: 'full', - type: 'axisExtentConfig', - } - }, { - type: 'yAxisConfig', - position: 'right', - labelsOrientation: 0, - showGridLines: false, - showLabels: true, - title: '', - extent: { - mode: 'full', - type: 'axisExtentConfig', - } - }], + axes: [ + { + type: 'yAxisConfig', + position: 'left', + labelsOrientation: 0, + showGridLines: false, + showLabels: true, + title: '', + extent: { + mode: 'full', + type: 'axisExtentConfig', + }, + }, + { + type: 'yAxisConfig', + position: 'right', + labelsOrientation: 0, + showGridLines: false, + showLabels: true, + title: '', + extent: { + mode: 'full', + type: 'axisExtentConfig', + }, + }, + ], xAxisConfig: { type: 'xAxisConfig', id: 'x', @@ -2292,29 +2313,32 @@ describe('XYChart component', () => { const args: XYProps = { legend: { type: 'legendConfig', isVisible: false, position: Position.Top }, valueLabels: 'hide', - axes: [{ - type: 'yAxisConfig', - position: 'left', - labelsOrientation: 0, - showGridLines: false, - showLabels: false, - title: '', - extent: { - mode: 'full', - type: 'axisExtentConfig', - } - }, { - type: 'yAxisConfig', - position: 'right', - labelsOrientation: 0, - showGridLines: false, - showLabels: false, - title: '', - extent: { - mode: 'full', - type: 'axisExtentConfig', - } - }], + axes: [ + { + type: 'yAxisConfig', + position: 'left', + labelsOrientation: 0, + showGridLines: false, + showLabels: false, + title: '', + extent: { + mode: 'full', + type: 'axisExtentConfig', + }, + }, + { + type: 'yAxisConfig', + position: 'right', + labelsOrientation: 0, + showGridLines: false, + showLabels: false, + title: '', + extent: { + mode: 'full', + type: 'axisExtentConfig', + }, + }, + ], xAxisConfig: { type: 'xAxisConfig', id: 'x', @@ -2368,29 +2392,32 @@ describe('XYChart component', () => { const args: XYProps = { legend: { type: 'legendConfig', isVisible: true, position: Position.Top }, valueLabels: 'hide', - axes: [{ - type: 'yAxisConfig', - position: 'left', - labelsOrientation: 0, - showGridLines: false, - showLabels: false, - title: '', - extent: { - mode: 'full', - type: 'axisExtentConfig', - } - }, { - type: 'yAxisConfig', - position: 'right', - labelsOrientation: 0, - showGridLines: false, - showLabels: false, - title: '', - extent: { - mode: 'full', - type: 'axisExtentConfig', - } - }], + axes: [ + { + type: 'yAxisConfig', + position: 'left', + labelsOrientation: 0, + showGridLines: false, + showLabels: false, + title: '', + extent: { + mode: 'full', + type: 'axisExtentConfig', + }, + }, + { + type: 'yAxisConfig', + position: 'right', + labelsOrientation: 0, + showGridLines: false, + showLabels: false, + title: '', + extent: { + mode: 'full', + type: 'axisExtentConfig', + }, + }, + ], xAxisConfig: { type: 'xAxisConfig', id: 'x', @@ -2564,15 +2591,18 @@ describe('XYChart component', () => { test('it should hide the X axis title if the corresponding switch is off', () => { const { args } = sampleArgs(); - args.axes = [{ - type: 'yAxisConfig', - position: 'left', - showTitle: true - }, { - type: 'yAxisConfig', - position: 'right', - showTitle: true - }]; + args.axes = [ + { + type: 'yAxisConfig', + position: 'left', + showTitle: true, + }, + { + type: 'yAxisConfig', + position: 'right', + showTitle: true, + }, + ]; args.xAxisConfig = { type: 'xAxisConfig', @@ -2596,15 +2626,18 @@ describe('XYChart component', () => { test('it should show the X axis gridlines if the setting is on', () => { const { args } = sampleArgs(); - args.axes = [{ - type: 'yAxisConfig', - position: 'left', - showGridLines: false - }, { - type: 'yAxisConfig', - position: 'right', - showGridLines: false - }]; + args.axes = [ + { + type: 'yAxisConfig', + position: 'left', + showGridLines: false, + }, + { + type: 'yAxisConfig', + position: 'right', + showGridLines: false, + }, + ]; args.xAxisConfig = { type: 'xAxisConfig', 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 f782f9b945c4f..5b606295439d7 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 @@ -12,7 +12,13 @@ import { Datatable } from '../../../../../src/plugins/expressions/public'; import type { DatasourceLayers, FramePublicAPI, Visualization } from '../types'; import { groupAxesByType } from './axes_configuration'; import { isHorizontalChart, isPercentageSeries, isStackedChart } from './state_helpers'; -import type { XYState, XYDataLayerConfig, XYReferenceLineLayerConfig, YAxisMode, YConfig } from './types'; +import type { + XYState, + XYDataLayerConfig, + XYReferenceLineLayerConfig, + YAxisMode, + YConfig, +} from './types'; import { checkScaleOperation, getAxisName, From 02ba3944fb10c00854c9ee7771ba2eabcc60084c Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Mon, 18 Apr 2022 12:10:58 +0300 Subject: [PATCH 121/213] 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 122/213] [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 b6de93ed878e603b0c9367007348012505ad2898 Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Mon, 18 Apr 2022 15:14:11 +0300 Subject: [PATCH 123/213] Fix tests and types --- .../expression_xy/common/__mocks__/index.ts | 73 ++++++++++--------- .../expression_functions/data_layer.test.ts | 3 + .../test/functional/apps/lens/smokescreen.ts | 4 +- 3 files changed, 44 insertions(+), 36 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 e34428d00084b..002dfc6beec74 100644 --- a/src/plugins/chart_expressions/expression_xy/common/__mocks__/index.ts +++ b/src/plugins/chart_expressions/expression_xy/common/__mocks__/index.ts @@ -57,6 +57,9 @@ export const sampleLayer: DataLayerConfigResult = { xScaleType: 'ordinal', yScaleType: 'linear', isHistogram: false, + isHorizontal: false, + isStacked: false, + isPercentage: false, palette: mockPaletteOutput, table: createSampleDatatableWithRows([]), }; @@ -64,9 +67,6 @@ export const sampleLayer: DataLayerConfigResult = { export const createArgsWithLayers = ( layers: DataLayerConfigResult | DataLayerConfigResult[] = sampleLayer ): XYProps => ({ - xTitle: '', - yTitle: '', - yRightTitle: '', legend: { type: 'legendConfig', isVisible: false, @@ -74,38 +74,43 @@ export const createArgsWithLayers = ( }, valueLabels: 'hide', valuesInLegend: false, - axisTitlesVisibilitySettings: { - type: 'axisTitlesVisibilityConfig', - x: true, - yLeft: true, - yRight: true, - }, - tickLabelsVisibilitySettings: { - type: 'tickLabelsConfig', - x: true, - yLeft: false, - yRight: false, - }, - labelsOrientation: { - type: 'labelsOrientationConfig', - x: 0, - yLeft: -90, - yRight: -45, - }, - gridlinesVisibilitySettings: { - type: 'gridlinesConfig', - x: true, - yLeft: false, - yRight: false, - }, - yLeftExtent: { - mode: 'full', - type: 'axisExtentConfig', - }, - yRightExtent: { - mode: 'full', - type: 'axisExtentConfig', + xAxisConfig: { + type: 'xAxisConfig', + position: 'bottom', + showGridLines: true, + labelsOrientation: 0, + showLabels: true, + showTitle: true, + title: '', }, + axes: [ + { + type: 'yAxisConfig', + position: 'right', + showGridLines: false, + labelsOrientation: -45, + showLabels: false, + showTitle: true, + title: '', + extent: { + mode: 'full', + type: 'axisExtentConfig', + }, + }, + { + type: 'yAxisConfig', + position: 'left', + showGridLines: false, + labelsOrientation: -90, + showLabels: false, + showTitle: true, + title: '', + extent: { + mode: 'full', + type: 'axisExtentConfig', + }, + }, + ], layers: Array.isArray(layers) ? layers : [layers], }); 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 14673ba25f864..86fdfd304e895 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 @@ -23,6 +23,9 @@ describe('dataLayerConfig', () => { xScaleType: 'linear', yScaleType: 'linear', isHistogram: false, + isHorizontal: false, + isPercentage: false, + isStacked: false, palette: mockPaletteOutput, }; diff --git a/x-pack/test/functional/apps/lens/smokescreen.ts b/x-pack/test/functional/apps/lens/smokescreen.ts index 721ff1e210326..141fc7f68a01d 100644 --- a/x-pack/test/functional/apps/lens/smokescreen.ts +++ b/x-pack/test/functional/apps/lens/smokescreen.ts @@ -286,14 +286,14 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.lens.waitForVisualization('xyVisChart'); let data = await PageObjects.lens.getCurrentChartDebugState(); - expect(data?.axes?.y?.[0].title).to.eql(axisTitle); + expect(data?.axes?.y?.[1].title).to.eql(axisTitle); // hide the gridlines await testSubjects.click('lnsshowyLeftAxisGridlines'); await PageObjects.lens.waitForVisualization('xyVisChart'); data = await PageObjects.lens.getCurrentChartDebugState(); - expect(data?.axes?.y?.[0].gridlines.length).to.eql(0); + expect(data?.axes?.y?.[1].gridlines.length).to.eql(0); }); it('should transition from a multi-layer stacked bar to donut chart using suggestions', async () => { From 12d9c8aa91adec1b2bf7c551e09b3583f32047fd Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Mon, 18 Apr 2022 15:54:46 +0300 Subject: [PATCH 124/213] Fix checks --- .../common/expression_functions/y_axis_config.ts | 2 +- .../expression_xy/public/components/xy_chart.tsx | 2 +- .../plugins/lens/public/xy_visualization/visualization.tsx | 7 +------ 3 files changed, 3 insertions(+), 8 deletions(-) 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 378f9a23f1d60..6cd12a2ce0602 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 @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import type { ExpressionFunctionDefinition } from '@kbn/expressions-plugin/common'; import { Y_AXIS_CONFIG, AxisModes, AXIS_EXTENT_CONFIG, YScaleTypes } from '../constants'; -import { YConfig, YConfigResult } from '../types'; +import { YAxisConfig, YAxisConfigResult } from '../types'; export const yAxisConfigFunction: ExpressionFunctionDefinition< typeof Y_AXIS_CONFIG, 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 4576215ec5c5c..1c58c877d986a 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 { XYChartProps } from '../../common/types'; import { isHorizontalChart, getAnnotationsLayers, diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx index 9a60d3b586c06..d894a6e14b628 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx @@ -16,12 +16,7 @@ import { ThemeServiceStart } from '@kbn/core/public'; import { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public'; import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; import { VIS_EVENT_TO_TRIGGER } from '@kbn/visualizations-plugin/public'; -import { - FillStyle, - SeriesType, - YAxisMode, - ExtendedYConfig, -} from '@kbn/expression-xy-plugin/common'; +import { FillStyle } from '@kbn/expression-xy-plugin/common'; import { getSuggestions } from './xy_suggestions'; import { XyToolbar } from './xy_config_panel'; import { DimensionEditor } from './xy_config_panel/dimension_editor'; From 9060da3e1fce2a26154c21edf86ee98e4869f2ff Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Mon, 18 Apr 2022 16:11:44 +0300 Subject: [PATCH 125/213] Remove unneeded imports --- .../lens/public/xy_visualization/reference_line_helpers.tsx | 1 - x-pack/plugins/lens/public/xy_visualization/state_helpers.ts | 1 - .../lens/public/xy_visualization/visualization_helpers.tsx | 1 - .../reference_line_config_panel/reference_line_panel.tsx | 2 +- 4 files changed, 1 insertion(+), 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 cfebc70d88516..1fa78da00517a 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 @@ -7,7 +7,6 @@ import { groupBy, partition } from 'lodash'; import { i18n } from '@kbn/i18n'; -import type { YAxisMode, ExtendedYConfig } from '@kbn/expression-xy-plugin/common'; import { Datatable } from '@kbn/expressions-plugin/public'; import { layerTypes } from '../../common'; import type { DatasourceLayers, FramePublicAPI, Visualization } from '../types'; 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 b2cd88dbb2d26..975676e241b76 100644 --- a/x-pack/plugins/lens/public/xy_visualization/state_helpers.ts +++ b/x-pack/plugins/lens/public/xy_visualization/state_helpers.ts @@ -6,7 +6,6 @@ */ import { EuiIconType } from '@elastic/eui/src/components/icon/icon'; -import type { SeriesType, ExtendedYConfig } from '@kbn/expression-xy-plugin/common'; import type { FramePublicAPI, DatasourcePublicAPI } from '../types'; import { visualizationTypes, 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 e57e1c268264c..c042741c5c224 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization_helpers.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/visualization_helpers.tsx @@ -7,7 +7,6 @@ import { i18n } from '@kbn/i18n'; import { uniq } from 'lodash'; -import { SeriesType } from '@kbn/expression-xy-plugin/common'; import { DatasourceLayers, OperationMetadata, VisualizationType } from '../types'; import { State, diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_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 index ba073a104d983..0ba32f3615939 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/reference_line_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,7 +9,7 @@ import React, { useCallback } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiButtonGroup, EuiFormRow } from '@elastic/eui'; import type { PaletteRegistry } from '@kbn/coloring'; -import { FillStyle, ExtendedYConfig } from '@kbn/expression-xy-plugin/common'; +import { FillStyle } from '@kbn/expression-xy-plugin/common'; import type { VisualizationDimensionEditorProps } from '../../../types'; import { State, XYState, XYReferenceLineLayerConfig, YConfig } from '../../types'; import { FormatFactory } from '../../../../common'; From 265d44f98f4b7e082573f06ce833a1c70013c6a4 Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Mon, 18 Apr 2022 16:35:05 +0300 Subject: [PATCH 126/213] Fix imports --- .../lens/public/shared_components/axis_title_settings.tsx | 2 +- .../xy_config_panel/axis_settings_popover.tsx | 2 +- .../xy_visualization/xy_config_panel/dimension_editor.tsx | 1 - .../lens/public/xy_visualization/xy_config_panel/index.tsx | 4 ++-- 4 files changed, 4 insertions(+), 5 deletions(-) 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 8e0c98ad61e8a..daa938451d83d 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 @@ -8,7 +8,7 @@ import React, { useCallback, useState } from 'react'; import { EuiSpacer, EuiFormRow } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { AxesSettingsConfig } from '@kbn/expression-xy-plugin/common'; +import { AxesSettingsConfig } from '../xy_visualization/types'; import { LabelMode, useDebouncedValue, VisLabel } from '.'; type AxesSettingsConfigKeys = keyof AxesSettingsConfig; 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 5e625fd541502..0a084574cced7 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 @@ -17,7 +17,7 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { isEqual } from 'lodash'; -import { AxesSettingsConfig } from '@kbn/expression-xy-plugin/common'; +import { AxisExtentConfig } from '@kbn/expression-xy-plugin/common'; import { ToolbarButtonProps } from '@kbn/kibana-react-plugin/public'; import { XYLayerConfig, AxesSettingsConfig } from '../types'; import { ToolbarPopover, useDebouncedValue, AxisTitleSettings } from '../../shared_components'; 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 96a9a8b057162..3217b15e37f46 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 @@ -9,7 +9,6 @@ import React, { useCallback } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiButtonGroup, EuiFormRow, htmlIdGenerator } from '@elastic/eui'; import type { PaletteRegistry } from '@kbn/coloring'; -import { YAxisMode, ExtendedYConfig } from '@kbn/expression-xy-plugin/common'; import type { VisualizationDimensionEditorProps } from '../../types'; import { State, XYState, XYDataLayerConfig, YConfig, YAxisMode } from '../types'; import { FormatFactory } from '../../../common'; 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..9b39fcd0ddb55 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 @@ -9,9 +9,9 @@ import React, { memo, useCallback } from 'react'; import { i18n } from '@kbn/i18n'; import { Position, ScaleType, VerticalAlignment, HorizontalAlignment } from '@elastic/charts'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import { AxesSettingsConfig, AxisExtentConfig } from '@kbn/expression-xy-plugin/common'; +import { AxisExtentConfig } from '@kbn/expression-xy-plugin/common'; import type { VisualizationToolbarProps, FramePublicAPI } from '../../types'; -import { State, XYState } from '../types'; +import { State, XYState, AxesSettingsConfig } from '../types'; import { isHorizontalChart } from '../state_helpers'; import { LegendSettingsPopover } from '../../shared_components'; import { AxisSettingsPopover } from './axis_settings_popover'; From 07c234b826a61cbe223b4d02d7be6dcbef2d54d8 Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Mon, 18 Apr 2022 16:56:13 +0300 Subject: [PATCH 127/213] Fix imports --- 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 b61b556330de4..99d2d3a10ed33 100644 --- a/x-pack/plugins/lens/public/index.ts +++ b/x-pack/plugins/lens/public/index.ts @@ -19,6 +19,7 @@ export type { XYDataLayerConfig, XYAnnotationLayerConfig, YAxisMode, + SeriesType, } from './xy_visualization/types'; export type { DatasourcePublicAPI, @@ -77,7 +78,6 @@ export type { LayerType, LineStyle, FillStyle, - SeriesType, YScaleType, XScaleType, AxisConfig, From b7470a978f228237a8dd6bbf96b7c4919bcaa778 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Mon, 18 Apr 2022 14:46:27 +0000 Subject: [PATCH 128/213] [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' --- .../expression_xy/common/expression_functions/x_axis_config.ts | 2 +- .../expression_xy/common/expression_functions/y_config.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/x_axis_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/x_axis_config.ts index 9d16ad15e0cc7..7f6f17e856d4b 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/x_axis_config.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/x_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 { AxisConfig, XAxisConfigResult } from '../types'; import { X_AXIS_CONFIG } from '../constants'; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/y_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/y_config.ts index ebeefd4902229..4a5b4ba19cfe3 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/y_config.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/y_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 { Y_CONFIG } from '../constants'; import { YConfig, YConfigResult } from '../types'; From acabb8f6af6d42be9b13fb65d9bf07e79d01aac4 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Tue, 19 Apr 2022 10:52:01 +0300 Subject: [PATCH 129/213] 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 130/213] 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 131/213] 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 132/213] 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 133/213] 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 134/213] 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 135/213] 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 136/213] 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 137/213] 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 138/213] 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 139/213] 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 140/213] 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 141/213] 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 142/213] 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 143/213] 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 144/213] 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 145/213] 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 146/213] 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 147/213] 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 148/213] 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 3e616089f7b1c503e53681e46db4f859a2d3a2ad Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Thu, 21 Apr 2022 15:24:48 +0300 Subject: [PATCH 149/213] Some fixes --- .../expression_functions/common_axis_args.ts | 71 +++++++ .../common_data_layer_args.ts | 19 ++ .../expression_functions/common_xy_args.ts | 54 ++--- .../common_y_config_args.ts | 35 ++-- .../common/expression_functions/data_layer.ts | 107 ---------- .../extended_data_layer.ts | 99 ---------- .../expression_functions/extended_y_config.ts | 13 +- .../expression_functions/layered_xy_vis.ts | 15 -- .../expression_functions/x_axis_config.ts | 95 +-------- .../common/expression_functions/xy_vis.ts | 34 ---- .../common/expression_functions/xy_vis_fn.ts | 57 ++++-- .../expression_functions/y_axis_config.ts | 111 +---------- .../common/expression_functions/y_config.ts | 20 +- .../expression_xy/common/i18n/index.tsx | 136 +++++++++---- .../common/types/expression_functions.ts | 15 ++ .../__snapshots__/xy_chart.test.tsx.snap | 185 +++++++++++++----- .../public/components/xy_chart.test.tsx | 12 +- .../public/components/xy_chart.tsx | 4 +- 18 files changed, 427 insertions(+), 655 deletions(-) create mode 100644 src/plugins/chart_expressions/expression_xy/common/expression_functions/common_axis_args.ts diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_axis_args.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_axis_args.ts new file mode 100644 index 0000000000000..8e45f0a158cd8 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_axis_args.ts @@ -0,0 +1,71 @@ +/* + * 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 { strings } from '../i18n'; +import { XAxisConfigFn, YAxisConfigFn } from '../types'; + +type CommonAxisConfigFn = XAxisConfigFn | YAxisConfigFn; + +export const commonAxisConfigArgs: Omit< + CommonAxisConfigFn['args'], + 'scaleType' | 'mode' | 'extent' | 'boundsMargin' +> = { + title: { + types: ['string'], + help: strings.getAxisTitleHelp(), + }, + id: { + types: ['string'], + help: strings.getAxisIdHelp(), + }, + position: { + types: ['string'], + help: strings.getAxisPositionHelp(), + default: 'left', + }, + hide: { + types: ['boolean'], + help: strings.getAxisHideHelp(), + }, + labelColor: { + types: ['string'], + help: strings.getAxisLabelColorHelp(), + }, + showOverlappingLabels: { + types: ['boolean'], + help: strings.getAxisShowOverlappingLabelsHelp(), + }, + showDuplicates: { + types: ['boolean'], + help: strings.getAxisShowDuplicatesHelp(), + }, + showGridLines: { + types: ['boolean'], + help: strings.getAxisShowGridLinesHelp(), + default: false, + }, + labelsOrientation: { + types: ['number'], + options: [0, -90, -45], + help: strings.getAxisLabelsOrientationHelp(), + }, + showLabels: { + types: ['boolean'], + help: strings.getAxisShowLabelsHelp(), + default: true, + }, + showTitle: { + types: ['boolean'], + help: strings.getAxisShowTitleHelp(), + default: true, + }, + truncate: { + types: ['number'], + help: strings.getAxisTruncateHelp(), + }, +}; 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..bc40f2e7f6c33 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 @@ -40,6 +40,21 @@ export const commonDataLayerArgs: Omit = { default: false, help: strings.getIsHistogramHelp(), }, + isPercentage: { + types: ['boolean'], + default: false, + help: strings.getIsPercentageHelp(), + }, + isStacked: { + types: ['boolean'], + default: false, + help: strings.getIsStackedHelp(), + }, + isHorizontal: { + types: ['boolean'], + default: false, + help: strings.getIsHorizontalHelp(), + }, yScaleType: { options: [...Object.values(YScaleTypes)], help: strings.getYScaleTypeHelp(), @@ -69,4 +84,8 @@ export const commonDataLayerArgs: Omit = { help: strings.getPaletteHelp(), default: '{palette}', }, + xAxisId: { + types: ['string'], + help: strings.getXAxisIdHelp(), + }, }; 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..8fc5618346cab 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 @@ -7,16 +7,13 @@ */ import { - AXIS_EXTENT_CONFIG, - AXIS_TITLES_VISIBILITY_CONFIG, EndValues, FittingFunctions, - GRID_LINES_CONFIG, - LABELS_ORIENTATION_CONFIG, LEGEND_CONFIG, - TICK_LABELS_CONFIG, ValueLabelModes, XYCurveTypes, + X_AXIS_CONFIG, + Y_AXIS_CONFIG } from '../constants'; import { strings } from '../i18n'; import { LayeredXyVisFn, XyVisFn } from '../types'; @@ -27,28 +24,6 @@ export const commonXYArgs: Omit< CommonXYFn['args'], '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(), - default: `{${AXIS_EXTENT_CONFIG}}`, - }, - yRightExtent: { - types: [AXIS_EXTENT_CONFIG], - help: strings.getYRightExtentHelp(), - default: `{${AXIS_EXTENT_CONFIG}}`, - }, legend: { types: [LEGEND_CONFIG], help: strings.getLegendHelp(), @@ -78,22 +53,6 @@ export const commonXYArgs: Omit< strict: true, default: ValueLabelModes.HIDE, }, - 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)], @@ -118,4 +77,13 @@ export const commonXYArgs: Omit< types: ['string'], help: strings.getAriaLabelHelp(), }, + xAxisConfig: { + types: [X_AXIS_CONFIG], + help: strings.getXAxisConfigHelp(), + }, + axes: { + types: [Y_AXIS_CONFIG], + help: strings.getAxesHelp(), + multi: true, + }, }; 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..4dd80ebcb5f76 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 @@ -6,28 +6,23 @@ * 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(), - }, -}; +export const commonYConfigArgs: Pick = + { + forAccessor: { + types: ['string'], + help: strings.getForAccessorHelp(), + }, + color: { + types: ['string'], + help: strings.getColorHelp(), + }, + axisId: { + types: ['string'], + help: strings.getAxisIdHelp(), + }, + }; 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 b62493fcde14d..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 @@ -17,113 +17,6 @@ export const dataLayerFunction: DataLayerFn = { type: DATA_LAYER, 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, - }, - isPercentage: { - types: ['boolean'], - default: false, - help: i18n.translate('expressionXY.dataLayer.isPercentage.help', { - defaultMessage: 'Whether to layout the chart has percentage mode.', - }), - }, - isStacked: { - types: ['boolean'], - default: false, - help: i18n.translate('expressionXY.dataLayer.isPercentage.help', { - defaultMessage: 'Layout of the chart in stacked mode.', - }), - }, - isHorizontal: { - types: ['boolean'], - default: false, - help: i18n.translate('expressionXY.dataLayer.isPercentage.help', { - defaultMessage: 'Layout of the chart is horizontal', - }), - }, - 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}', - }, - xAxisId: { - types: ['string'], - help: i18n.translate('expressionXY.dataLayer.xAxisId.help', { - defaultMessage: 'Id of x-axis', - }), - }, - }, args: { ...commonDataLayerArgs }, fn(table, args) { return { 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 9a307e8880ffc..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 @@ -18,105 +18,6 @@ export const extendedDataLayerFunction: ExtendedDataLayerFn = { 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, - }, - isPercentage: { - types: ['boolean'], - default: false, - help: i18n.translate('expressionXY.dataLayer.isPercentage.help', { - defaultMessage: 'Layout of the chart in percentage mode', - }), - }, - isStacked: { - types: ['boolean'], - default: false, - help: i18n.translate('expressionXY.dataLayer.isPercentage.help', { - defaultMessage: 'Layout of the chart in stacked mode', - }), - }, - isHorizontal: { - types: ['boolean'], - default: false, - help: i18n.translate('expressionXY.dataLayer.isPercentage.help', { - defaultMessage: 'Layout of the chart is horizontal', - }), - }, - 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'], diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_y_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_y_config.ts index 6b3bc3358b310..1e0090f1b5e40 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_y_config.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_y_config.ts @@ -18,12 +18,7 @@ import { strings } from '../i18n'; import { ExtendedYConfigFn } from '../types'; import { commonYConfigArgs } from './common_y_config_args'; -export const extendedYConfigFunction: ExpressionFunctionDefinition< - typeof EXTENDED_Y_CONFIG, - null, - ExtendedYConfig, - ExtendedYConfigResult -> = { +export const extendedYConfigFunction: ExtendedYConfigFn = { name: EXTENDED_Y_CONFIG, aliases: [], type: EXTENDED_Y_CONFIG, @@ -75,12 +70,6 @@ export const extendedYConfigFunction: ExpressionFunctionDefinition< }), strict: true, }, - axisId: { - types: ['string'], - help: i18n.translate('expressionXY.yConfig.axisId.help', { - defaultMessage: 'An optional id of axis', - }), - }, }, fn(input, args) { return { 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 2c1397a254696..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 @@ -14,8 +14,6 @@ import { EXTENDED_REFERENCE_LINE_LAYER, LAYERED_XY_VIS, EXTENDED_ANNOTATION_LAYER, - X_AXIS_CONFIG, - Y_AXIS_CONFIG, } from '../constants'; import { logDatatables } from '../utils'; import { commonXYArgs } from './common_xy_args'; @@ -36,19 +34,6 @@ export const layeredXyVisFunction: LayeredXyVisFn = { }), multi: true, }, - axes: { - types: [Y_AXIS_CONFIG], - help: i18n.translate('expressionXY.layeredXyVis.axes.help', { - defaultMessage: 'Specifies the configs for axes', - }), - multi: true, - }, - xAxisConfig: { - types: [X_AXIS_CONFIG], - help: i18n.translate('expressionXY.xyVis.xAxisConfig.help', { - defaultMessage: 'Specifies the configs for x-axis', - }), - }, }, fn(data, args, handlers) { const layers = appendLayerIds(args.layers ?? [], 'layers'); diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/x_axis_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/x_axis_config.ts index 7f6f17e856d4b..d6fbe3b52b2ed 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/x_axis_config.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/x_axis_config.ts @@ -6,102 +6,19 @@ * Side Public License, v 1. */ -import { i18n } from '@kbn/i18n'; -import type { ExpressionFunctionDefinition } from '@kbn/expressions-plugin/common'; -import { AxisConfig, XAxisConfigResult } from '../types'; +import { strings } from '../i18n'; +import { XAxisConfigFn } from '../types'; import { X_AXIS_CONFIG } from '../constants'; +import { commonAxisConfigArgs } from './common_axis_args'; -export const xAxisConfigFunction: ExpressionFunctionDefinition< - typeof X_AXIS_CONFIG, - null, - AxisConfig, - XAxisConfigResult -> = { +export const xAxisConfigFunction: XAxisConfigFn = { name: X_AXIS_CONFIG, aliases: [], type: X_AXIS_CONFIG, - help: i18n.translate('expressionXY.axisConfig.help', { - defaultMessage: `Configure the xy chart's axis config`, - }), + help: strings.getXAxisConfigFnHelp(), inputTypes: ['null'], args: { - title: { - types: ['string'], - help: i18n.translate('expressionXY.axisConfig.title.help', { - defaultMessage: 'Title of axis', - }), - }, - id: { - types: ['string'], - help: i18n.translate('expressionXY.axisConfig.id.help', { - defaultMessage: 'Id of axis', - }), - }, - position: { - types: ['string'], - help: i18n.translate('expressionXY.axisConfig.position.help', { - defaultMessage: 'Position of the axis', - }), - default: 'left', - }, - hide: { - types: ['boolean'], - help: i18n.translate('expressionXY.axisConfig.boolean.help', { - defaultMessage: 'Hide the specified axis', - }), - }, - labelColor: { - types: ['string'], - help: i18n.translate('expressionXY.axisConfig.labelColor.help', { - defaultMessage: 'Color of the axis labels', - }), - }, - showOverlappingLabels: { - types: ['boolean'], - help: i18n.translate('expressionXY.axisConfig.showOverlappingLabels.help', { - defaultMessage: 'Show overlapping labels', - }), - }, - showDuplicates: { - types: ['boolean'], - help: i18n.translate('expressionXY.axisConfig.showDuplicates.help', { - defaultMessage: 'Show duplicated ticks', - }), - }, - showGridLines: { - types: ['boolean'], - help: i18n.translate('expressionXY.axisConfig.showGridLines.help', { - defaultMessage: 'Specifies whether or not the gridlines of the axis are visible.', - }), - default: false, - }, - labelsOrientation: { - types: ['number'], - options: [0, -90, -45], - help: i18n.translate('expressionXY.axisConfig.labelsOrientation.help', { - defaultMessage: 'Specifies the labels orientation of the axis.', - }), - }, - showLabels: { - types: ['boolean'], - help: i18n.translate('expressionXY.axisConfig.showLabels.help', { - defaultMessage: 'Show labels', - }), - default: true, - }, - showTitle: { - types: ['boolean'], - help: i18n.translate('expressionXY.axisConfig.showTitle.help', { - defaultMessage: 'Show title of the axis', - }), - default: true, - }, - truncate: { - types: ['number'], - help: i18n.translate('expressionXY.axisConfig.truncate.help', { - defaultMessage: 'The number of symbols before truncating', - }), - }, + ...commonAxisConfigArgs, }, fn(input, args) { return { 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 872dc6c1b7387..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 @@ -11,27 +11,6 @@ import { XY_VIS, DATA_LAYER, REFERENCE_LINE_LAYER, ANNOTATION_LAYER } from '../c import { strings } from '../i18n'; import { commonXYArgs } from './common_xy_args'; -const validateExtents = (dataLayers: DataLayerConfigResult[], axes?: YAxisConfigResult[]) => { - const lineSeries = dataLayers.filter(({ seriesType }) => seriesType === SeriesTypes.LINE); - const hasBarOrArea = - dataLayers.filter( - ({ seriesType }) => seriesType === SeriesTypes.BAR || seriesType === SeriesTypes.AREA - ).length > 0; - axes?.forEach((axis) => { - if ( - hasBarOrArea && - axis.extent?.mode === AxisExtentModes.CUSTOM && - !areValidBounds(axis.extent) - ) { - throw new Error(errors.extendBoundsAreInvalidError()); - } - - if (!lineSeries.length && axis.extent?.mode === AxisExtentModes.DATA_BOUNDS) { - throw new Error(errors.dataBoundsForNotLineChartError()); - } - }); -}; - export const xyVisFunction: XyVisFn = { name: XY_VIS, type: 'render', @@ -54,19 +33,6 @@ export const xyVisFunction: XyVisFn = { help: strings.getAnnotationLayerHelp(), multi: true, }, - xAxisConfig: { - types: [X_AXIS_CONFIG], - help: i18n.translate('expressionXY.xyVis.xAxisConfig.help', { - defaultMessage: 'Specifies the configs for x-axis', - }), - }, - axes: { - types: [Y_AXIS_CONFIG], - help: i18n.translate('expressionXY.xyVis.axes.help', { - defaultMessage: 'Specifies the configs for y-axes', - }), - multi: true, - }, }, async fn(data, args, handlers) { const { xyVisFn } = await import('./xy_vis_fn'); 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 23516508dcb09..0f124f9c65c34 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 @@ -8,9 +8,21 @@ import { i18n } from '@kbn/i18n'; import { Dimension, prepareLogTable } from '@kbn/visualizations-plugin/common/utils'; -import { AxisExtentModes, LayerTypes, ValueLabelModes, XY_VIS_RENDERER } from '../constants'; +import { + AxisExtentModes, + LayerTypes, + ValueLabelModes, + XY_VIS_RENDERER, + SeriesTypes, +} from '../constants'; import { appendLayerIds } from '../helpers'; -import { AxisExtentConfigResult, DataLayerConfigResult, XYLayerConfig, XyVisFn } from '../types'; +import { + AxisExtentConfigResult, + DataLayerConfigResult, + XYLayerConfig, + XyVisFn, + YAxisConfigResult, +} from '../types'; import { getLayerDimensions } from '../utils'; const errors = { @@ -37,26 +49,34 @@ const errors = { }), }; -const validateExtent = ( - extent: AxisExtentConfigResult, - hasBarOrArea: boolean, - dataLayers: DataLayerConfigResult[] -) => { +function areValidBounds(extent: AxisExtentConfigResult) { 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()); - } + return isValidLowerBound && isValidUpperBound; +} + +const validateExtents = (dataLayers: DataLayerConfigResult[], axes?: YAxisConfigResult[]) => { + const lineSeries = dataLayers.filter(({ seriesType }) => seriesType === SeriesTypes.LINE); + const hasBarOrArea = + dataLayers.filter( + ({ seriesType }) => seriesType === SeriesTypes.BAR || seriesType === SeriesTypes.AREA + ).length > 0; + axes?.forEach((axis) => { + if ( + hasBarOrArea && + axis.extent?.mode === AxisExtentModes.CUSTOM && + !areValidBounds(axis.extent) + ) { + throw new Error(errors.extendBoundsAreInvalidError()); + } + + if (!lineSeries.length && axis.extent?.mode === AxisExtentModes.DATA_BOUNDS) { + throw new Error(errors.dataBoundsForNotLineChartError()); + } + }); }; export const xyVisFn: XyVisFn['fn'] = async (data, args, handlers) => { @@ -83,8 +103,7 @@ export const xyVisFn: XyVisFn['fn'] = async (data, args, handlers) => { 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); + validateExtents(dataLayers, args.axes); if (!hasArea && args.fillOpacity !== undefined) { throw new Error(errors.notUsedFillOpacityError()); 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 6cd12a2ce0602..6e1b2d17915dc 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,127 +6,36 @@ * Side Public License, v 1. */ -import { i18n } from '@kbn/i18n'; -import type { ExpressionFunctionDefinition } from '@kbn/expressions-plugin/common'; +import { strings } from '../i18n'; import { Y_AXIS_CONFIG, AxisModes, AXIS_EXTENT_CONFIG, YScaleTypes } from '../constants'; -import { YAxisConfig, YAxisConfigResult } from '../types'; +import { YAxisConfigFn } from '../types'; +import { commonAxisConfigArgs } from './common_axis_args'; -export const yAxisConfigFunction: ExpressionFunctionDefinition< - typeof Y_AXIS_CONFIG, - null, - YAxisConfig, - YAxisConfigResult -> = { +export const yAxisConfigFunction: YAxisConfigFn = { name: Y_AXIS_CONFIG, aliases: [], type: Y_AXIS_CONFIG, - help: i18n.translate('expressionXY.axisConfig.help', { - defaultMessage: `Configure the xy chart's axis config`, - }), + help: strings.getYAxisConfigFnHelp(), inputTypes: ['null'], args: { - title: { - types: ['string'], - help: i18n.translate('expressionXY.axisConfig.title.help', { - defaultMessage: 'Title of axis', - }), - }, - id: { - types: ['string'], - help: i18n.translate('expressionXY.axisConfig.id.help', { - defaultMessage: 'Id of axis', - }), - }, - position: { - types: ['string'], - help: i18n.translate('expressionXY.axisConfig.position.help', { - defaultMessage: 'Position of the axis', - }), - default: 'left', - }, - hide: { - types: ['boolean'], - help: i18n.translate('expressionXY.axisConfig.boolean.help', { - defaultMessage: 'Hide the specified axis', - }), - }, + ...commonAxisConfigArgs, mode: { types: ['string'], options: [...Object.values(AxisModes)], - help: i18n.translate('expressionXY.axisConfig.mode.help', { - defaultMessage: 'Scale mode. Can be normal, percentage, wiggle or silhouette', - }), + help: strings.getAxisModeHelp(), }, boundsMargin: { types: ['number'], - help: i18n.translate('expressionXY.axisConfig.boundsMargin.help', { - defaultMessage: 'Margin of bounds', - }), - }, - labelColor: { - types: ['string'], - help: i18n.translate('expressionXY.axisConfig.labelColor.help', { - defaultMessage: 'Color of the axis labels', - }), - }, - showOverlappingLabels: { - types: ['boolean'], - help: i18n.translate('expressionXY.axisConfig.showOverlappingLabels.help', { - defaultMessage: 'Show overlapping labels', - }), - }, - showDuplicates: { - types: ['boolean'], - help: i18n.translate('expressionXY.axisConfig.showDuplicates.help', { - defaultMessage: 'Show duplicated ticks', - }), - }, - showGridLines: { - types: ['boolean'], - help: i18n.translate('expressionXY.axisConfig.showGridLines.help', { - defaultMessage: 'Specifies whether or not the gridlines of the axis are visible.', - }), - default: false, - }, - labelsOrientation: { - types: ['number'], - options: [0, -90, -45], - help: i18n.translate('expressionXY.axisConfig.labelsOrientation.help', { - defaultMessage: 'Specifies the labels orientation of the axis.', - }), - }, - showLabels: { - types: ['boolean'], - help: i18n.translate('expressionXY.axisConfig.showLabels.help', { - defaultMessage: 'Show labels', - }), - default: true, - }, - showTitle: { - types: ['boolean'], - help: i18n.translate('expressionXY.axisConfig.showTitle.help', { - defaultMessage: 'Show title of the axis', - }), - default: true, - }, - truncate: { - types: ['number'], - help: i18n.translate('expressionXY.axisConfig.truncate.help', { - defaultMessage: 'The number of symbols before truncating', - }), + help: strings.getAxisBoundsMarginHelp(), }, extent: { types: [AXIS_EXTENT_CONFIG], - help: i18n.translate('expressionXY.yAxisConfig.extent.help', { - defaultMessage: 'Axis extents', - }), + help: strings.getAxisExtentHelp(), default: `{${AXIS_EXTENT_CONFIG}}`, }, scaleType: { options: [...Object.values(YScaleTypes)], - help: i18n.translate('expressionXY.yAxisConfig.scaleType.help', { - defaultMessage: 'The scale type of the axis', - }), + help: strings.getAxisScaleTypeHelp(), default: YScaleTypes.LINEAR, }, }, diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/y_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/y_config.ts index 4a5b4ba19cfe3..0e92f8f73704d 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/y_config.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/y_config.ts @@ -10,6 +10,7 @@ import { i18n } from '@kbn/i18n'; import type { ExpressionFunctionDefinition } from '@kbn/expressions-plugin/common'; import { Y_CONFIG } from '../constants'; import { YConfig, YConfigResult } from '../types'; +import { commonYConfigArgs } from './common_y_config_args'; export const yConfigFunction: ExpressionFunctionDefinition< typeof Y_CONFIG, @@ -25,24 +26,7 @@ export const yConfigFunction: ExpressionFunctionDefinition< }), inputTypes: ['null'], args: { - forAccessor: { - types: ['string'], - help: i18n.translate('expressionXY.yConfig.forAccessor.help', { - defaultMessage: 'The accessor this configuration is for', - }), - }, - color: { - types: ['string'], - help: i18n.translate('expressionXY.yConfig.color.help', { - defaultMessage: 'The color of the series', - }), - }, - axisId: { - types: ['string'], - help: i18n.translate('expressionXY.yConfig.axisId.help', { - defaultMessage: 'An optional id of axis', - }), - }, + ...commonYConfigArgs, }, fn(input, args) { return { 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 225f9de0d6a7c..32d5160f9ff8f 100644 --- a/src/plugins/chart_expressions/expression_xy/common/i18n/index.tsx +++ b/src/plugins/chart_expressions/expression_xy/common/i18n/index.tsx @@ -29,26 +29,6 @@ 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.', @@ -65,22 +45,6 @@ export const strings = { 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', @@ -113,6 +77,14 @@ export const strings = { i18n.translate('expressionXY.xyVis.ariaLabel.help', { defaultMessage: 'Specifies the aria label of the xy chart', }), + getXAxisConfigHelp: () => + i18n.translate('expressionXY.xyVis.ariaLabel.help', { + defaultMessage: 'Specifies x-axis config', + }), + getAxesHelp: () => + i18n.translate('expressionXY.xyVis.ariaLabel.help', { + defaultMessage: 'Specifies y-axes configs', + }), getDataLayerFnHelp: () => i18n.translate('expressionXY.dataLayer.help', { defaultMessage: `Configure a layer in the xy chart`, @@ -137,6 +109,18 @@ export const strings = { i18n.translate('expressionXY.dataLayer.isHistogram.help', { defaultMessage: 'Whether to layout the chart as a histogram', }), + getIsStackedHelp: () => + i18n.translate('expressionXY.dataLayer.isHistogram.help', { + defaultMessage: 'Layout of the chart in stacked mode', + }), + getIsPercentageHelp: () => + i18n.translate('expressionXY.dataLayer.isHistogram.help', { + defaultMessage: 'Whether to layout the chart has percentage mode', + }), + getIsHorizontalHelp: () => + i18n.translate('expressionXY.dataLayer.isHistogram.help', { + defaultMessage: 'Layout of the chart is horizontal', + }), getYScaleTypeHelp: () => i18n.translate('expressionXY.dataLayer.yScaleType.help', { defaultMessage: 'The scale type of the y axes', @@ -161,6 +145,10 @@ export const strings = { i18n.translate('expressionXY.dataLayer.palette.help', { defaultMessage: 'Palette', }), + getXAxisIdHelp: () => + i18n.translate('expressionXY.dataLayer.xAxisId.help', { + defaultMessage: 'Id of x-axis', + }), getTableHelp: () => i18n.translate('expressionXY.layers.table.help', { defaultMessage: 'Table', @@ -189,14 +177,14 @@ export const strings = { 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', }), + getAxisIdHelp: () => + i18n.translate('expressionXY.yConfig.axisId.help', { + defaultMessage: 'Id of axis', + }), getAnnotationLayerFnHelp: () => i18n.translate('expressionXY.annotationLayer.help', { defaultMessage: `Configure an annotation layer in the xy chart`, @@ -209,4 +197,72 @@ export const strings = { i18n.translate('expressionXY.annotationLayer.annotations.help', { defaultMessage: 'Annotations', }), + getXAxisConfigFnHelp: () => + i18n.translate('expressionXY.axisConfig.help', { + defaultMessage: `Configure the xy chart's x-axis config`, + }), + getYAxisConfigFnHelp: () => + i18n.translate('expressionXY.axisConfig.help', { + defaultMessage: `Configure the xy chart's y-axis config`, + }), + getAxisModeHelp: () => + i18n.translate('expressionXY.axisConfig.mode.help', { + defaultMessage: 'Scale mode. Can be normal, percentage, wiggle or silhouette', + }), + getAxisBoundsMarginHelp: () => + i18n.translate('expressionXY.axisConfig.boundsMargin.help', { + defaultMessage: 'Margin of bounds', + }), + getAxisExtentHelp: () => + i18n.translate('expressionXY.axisConfig.extent.help', { + defaultMessage: 'Axis extents', + }), + getAxisScaleTypeHelp: () => + i18n.translate('expressionXY.axisConfig.scaleType.help', { + defaultMessage: 'The scale type of the axis', + }), + getAxisTitleHelp: () => + i18n.translate('expressionXY.axisConfig.title.help', { + defaultMessage: 'Title of axis', + }), + getAxisPositionHelp: () => + i18n.translate('expressionXY.axisConfig.position.help', { + defaultMessage: 'Position of axis', + }), + getAxisHideHelp: () => + i18n.translate('expressionXY.axisConfig.hide.help', { + defaultMessage: 'Hide the axis', + }), + getAxisLabelColorHelp: () => + i18n.translate('expressionXY.axisConfig.title.help', { + defaultMessage: 'Color of the axis labels', + }), + getAxisShowOverlappingLabelsHelp: () => + i18n.translate('expressionXY.axisConfig.showOverlappingLabels.help', { + defaultMessage: 'Show overlapping labels', + }), + getAxisShowDuplicatesHelp: () => + i18n.translate('expressionXY.axisConfig.showDuplicates.help', { + defaultMessage: 'Show duplicated ticks', + }), + getAxisShowGridLinesHelp: () => + i18n.translate('expressionXY.axisConfig.showGridLines.help', { + defaultMessage: 'Specifies whether or not the gridlines of the axis are visible', + }), + getAxisLabelsOrientationHelp: () => + i18n.translate('expressionXY.axisConfig.labelsOrientation.help', { + defaultMessage: 'Specifies the labels orientation of the axis', + }), + getAxisShowLabelsHelp: () => + i18n.translate('expressionXY.axisConfig.showLabels.help', { + defaultMessage: 'Show labels', + }), + getAxisShowTitleHelp: () => + i18n.translate('expressionXY.axisConfig.showTitle.help', { + defaultMessage: 'Show title of the axis', + }), + getAxisTruncateHelp: () => + i18n.translate('expressionXY.axisConfig.truncate.help', { + defaultMessage: 'The number of symbols before truncating', + }), }; 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 a46aa73670052..f34f678264810 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 @@ -149,6 +149,7 @@ export interface ExtendedDataLayerArgs { // palette will always be set on the expression yConfig?: YConfigResult[]; table?: Datatable; + xAxisId?: string; } export interface LegendConfig { @@ -419,3 +420,17 @@ export type LegendConfigFn = ExpressionFunctionDefinition< LegendConfig, Promise >; + +export type XAxisConfigFn = ExpressionFunctionDefinition< + typeof X_AXIS_CONFIG, + null, + AxisConfig, + XAxisConfigResult +>; + +export type YAxisConfigFn = ExpressionFunctionDefinition< + typeof Y_AXIS_CONFIG, + null, + YAxisConfig, + YAxisConfigResult +>; 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 ebd5943c3f005..c900b2e2a8298 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 @@ -261,7 +261,7 @@ exports[`XYChart component it renders area 1`] = ` gridLine={ Object { "strokeWidth": 1, - "visible": undefined, + "visible": true, } } hide={false} @@ -271,13 +271,13 @@ exports[`XYChart component it renders area 1`] = ` Object { "axisTitle": Object { "padding": undefined, - "visible": undefined, + "visible": true, }, "tickLabel": Object { "fill": undefined, "padding": undefined, - "rotation": undefined, - "visible": undefined, + "rotation": 0, + "visible": true, }, } } @@ -297,7 +297,7 @@ exports[`XYChart component it renders area 1`] = ` } gridLine={ Object { - "visible": undefined, + "visible": false, } } groupId="left" @@ -308,13 +308,13 @@ exports[`XYChart component it renders area 1`] = ` Object { "axisTitle": Object { "padding": undefined, - "visible": undefined, + "visible": true, }, "tickLabel": Object { "fill": undefined, "padding": undefined, - "rotation": undefined, - "visible": undefined, + "rotation": -90, + "visible": false, }, } } @@ -478,6 +478,9 @@ exports[`XYChart component it renders area 1`] = ` ], "columnToLabel": "{\\"a\\": \\"Label A\\", \\"b\\": \\"Label B\\", \\"d\\": \\"Label D\\"}", "isHistogram": false, + "isHorizontal": false, + "isPercentage": false, + "isStacked": false, "layerId": "first", "layerType": "data", "palette": Object { @@ -576,6 +579,10 @@ exports[`XYChart component it renders area 1`] = ` yAxesConfiguration={ Array [ Object { + "extent": Object { + "mode": "full", + "type": "axisExtentConfig", + }, "formatter": Object { "convert": [MockFunction] { "calls": Array [ @@ -599,17 +606,25 @@ exports[`XYChart component it renders area 1`] = ` }, }, "groupId": "left", + "labelsOrientation": -90, "position": "left", "series": Array [ Object { "accessor": "a", + "axisId": undefined, "layer": "first", }, Object { "accessor": "b", + "axisId": undefined, "layer": "first", }, ], + "showGridLines": false, + "showLabels": false, + "showTitle": true, + "title": "", + "type": "yAxisConfig", }, ] } @@ -665,7 +680,7 @@ exports[`XYChart component it renders bar 1`] = ` gridLine={ Object { "strokeWidth": 1, - "visible": undefined, + "visible": true, } } hide={false} @@ -675,13 +690,13 @@ exports[`XYChart component it renders bar 1`] = ` Object { "axisTitle": Object { "padding": undefined, - "visible": undefined, + "visible": true, }, "tickLabel": Object { "fill": undefined, "padding": undefined, - "rotation": undefined, - "visible": undefined, + "rotation": 0, + "visible": true, }, } } @@ -701,7 +716,7 @@ exports[`XYChart component it renders bar 1`] = ` } gridLine={ Object { - "visible": undefined, + "visible": false, } } groupId="left" @@ -712,13 +727,13 @@ exports[`XYChart component it renders bar 1`] = ` Object { "axisTitle": Object { "padding": undefined, - "visible": undefined, + "visible": true, }, "tickLabel": Object { "fill": undefined, "padding": undefined, - "rotation": undefined, - "visible": undefined, + "rotation": -90, + "visible": false, }, } } @@ -882,6 +897,9 @@ exports[`XYChart component it renders bar 1`] = ` ], "columnToLabel": "{\\"a\\": \\"Label A\\", \\"b\\": \\"Label B\\", \\"d\\": \\"Label D\\"}", "isHistogram": false, + "isHorizontal": false, + "isPercentage": false, + "isStacked": false, "layerId": "first", "layerType": "data", "palette": Object { @@ -980,6 +998,10 @@ exports[`XYChart component it renders bar 1`] = ` yAxesConfiguration={ Array [ Object { + "extent": Object { + "mode": "full", + "type": "axisExtentConfig", + }, "formatter": Object { "convert": [MockFunction] { "calls": Array [ @@ -1003,17 +1025,25 @@ exports[`XYChart component it renders bar 1`] = ` }, }, "groupId": "left", + "labelsOrientation": -90, "position": "left", "series": Array [ Object { "accessor": "a", + "axisId": undefined, "layer": "first", }, Object { "accessor": "b", + "axisId": undefined, "layer": "first", }, ], + "showGridLines": false, + "showLabels": false, + "showTitle": true, + "title": "", + "type": "yAxisConfig", }, ] } @@ -1069,23 +1099,23 @@ exports[`XYChart component it renders horizontal bar 1`] = ` gridLine={ Object { "strokeWidth": 1, - "visible": undefined, + "visible": true, } } hide={false} id="x" - position="left" + position="right" style={ Object { "axisTitle": Object { "padding": undefined, - "visible": undefined, + "visible": true, }, "tickLabel": Object { "fill": undefined, "padding": undefined, - "rotation": undefined, - "visible": undefined, + "rotation": 0, + "visible": true, }, } } @@ -1286,6 +1316,9 @@ exports[`XYChart component it renders horizontal bar 1`] = ` ], "columnToLabel": "{\\"a\\": \\"Label A\\", \\"b\\": \\"Label B\\", \\"d\\": \\"Label D\\"}", "isHistogram": false, + "isHorizontal": true, + "isPercentage": false, + "isStacked": false, "layerId": "first", "layerType": "data", "palette": Object { @@ -1411,10 +1444,12 @@ exports[`XYChart component it renders horizontal bar 1`] = ` "series": Array [ Object { "accessor": "a", + "axisId": undefined, "layer": "first", }, Object { "accessor": "b", + "axisId": undefined, "layer": "first", }, ], @@ -1473,7 +1508,7 @@ exports[`XYChart component it renders line 1`] = ` gridLine={ Object { "strokeWidth": 1, - "visible": undefined, + "visible": true, } } hide={false} @@ -1483,13 +1518,13 @@ exports[`XYChart component it renders line 1`] = ` Object { "axisTitle": Object { "padding": undefined, - "visible": undefined, + "visible": true, }, "tickLabel": Object { "fill": undefined, "padding": undefined, - "rotation": undefined, - "visible": undefined, + "rotation": 0, + "visible": true, }, } } @@ -1509,7 +1544,7 @@ exports[`XYChart component it renders line 1`] = ` } gridLine={ Object { - "visible": undefined, + "visible": false, } } groupId="left" @@ -1520,13 +1555,13 @@ exports[`XYChart component it renders line 1`] = ` Object { "axisTitle": Object { "padding": undefined, - "visible": undefined, + "visible": true, }, "tickLabel": Object { "fill": undefined, "padding": undefined, - "rotation": undefined, - "visible": undefined, + "rotation": -90, + "visible": false, }, } } @@ -1690,6 +1725,9 @@ exports[`XYChart component it renders line 1`] = ` ], "columnToLabel": "{\\"a\\": \\"Label A\\", \\"b\\": \\"Label B\\", \\"d\\": \\"Label D\\"}", "isHistogram": false, + "isHorizontal": false, + "isPercentage": false, + "isStacked": false, "layerId": "first", "layerType": "data", "palette": Object { @@ -1788,6 +1826,10 @@ exports[`XYChart component it renders line 1`] = ` yAxesConfiguration={ Array [ Object { + "extent": Object { + "mode": "full", + "type": "axisExtentConfig", + }, "formatter": Object { "convert": [MockFunction] { "calls": Array [ @@ -1811,17 +1853,25 @@ exports[`XYChart component it renders line 1`] = ` }, }, "groupId": "left", + "labelsOrientation": -90, "position": "left", "series": Array [ Object { "accessor": "a", + "axisId": undefined, "layer": "first", }, Object { "accessor": "b", + "axisId": undefined, "layer": "first", }, ], + "showGridLines": false, + "showLabels": false, + "showTitle": true, + "title": "", + "type": "yAxisConfig", }, ] } @@ -1877,7 +1927,7 @@ exports[`XYChart component it renders stacked area 1`] = ` gridLine={ Object { "strokeWidth": 1, - "visible": undefined, + "visible": true, } } hide={false} @@ -1887,13 +1937,13 @@ exports[`XYChart component it renders stacked area 1`] = ` Object { "axisTitle": Object { "padding": undefined, - "visible": undefined, + "visible": true, }, "tickLabel": Object { "fill": undefined, "padding": undefined, - "rotation": undefined, - "visible": undefined, + "rotation": 0, + "visible": true, }, } } @@ -1913,7 +1963,7 @@ exports[`XYChart component it renders stacked area 1`] = ` } gridLine={ Object { - "visible": undefined, + "visible": false, } } groupId="left" @@ -1924,13 +1974,13 @@ exports[`XYChart component it renders stacked area 1`] = ` Object { "axisTitle": Object { "padding": undefined, - "visible": undefined, + "visible": true, }, "tickLabel": Object { "fill": undefined, "padding": undefined, - "rotation": undefined, - "visible": undefined, + "rotation": -90, + "visible": false, }, } } @@ -2094,6 +2144,9 @@ exports[`XYChart component it renders stacked area 1`] = ` ], "columnToLabel": "{\\"a\\": \\"Label A\\", \\"b\\": \\"Label B\\", \\"d\\": \\"Label D\\"}", "isHistogram": false, + "isHorizontal": false, + "isPercentage": false, + "isStacked": true, "layerId": "first", "layerType": "data", "palette": Object { @@ -2192,6 +2245,10 @@ exports[`XYChart component it renders stacked area 1`] = ` yAxesConfiguration={ Array [ Object { + "extent": Object { + "mode": "full", + "type": "axisExtentConfig", + }, "formatter": Object { "convert": [MockFunction] { "calls": Array [ @@ -2215,17 +2272,25 @@ exports[`XYChart component it renders stacked area 1`] = ` }, }, "groupId": "left", + "labelsOrientation": -90, "position": "left", "series": Array [ Object { "accessor": "a", + "axisId": undefined, "layer": "first", }, Object { "accessor": "b", + "axisId": undefined, "layer": "first", }, ], + "showGridLines": false, + "showLabels": false, + "showTitle": true, + "title": "", + "type": "yAxisConfig", }, ] } @@ -2281,7 +2346,7 @@ exports[`XYChart component it renders stacked bar 1`] = ` gridLine={ Object { "strokeWidth": 1, - "visible": undefined, + "visible": true, } } hide={false} @@ -2291,13 +2356,13 @@ exports[`XYChart component it renders stacked bar 1`] = ` Object { "axisTitle": Object { "padding": undefined, - "visible": undefined, + "visible": true, }, "tickLabel": Object { "fill": undefined, "padding": undefined, - "rotation": undefined, - "visible": undefined, + "rotation": 0, + "visible": true, }, } } @@ -2317,7 +2382,7 @@ exports[`XYChart component it renders stacked bar 1`] = ` } gridLine={ Object { - "visible": undefined, + "visible": false, } } groupId="left" @@ -2328,13 +2393,13 @@ exports[`XYChart component it renders stacked bar 1`] = ` Object { "axisTitle": Object { "padding": undefined, - "visible": undefined, + "visible": true, }, "tickLabel": Object { "fill": undefined, "padding": undefined, - "rotation": undefined, - "visible": undefined, + "rotation": -90, + "visible": false, }, } } @@ -2498,6 +2563,9 @@ exports[`XYChart component it renders stacked bar 1`] = ` ], "columnToLabel": "{\\"a\\": \\"Label A\\", \\"b\\": \\"Label B\\", \\"d\\": \\"Label D\\"}", "isHistogram": false, + "isHorizontal": false, + "isPercentage": false, + "isStacked": true, "layerId": "first", "layerType": "data", "palette": Object { @@ -2596,6 +2664,10 @@ exports[`XYChart component it renders stacked bar 1`] = ` yAxesConfiguration={ Array [ Object { + "extent": Object { + "mode": "full", + "type": "axisExtentConfig", + }, "formatter": Object { "convert": [MockFunction] { "calls": Array [ @@ -2619,17 +2691,25 @@ exports[`XYChart component it renders stacked bar 1`] = ` }, }, "groupId": "left", + "labelsOrientation": -90, "position": "left", "series": Array [ Object { "accessor": "a", + "axisId": undefined, "layer": "first", }, Object { "accessor": "b", + "axisId": undefined, "layer": "first", }, ], + "showGridLines": false, + "showLabels": false, + "showTitle": true, + "title": "", + "type": "yAxisConfig", }, ] } @@ -2685,23 +2765,23 @@ exports[`XYChart component it renders stacked horizontal bar 1`] = ` gridLine={ Object { "strokeWidth": 1, - "visible": undefined, + "visible": true, } } hide={false} id="x" - position="left" + position="right" style={ Object { "axisTitle": Object { "padding": undefined, - "visible": undefined, + "visible": true, }, "tickLabel": Object { "fill": undefined, "padding": undefined, - "rotation": undefined, - "visible": undefined, + "rotation": 0, + "visible": true, }, } } @@ -2902,6 +2982,9 @@ exports[`XYChart component it renders stacked horizontal bar 1`] = ` ], "columnToLabel": "{\\"a\\": \\"Label A\\", \\"b\\": \\"Label B\\", \\"d\\": \\"Label D\\"}", "isHistogram": false, + "isHorizontal": true, + "isPercentage": false, + "isStacked": true, "layerId": "first", "layerType": "data", "palette": Object { @@ -3027,10 +3110,12 @@ exports[`XYChart component it renders stacked horizontal bar 1`] = ` "series": Array [ Object { "accessor": "a", + "axisId": undefined, "layer": "first", }, Object { "accessor": "b", + "axisId": undefined, "layer": "first", }, ], 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 56c14b7314438..172b49e7c9323 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 @@ -585,7 +585,7 @@ describe('XYChart component', () => { { args={{ ...args, layers: [ - { ...(args.layers[0] as DataLayerConfigResult), isHorizontal: true, seriesType: 'bar' }, + { ...(args.layers[0] as DataLayerConfig), isHorizontal: true, seriesType: 'bar' }, ], }} /> @@ -1263,7 +1263,7 @@ describe('XYChart component', () => { args={{ ...args, layers: [ - { ...(args.layers[0] as DataLayerConfigResult), seriesType: 'bar', isStacked: true }, + { ...(args.layers[0] as DataLayerConfig), seriesType: 'bar', isStacked: true }, ], }} /> @@ -1284,7 +1284,7 @@ describe('XYChart component', () => { args={{ ...args, layers: [ - { ...(args.layers[0] as DataLayerConfigResult), seriesType: 'area', isStacked: true }, + { ...(args.layers[0] as DataLayerConfig), seriesType: 'area', isStacked: true }, ], }} /> @@ -1306,7 +1306,7 @@ describe('XYChart component', () => { ...args, layers: [ { - ...(args.layers[0] as DataLayerConfigResult), + ...(args.layers[0] as DataLayerConfig), seriesType: 'bar', isStacked: true, isHorizontal: true, @@ -1424,7 +1424,7 @@ describe('XYChart component', () => { ...args, layers: [ { - ...(args.layers[0] as DataLayerConfigResult), + ...(args.layers[0] as DataLayerConfig), seriesType: 'bar', isStacked: true, isHistogram: true, 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 c763efcc97d5b..ea9621f381dae 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 @@ -106,7 +106,7 @@ function getValueLabelsStyling(isHorizontal: boolean): { }; } -function getIconForSeriesType(layer: CommonXYDataLayerConfigResult): IconType { +function getIconForSeriesType(layer: CommonXYDataLayerConfig): IconType { return ( visualizationDefinitions.find( (c) => @@ -324,7 +324,7 @@ export function XYChart({ .flatMap((l) => l.yConfig ? l.yConfig.map((yConfig) => ({ layerId: l.layerId, yConfig })) : [] ) - .filter(({ yConfig }) => yConfig.axisMode === axis.groupId) + .filter(({ yConfig }) => axis.series.some((s) => s.accessor === yConfig.forAccessor)) .map( ({ layerId, yConfig }) => `${layerId}-${yConfig.forAccessor}-${yConfig.fill !== 'none' ? 'rect' : 'line'}` From d468323352a204b99ab3ca11295102aaaa830cc9 Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Thu, 21 Apr 2022 15:35:16 +0300 Subject: [PATCH 150/213] Fix checks --- .../common/expression_functions/common_xy_args.ts | 2 +- .../expression_xy/public/components/xy_chart.test.tsx | 8 ++------ 2 files changed, 3 insertions(+), 7 deletions(-) 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 8fc5618346cab..16ffa2dea8e27 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 @@ -13,7 +13,7 @@ import { ValueLabelModes, XYCurveTypes, X_AXIS_CONFIG, - Y_AXIS_CONFIG + Y_AXIS_CONFIG, } from '../constants'; import { strings } from '../i18n'; import { LayeredXyVisFn, XyVisFn } from '../types'; 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 172b49e7c9323..0fa4415f92aec 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 @@ -1262,9 +1262,7 @@ describe('XYChart component', () => { {...defaultProps} args={{ ...args, - layers: [ - { ...(args.layers[0] as DataLayerConfig), seriesType: 'bar', isStacked: true }, - ], + layers: [{ ...(args.layers[0] as DataLayerConfig), seriesType: 'bar', isStacked: true }], }} /> ); @@ -1283,9 +1281,7 @@ describe('XYChart component', () => { {...defaultProps} args={{ ...args, - layers: [ - { ...(args.layers[0] as DataLayerConfig), seriesType: 'area', isStacked: true }, - ], + layers: [{ ...(args.layers[0] as DataLayerConfig), seriesType: 'area', isStacked: true }], }} /> ); From 3898554b5196e690584c11e10778bc17c4214022 Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Thu, 21 Apr 2022 15:49:29 +0300 Subject: [PATCH 151/213] Fix snapshots --- .../xy_visualization/__snapshots__/to_expression.test.ts.snap | 3 +++ 1 file changed, 3 insertions(+) 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 198883908933a..b5dabcf5f60de 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 @@ -125,6 +125,9 @@ Object { "isHorizontal": Array [], "isPercentage": Array [], "isStacked": Array [], + "layerId": Array [ + "first", + ], "palette": Array [ Object { "chain": Array [ From 1b3219368d5b6f865c2e2024aaddcb656baf119e Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Thu, 21 Apr 2022 16:17:16 +0300 Subject: [PATCH 152/213] Fixed problems with resetting of the inspector. --- .../expression_xy/common/utils/log_datatables.ts | 3 +++ 1 file changed, 3 insertions(+) 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 16106b6763628..79a3cbd2eef19 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 @@ -21,6 +21,9 @@ export const logDatatables = (layers: CommonXYLayerConfig[], handlers: Execution return; } + handlers.inspectorAdapters.tables.reset(); + handlers.inspectorAdapters.tables.allowCsvExport = true; + layers.forEach((layer) => { if (layer.layerType === LayerTypes.ANNOTATIONS) { return; From d978efa340fdd1c70ca2700246f396b62700a65e Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Thu, 21 Apr 2022 16:31:21 +0300 Subject: [PATCH 153/213] Fixed migrations. --- .../server/embeddable/make_lens_embeddable_factory.ts | 8 +++----- .../plugins/lens/server/migrations/common_migrations.ts | 4 ++++ .../lens/server/migrations/saved_object_migrations.ts | 4 ---- 3 files changed, 7 insertions(+), 9 deletions(-) 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 f6d6a6e49e450..215f080d3dbdf 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 @@ -115,11 +115,9 @@ export const makeLensEmbeddableFactory = '8.3.0': (state) => { const lensState = state as unknown as { attributes: LensDocShape810 }; let migratedLensState = commonLockOldMetricVisSettings(lensState.attributes); - if (migratedLensState.visualizationType !== 'lnsXY') { - migratedLensState = commonFixValueLabelsInXY( - migratedLensState as LensDocShape810 - ); - } + 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 d276b8d832914..7cafa41f569d4 100644 --- a/x-pack/plugins/lens/server/migrations/common_migrations.ts +++ b/x-pack/plugins/lens/server/migrations/common_migrations.ts @@ -348,6 +348,10 @@ export const fixLensTopValuesCustomFormatting = (attributes: LensDocShape810): L export const commonFixValueLabelsInXY = ( attributes: LensDocShape830 ): LensDocShape830 => { + if (attributes.visualizationType !== 'lnsXY') { + return attributes as LensDocShape830; + } + const newAttributes: LensDocShape830 = cloneDeep(attributes); const { visualization } = newAttributes.state; const { valueLabels } = visualization; 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 a9bac4f6edddf..6f9cd588d4ce3 100644 --- a/x-pack/plugins/lens/server/migrations/saved_object_migrations.ts +++ b/x-pack/plugins/lens/server/migrations/saved_object_migrations.ts @@ -499,10 +499,6 @@ const fixValueLabelsInXY: SavedObjectMigrationFn< LensDocShape830, LensDocShape830 > = (doc) => { - if (doc.attributes.visualizationType !== 'lnsXY') { - return doc; - } - const newDoc = cloneDeep(doc); return { ...newDoc, attributes: commonFixValueLabelsInXY(newDoc.attributes) }; }; From adb6d93ae5901ea4a0ace2d8d1b6cf4c0d3af706 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Thu, 21 Apr 2022 16:44:06 +0300 Subject: [PATCH 154/213] Removed types. --- x-pack/plugins/lens/public/xy_visualization/types.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/plugins/lens/public/xy_visualization/types.ts b/x-pack/plugins/lens/public/xy_visualization/types.ts index cd770cedce3f9..b96ddf1aaee2d 100644 --- a/x-pack/plugins/lens/public/xy_visualization/types.ts +++ b/x-pack/plugins/lens/public/xy_visualization/types.ts @@ -56,7 +56,6 @@ export interface XYReferenceLineLayerConfig { layerId: string; accessors: string[]; yConfig?: ExtendedYConfig[]; - palette?: PaletteOutput; layerType: 'referenceLine'; } From 8cc4adc6723456277382902f41e06dac1be59052 Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Thu, 21 Apr 2022 16:52:12 +0300 Subject: [PATCH 155/213] Fix i18n --- .../expression_xy/common/i18n/index.tsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) 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 32d5160f9ff8f..189713d9ce987 100644 --- a/src/plugins/chart_expressions/expression_xy/common/i18n/index.tsx +++ b/src/plugins/chart_expressions/expression_xy/common/i18n/index.tsx @@ -78,11 +78,11 @@ export const strings = { defaultMessage: 'Specifies the aria label of the xy chart', }), getXAxisConfigHelp: () => - i18n.translate('expressionXY.xyVis.ariaLabel.help', { + i18n.translate('expressionXY.xyVis.xAxisConfig.help', { defaultMessage: 'Specifies x-axis config', }), getAxesHelp: () => - i18n.translate('expressionXY.xyVis.ariaLabel.help', { + i18n.translate('expressionXY.xyVis.axes.help', { defaultMessage: 'Specifies y-axes configs', }), getDataLayerFnHelp: () => @@ -110,15 +110,15 @@ export const strings = { defaultMessage: 'Whether to layout the chart as a histogram', }), getIsStackedHelp: () => - i18n.translate('expressionXY.dataLayer.isHistogram.help', { + i18n.translate('expressionXY.dataLayer.isStacked.help', { defaultMessage: 'Layout of the chart in stacked mode', }), getIsPercentageHelp: () => - i18n.translate('expressionXY.dataLayer.isHistogram.help', { + i18n.translate('expressionXY.dataLayer.isPercentage.help', { defaultMessage: 'Whether to layout the chart has percentage mode', }), getIsHorizontalHelp: () => - i18n.translate('expressionXY.dataLayer.isHistogram.help', { + i18n.translate('expressionXY.dataLayer.isHorizontal.help', { defaultMessage: 'Layout of the chart is horizontal', }), getYScaleTypeHelp: () => @@ -198,11 +198,11 @@ export const strings = { defaultMessage: 'Annotations', }), getXAxisConfigFnHelp: () => - i18n.translate('expressionXY.axisConfig.help', { + i18n.translate('expressionXY.xAxisConfigFn.help', { defaultMessage: `Configure the xy chart's x-axis config`, }), getYAxisConfigFnHelp: () => - i18n.translate('expressionXY.axisConfig.help', { + i18n.translate('expressionXY.yAxisConfigFn.help', { defaultMessage: `Configure the xy chart's y-axis config`, }), getAxisModeHelp: () => @@ -234,7 +234,7 @@ export const strings = { defaultMessage: 'Hide the axis', }), getAxisLabelColorHelp: () => - i18n.translate('expressionXY.axisConfig.title.help', { + i18n.translate('expressionXY.axisConfig.labelColor.help', { defaultMessage: 'Color of the axis labels', }), getAxisShowOverlappingLabelsHelp: () => From 351a4d157076164a6d312c2ab287ef999ac4dff7 Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Fri, 22 Apr 2022 10:54:17 +0300 Subject: [PATCH 156/213] Remove unused translations --- .../translations/translations/fr-FR.json | 26 ------------------- .../translations/translations/ja-JP.json | 26 ------------------- .../translations/translations/zh-CN.json | 26 ------------------- 3 files changed, 78 deletions(-) diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 2aa3620ac29a0..f3c8c7cb7d8d9 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -3375,10 +3375,6 @@ "expressionXY.axisExtentConfig.help": "Configurer les étendues d’axe du graphique xy", "expressionXY.axisExtentConfig.lowerBound.help": "Limite inférieure", "expressionXY.axisExtentConfig.upperBound.help": "Limite supérieure", - "expressionXY.axisTitlesVisibilityConfig.help": "Configurer l’aspect des titres d’axe du graphique xy", - "expressionXY.axisTitlesVisibilityConfig.x.help": "Spécifie si le titre de l'axe X est visible ou non.", - "expressionXY.axisTitlesVisibilityConfig.yLeft.help": "Spécifie si le titre de l'axe Y de gauche est visible ou non.", - "expressionXY.axisTitlesVisibilityConfig.yRight.help": "Spécifie si le titre de l'axe Y de droite est visible ou non.", "expressionXY.dataLayer.accessors.help": "Les colonnes à afficher sur l’axe y.", "expressionXY.layer.columnToLabel.help": "Paires clé-valeur JSON de l’ID de colonne pour l’étiquette", "expressionXY.dataLayer.help": "Configurer un calque dans le graphique xy", @@ -3392,14 +3388,6 @@ "expressionXY.dataLayer.xScaleType.help": "Type d’échelle de l’axe x", "expressionXY.dataLayer.yConfig.help": "Configuration supplémentaire pour les axes y", "expressionXY.dataLayer.yScaleType.help": "Type d’échelle des axes y", - "expressionXY.gridlinesConfig.help": "Configurer l’aspect du quadrillage du graphique xy", - "expressionXY.gridlinesConfig.x.help": "Spécifie si le quadrillage de l'axe X est visible ou non.", - "expressionXY.gridlinesConfig.yLeft.help": "Spécifie si le quadrillage de l'axe Y de gauche est visible ou non.", - "expressionXY.gridlinesConfig.yRight.help": "Spécifie si le quadrillage de l'axe Y de droite est visible ou non.", - "expressionXY.labelsOrientationConfig.help": "Configurer l’orientation des étiquettes de coche du graphique xy", - "expressionXY.labelsOrientationConfig.x.help": "Spécifie l'orientation des étiquettes de l'axe X.", - "expressionXY.labelsOrientationConfig.yLeft.help": "Spécifie l'orientation des étiquettes de l'axe Y de gauche.", - "expressionXY.labelsOrientationConfig.yRight.help": "Spécifie l'orientation des étiquettes de l'axe Y de droite.", "expressionXY.legend.filterForValueButtonAriaLabel": "Filtrer sur la valeur", "expressionXY.legend.filterOptionsLegend": "{legendDataLabel}, options de filtre", "expressionXY.legend.filterOutValueButtonAriaLabel": "Exclure la valeur", @@ -3417,10 +3405,6 @@ "expressionXY.referenceLineLayer.accessors.help": "Les colonnes à afficher sur l’axe y.", "expressionXY.referenceLineLayer.help": "Configurer une ligne de référence dans le graphique xy", "expressionXY.referenceLineLayer.yConfig.help": "Configuration supplémentaire pour les axes y", - "expressionXY.tickLabelsConfig.help": "Configurer l’aspect des étiquettes de coche du graphique xy", - "expressionXY.tickLabelsConfig.x.help": "Spécifie si les étiquettes de graduation de l'axe X sont visibles ou non.", - "expressionXY.tickLabelsConfig.yLeft.help": "Spécifie si les étiquettes de graduation de l'axe Y de gauche sont visibles ou non.", - "expressionXY.tickLabelsConfig.yRight.help": "Spécifie si les étiquettes de graduation de l'axe Y de droite sont visibles ou non.", "expressionXY.xyChart.emptyXLabel": "(vide)", "expressionXY.xyChart.iconSelect.alertIconLabel": "Alerte", "expressionXY.xyChart.iconSelect.asteriskIconLabel": "Astérisque", @@ -3437,30 +3421,20 @@ "expressionXY.xyChart.iconSelect.tagIconLabel": "Balise", "expressionXY.xyChart.iconSelect.triangleIconLabel": "Triangle", "expressionXY.xyVis.ariaLabel.help": "Spécifie l’attribut aria-label du graphique xy", - "expressionXY.xyVis.axisTitlesVisibilitySettings.help": "Afficher les titres des axes X et Y", "expressionXY.xyVis.curveType.help": "Définir de quelle façon le type de courbe est rendu pour un graphique linéaire", "expressionXY.xyVis.endValue.help": "Valeur de fin", "expressionXY.xyVis.fillOpacity.help": "Définir l'opacité du remplissage du graphique en aires", "expressionXY.xyVis.fittingFunction.help": "Définir le mode de traitement des valeurs manquantes", - "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.xyVis.labelsOrientation.help": "Définit la rotation des étiquettes des axes", "expressionXY.layeredXyVis.layers.help": "Calques de série visuelle", "expressionXY.xyVis.legend.help": "Configurez la légende du graphique.", "expressionXY.xyVis.logDatatable.breakDown": "Répartir par", "expressionXY.xyVis.logDatatable.metric": "Axe vertical", "expressionXY.xyVis.logDatatable.x": "Axe horizontal", "expressionXY.xyVis.renderer.help": "Outil de rendu de graphique X/Y", - "expressionXY.xyVis.tickLabelsVisibilitySettings.help": "Afficher les étiquettes de graduation des axes X et Y", "expressionXY.xyVis.valueLabels.help": "Mode des étiquettes de valeur", "expressionXY.xyVis.valuesInLegend.help": "Afficher les valeurs dans la légende", - "expressionXY.xyVis.xTitle.help": "Titre de l'axe X", - "expressionXY.xyVis.yLeftExtent.help": "Portée de l'axe Y de gauche", - "expressionXY.xyVis.yLeftTitle.help": "Titre de l'axe Y de gauche", - "expressionXY.xyVis.yRightExtent.help": "Portée de l'axe Y de droite", - "expressionXY.xyVis.yRightTitle.help": "Titre de l'axe Y de droite", - "expressionXY.yConfig.axisMode.help": "Le mode axe de l’indicateur", "expressionXY.yConfig.color.help": "La couleur des séries", "expressionXY.yConfig.fill.help": "Remplir", "expressionXY.yConfig.forAccessor.help": "L’accesseur auquel cette configuration s’applique", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 70905765ec4c8..b2f6aacff62f4 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -3473,10 +3473,6 @@ "expressionXY.axisExtentConfig.help": "xyグラフの軸範囲を構成", "expressionXY.axisExtentConfig.lowerBound.help": "下界", "expressionXY.axisExtentConfig.upperBound.help": "上界", - "expressionXY.axisTitlesVisibilityConfig.help": "xyグラフの軸タイトル表示を構成", - "expressionXY.axisTitlesVisibilityConfig.x.help": "x軸のタイトルを表示するかどうかを指定します。", - "expressionXY.axisTitlesVisibilityConfig.yLeft.help": "左y軸のタイトルを表示するかどうかを指定します。", - "expressionXY.axisTitlesVisibilityConfig.yRight.help": "右y軸のタイトルを表示するかどうかを指定します。", "expressionXY.dataLayer.accessors.help": "y軸に表示する列。", "expressionXY.layer.columnToLabel.help": "ラベリングする列IDのJSONキー値のペア", "expressionXY.dataLayer.help": "xyグラフでレイヤーを構成", @@ -3490,14 +3486,6 @@ "expressionXY.dataLayer.xScaleType.help": "x軸の目盛タイプ", "expressionXY.dataLayer.yConfig.help": "y軸の詳細構成", "expressionXY.dataLayer.yScaleType.help": "y軸の目盛タイプ", - "expressionXY.gridlinesConfig.help": "xyグラフのグリッド線表示を構成", - "expressionXY.gridlinesConfig.x.help": "x 軸のグリッド線を表示するかどうかを指定します。", - "expressionXY.gridlinesConfig.yLeft.help": "左y軸のグリッド線を表示するかどうかを指定します。", - "expressionXY.gridlinesConfig.yRight.help": "右y軸のグリッド線を表示するかどうかを指定します。", - "expressionXY.labelsOrientationConfig.help": "xyグラフのティックラベルの向きを構成", - "expressionXY.labelsOrientationConfig.x.help": "x軸のラベルの向きを指定します。", - "expressionXY.labelsOrientationConfig.yLeft.help": "左y軸のラベルの向きを指定します。", - "expressionXY.labelsOrientationConfig.yRight.help": "右y軸のラベルの向きを指定します。", "expressionXY.legend.filterForValueButtonAriaLabel": "値でフィルター", "expressionXY.legend.filterOptionsLegend": "{legendDataLabel}、フィルターオプション", "expressionXY.legend.filterOutValueButtonAriaLabel": "値を除外", @@ -3515,10 +3503,6 @@ "expressionXY.referenceLineLayer.accessors.help": "y軸に表示する列。", "expressionXY.referenceLineLayer.help": "xyグラフで基準線を構成", "expressionXY.referenceLineLayer.yConfig.help": "y軸の詳細構成", - "expressionXY.tickLabelsConfig.help": "xyグラフのティックラベルの表示を構成", - "expressionXY.tickLabelsConfig.x.help": "x軸の目盛ラベルを表示するかどうかを指定します。", - "expressionXY.tickLabelsConfig.yLeft.help": "左y軸の目盛ラベルを表示するかどうかを指定します。", - "expressionXY.tickLabelsConfig.yRight.help": "右y軸の目盛ラベルを表示するかどうかを指定します。", "expressionXY.xyChart.emptyXLabel": "(空)", "expressionXY.xyChart.iconSelect.alertIconLabel": "アラート", "expressionXY.xyChart.iconSelect.asteriskIconLabel": "アスタリスク", @@ -3535,30 +3519,20 @@ "expressionXY.xyChart.iconSelect.tagIconLabel": "タグ", "expressionXY.xyChart.iconSelect.triangleIconLabel": "三角形", "expressionXY.xyVis.ariaLabel.help": "xyグラフのariaラベルを指定します", - "expressionXY.xyVis.axisTitlesVisibilitySettings.help": "xおよびy軸のタイトルを表示", "expressionXY.xyVis.curveType.help": "折れ線グラフで曲線タイプをレンダリングする方法を定義します", "expressionXY.xyVis.endValue.help": "終了値", "expressionXY.xyVis.fillOpacity.help": "エリアグラフの塗りつぶしの透明度を定義", "expressionXY.xyVis.fittingFunction.help": "欠測値の処理方法を定義", - "expressionXY.xyVis.gridlinesVisibilitySettings.help": "xおよびy軸のグリッド線を表示", "expressionXY.xyVis.help": "X/Y チャート", "expressionXY.xyVis.hideEndzones.help": "部分データの終了ゾーンマーカーを非表示", - "expressionXY.xyVis.labelsOrientation.help": "軸ラベルの回転を定義します", "expressionXY.layeredXyVis.layers.help": "視覚的な系列のレイヤー", "expressionXY.xyVis.legend.help": "チャートの凡例を構成します。", "expressionXY.xyVis.logDatatable.breakDown": "内訳の基準", "expressionXY.xyVis.logDatatable.metric": "縦軸", "expressionXY.xyVis.logDatatable.x": "横軸", "expressionXY.xyVis.renderer.help": "X/Y チャートを再レンダリング", - "expressionXY.xyVis.tickLabelsVisibilitySettings.help": "xおよびy軸の目盛ラベルを表示", "expressionXY.xyVis.valueLabels.help": "値ラベルモード", "expressionXY.xyVis.valuesInLegend.help": "凡例に値を表示", - "expressionXY.xyVis.xTitle.help": "x軸のタイトル", - "expressionXY.xyVis.yLeftExtent.help": "Y左軸範囲", - "expressionXY.xyVis.yLeftTitle.help": "左y軸のタイトル", - "expressionXY.xyVis.yRightExtent.help": "Y右軸範囲", - "expressionXY.xyVis.yRightTitle.help": "右 y 軸のタイトル", - "expressionXY.yConfig.axisMode.help": "メトリックの軸モード", "expressionXY.yConfig.color.help": "系列の色", "expressionXY.yConfig.fill.help": "塗りつぶし", "expressionXY.yConfig.forAccessor.help": "この構成のアクセサー", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 3b8ba726b1f7f..a6d4ccb5e5ec3 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -3484,10 +3484,6 @@ "expressionXY.axisExtentConfig.help": "配置 xy 图表的轴范围", "expressionXY.axisExtentConfig.lowerBound.help": "下边界", "expressionXY.axisExtentConfig.upperBound.help": "上边界", - "expressionXY.axisTitlesVisibilityConfig.help": "配置 xy 图表的轴标题外观", - "expressionXY.axisTitlesVisibilityConfig.x.help": "指定 x 轴的标题是否可见。", - "expressionXY.axisTitlesVisibilityConfig.yLeft.help": "指定左侧 y 轴的标题是否可见。", - "expressionXY.axisTitlesVisibilityConfig.yRight.help": "指定右侧 y 轴的标题是否可见。", "expressionXY.dataLayer.accessors.help": "要在 y 轴上显示的列。", "expressionXY.layer.columnToLabel.help": "要标记的列 ID 的 JSON 键值对", "expressionXY.dataLayer.help": "配置 xy 图表中的图层", @@ -3501,14 +3497,6 @@ "expressionXY.dataLayer.xScaleType.help": "x 轴的缩放类型", "expressionXY.dataLayer.yConfig.help": "y 轴的其他配置", "expressionXY.dataLayer.yScaleType.help": "y 轴的缩放类型", - "expressionXY.gridlinesConfig.help": "配置 xy 图表的网格线外观", - "expressionXY.gridlinesConfig.x.help": "指定 x 轴的网格线是否可见。", - "expressionXY.gridlinesConfig.yLeft.help": "指定左侧 y 轴的网格线是否可见。", - "expressionXY.gridlinesConfig.yRight.help": "指定右侧 y 轴的网格线是否可见。", - "expressionXY.labelsOrientationConfig.help": "配置 xy 图表的刻度标签方向", - "expressionXY.labelsOrientationConfig.x.help": "指定 x 轴的标签方向。", - "expressionXY.labelsOrientationConfig.yLeft.help": "指定左 y 轴的标签方向。", - "expressionXY.labelsOrientationConfig.yRight.help": "指定右 y 轴的标签方向。", "expressionXY.legend.filterForValueButtonAriaLabel": "筛留值", "expressionXY.legend.filterOptionsLegend": "{legendDataLabel}, 筛选选项", "expressionXY.legend.filterOutValueButtonAriaLabel": "筛除值", @@ -3526,10 +3514,6 @@ "expressionXY.referenceLineLayer.accessors.help": "要在 y 轴上显示的列。", "expressionXY.referenceLineLayer.help": "配置 xy 图表中的参考线", "expressionXY.referenceLineLayer.yConfig.help": "y 轴的其他配置", - "expressionXY.tickLabelsConfig.help": "配置 xy 图表的刻度标签外观", - "expressionXY.tickLabelsConfig.x.help": "指定 x 轴的刻度标签是否可见。", - "expressionXY.tickLabelsConfig.yLeft.help": "指定左侧 y 轴的刻度标签是否可见。", - "expressionXY.tickLabelsConfig.yRight.help": "指定右侧 y 轴的刻度标签是否可见。", "expressionXY.xyChart.emptyXLabel": "(空)", "expressionXY.xyChart.iconSelect.alertIconLabel": "告警", "expressionXY.xyChart.iconSelect.asteriskIconLabel": "星号", @@ -3546,30 +3530,20 @@ "expressionXY.xyChart.iconSelect.tagIconLabel": "标签", "expressionXY.xyChart.iconSelect.triangleIconLabel": "三角形", "expressionXY.xyVis.ariaLabel.help": "指定 xy 图表的 aria 标签", - "expressionXY.xyVis.axisTitlesVisibilitySettings.help": "显示 x 和 y 轴标题", "expressionXY.xyVis.curveType.help": "定义为折线图渲染曲线类型的方式", "expressionXY.xyVis.endValue.help": "结束值", "expressionXY.xyVis.fillOpacity.help": "定义面积图填充透明度", "expressionXY.xyVis.fittingFunction.help": "定义处理缺失值的方式", - "expressionXY.xyVis.gridlinesVisibilitySettings.help": "显示 x 和 y 轴网格线", "expressionXY.xyVis.help": "X/Y 图表", "expressionXY.xyVis.hideEndzones.help": "隐藏部分数据的末日区域标记", - "expressionXY.xyVis.labelsOrientation.help": "定义轴标签的旋转", "expressionXY.layeredXyVis.layers.help": "可视序列的图层", "expressionXY.xyVis.legend.help": "配置图表图例。", "expressionXY.xyVis.logDatatable.breakDown": "细分方式", "expressionXY.xyVis.logDatatable.metric": "垂直轴", "expressionXY.xyVis.logDatatable.x": "水平轴", "expressionXY.xyVis.renderer.help": "X/Y 图表呈现器", - "expressionXY.xyVis.tickLabelsVisibilitySettings.help": "显示 x 和 y 轴刻度标签", "expressionXY.xyVis.valueLabels.help": "值标签模式", "expressionXY.xyVis.valuesInLegend.help": "在图例中显示值", - "expressionXY.xyVis.xTitle.help": "X 轴标题", - "expressionXY.xyVis.yLeftExtent.help": "左侧 Y 轴范围", - "expressionXY.xyVis.yLeftTitle.help": "左侧 Y 轴标题", - "expressionXY.xyVis.yRightExtent.help": "右侧 Y 轴范围", - "expressionXY.xyVis.yRightTitle.help": "右侧 Y 轴标题", - "expressionXY.yConfig.axisMode.help": "指标的轴模式", "expressionXY.yConfig.color.help": "序列的颜色", "expressionXY.yConfig.fill.help": "填充", "expressionXY.yConfig.forAccessor.help": "此配置针对的访问器", From 19fb3c29380668071aeb43ec40b9d81cb87391ed Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Fri, 22 Apr 2022 11:55:07 +0300 Subject: [PATCH 157/213] Remove unused xAxisId --- .../common/expression_functions/common_data_layer_args.ts | 4 ---- .../chart_expressions/expression_xy/common/i18n/index.tsx | 4 ---- .../expression_xy/common/types/expression_functions.ts | 2 -- 3 files changed, 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 bc40f2e7f6c33..f6f23901bb7cb 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 @@ -84,8 +84,4 @@ export const commonDataLayerArgs: Omit = { help: strings.getPaletteHelp(), default: '{palette}', }, - xAxisId: { - types: ['string'], - help: strings.getXAxisIdHelp(), - }, }; 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 189713d9ce987..3ea159bac38f6 100644 --- a/src/plugins/chart_expressions/expression_xy/common/i18n/index.tsx +++ b/src/plugins/chart_expressions/expression_xy/common/i18n/index.tsx @@ -145,10 +145,6 @@ export const strings = { i18n.translate('expressionXY.dataLayer.palette.help', { defaultMessage: 'Palette', }), - getXAxisIdHelp: () => - i18n.translate('expressionXY.dataLayer.xAxisId.help', { - defaultMessage: 'Id of x-axis', - }), getTableHelp: () => i18n.translate('expressionXY.layers.table.help', { defaultMessage: 'Table', 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 f34f678264810..288d1017cbff2 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 @@ -123,7 +123,6 @@ export interface DataLayerArgs { // palette will always be set on the expression palette: PaletteOutput; yConfig?: YConfigResult[]; - xAxisId?: string; } export interface ValidLayer extends DataLayerConfigResult { @@ -149,7 +148,6 @@ export interface ExtendedDataLayerArgs { // palette will always be set on the expression yConfig?: YConfigResult[]; table?: Datatable; - xAxisId?: string; } export interface LegendConfig { From 73d0442f1c7463a867e33ec571fa9a94b01ed488 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Tue, 26 Apr 2022 12:58:09 +0300 Subject: [PATCH 158/213] Removed tones of `areFormatted` calculations. --- .../__snapshots__/xy_chart.test.tsx.snap | 638 +++++++++++++++--- .../public/components/data_layers.tsx | 20 +- .../public/components/legend_action.tsx | 7 +- .../public/components/xy_chart.tsx | 18 +- .../public/helpers/data_layers.tsx | 108 +-- 5 files changed, 643 insertions(+), 148 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 b0b0e4d6ab4ad..04166930ab979 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 @@ -324,16 +324,6 @@ exports[`XYChart component it renders area 1`] = ` histogramMode={false} /> >; + formattedDatatables: DatatablesWithFormatInfo; syncColors?: boolean; timeZone?: string; emphasizeFitting?: boolean; @@ -65,7 +64,7 @@ export const DataLayers: FC = ({ emphasizeFitting, yAxesConfiguration, shouldShowValueLabels, - areLayersAlreadyFormatted, + formattedDatatables, chartHasMoreThanOneBarSeries, }) => { const colorAssignments = getColorAssignments(layers, formatFactory); @@ -73,7 +72,7 @@ export const DataLayers: FC = ({ <> {layers.flatMap((layer) => layer.accessors.map((accessor, accessorIndex) => { - const { splitAccessor, seriesType, xAccessor, table, columnToLabel, xScaleType } = layer; + const { splitAccessor, seriesType, xAccessor, columnToLabel, layerId } = layer; const columnToLabelMap: Record = columnToLabel ? JSON.parse(columnToLabel) : {}; @@ -81,18 +80,13 @@ export const DataLayers: FC = ({ // 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 formattedDatatableInfo = formattedDatatables[layerId]; 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( + const rows = formattedDatatableInfo.table.rows.filter( (row) => !(xAccessor && typeof row[xAccessor] === 'undefined') && !( @@ -122,7 +116,7 @@ export const DataLayers: FC = ({ formatFactory, columnToLabelMap, paletteService, - alreadyFormattedColumns: areLayersAlreadyFormatted[layer.layerId] ?? {}, + formattedDatatableInfo, syncColors, yAxis, timeZone, 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 e2a4ca8da554b..da1939f223649 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 @@ -12,12 +12,13 @@ import type { FilterEvent } from '../types'; import type { CommonXYDataLayerConfig } from '../../common'; import type { FormatFactory } from '../types'; import { LegendActionPopover } from './legend_action_popover'; +import { DatatablesWithFormatInfo } from '../helpers'; export const getLegendAction = ( dataLayers: CommonXYDataLayerConfig[], onFilter: (data: FilterEvent['data']) => void, formatFactory: FormatFactory, - layersAlreadyFormatted: Record> + formattedDatatables: DatatablesWithFormatInfo ): LegendAction => React.memo(({ series: [xySeries] }) => { const series = xySeries as XYChartSeriesIdentifier; @@ -42,7 +43,7 @@ export const getLegendAction = ( const formatter = formatFactory(splitColumn && splitColumn.meta?.params); const rowIndex = table.rows.findIndex((row) => { - if (layersAlreadyFormatted[layer.layerId]?.[accessor]) { + if (formattedDatatables[layer.layerId]?.formattedColumns[accessor]) { // stringify the value to compare with the chart value return formatter.convert(row[accessor]) === splitLabel; } @@ -67,7 +68,7 @@ export const getLegendAction = ( return ( id === dataLayers[0].xAccessor); const xAxisFormatter = formatFactory(xAxisColumn && xAxisColumn.meta?.params); - const areLayersAlreadyFormatted = getAreAlreadyFormattedLayersInfo(dataLayers, formatFactory); + const formattedDatatables = getFormattedTablesByLayers(dataLayers, formatFactory); // This is a safe formatter for the xAccessor that abstracts the knowledge of already formatted layers const safeXAccessorLabelRenderer = (value: unknown): string => - xAxisColumn && areLayersAlreadyFormatted[dataLayers[0]?.layerId]?.[xAxisColumn.id] + xAxisColumn && formattedDatatables[dataLayers[0]?.layerId]?.formattedColumns[xAxisColumn.id] ? String(value) : String(xAxisFormatter.convert(value)); @@ -354,13 +354,15 @@ export function XYChart({ const xColumn = table.columns.find((col) => col.id === layer.xAccessor); const currentXFormatter = - layer.xAccessor && areLayersAlreadyFormatted[layer.layerId]?.[layer.xAccessor] && xColumn + layer.xAccessor && + formattedDatatables[layer.layerId]?.formattedColumns[layer.xAccessor] && + xColumn ? formatFactory(xColumn.meta.params) : xAxisFormatter; const rowIndex = table.rows.findIndex((row) => { if (layer.xAccessor) { - if (areLayersAlreadyFormatted[layer.layerId]?.[layer.xAccessor]) { + if (formattedDatatables[layer.layerId]?.formattedColumns[layer.xAccessor]) { // stringify the value to compare with the chart value return currentXFormatter.convert(row[layer.xAccessor]) === xyGeometry.x; } @@ -385,7 +387,7 @@ export function XYChart({ points.push({ row: table.rows.findIndex((row) => { if (layer.splitAccessor) { - if (areLayersAlreadyFormatted[layer.layerId]?.[layer.splitAccessor]) { + if (formattedDatatables[layer.layerId]?.formattedColumns[layer.splitAccessor]) { return splitFormatter.convert(row[layer.splitAccessor]) === pointValue; } return row[layer.splitAccessor] === pointValue; @@ -518,7 +520,7 @@ export function XYChart({ onElementClick={interactive ? clickHandler : undefined} legendAction={ interactive - ? getLegendAction(dataLayers, onClickValue, formatFactory, areLayersAlreadyFormatted) + ? getLegendAction(dataLayers, onClickValue, formatFactory, formattedDatatables) : undefined } showLegendExtra={isHistogramViz && valuesInLegend} @@ -591,7 +593,7 @@ export function XYChart({ emphasizeFitting={emphasizeFitting} yAxesConfiguration={yAxesConfiguration} shouldShowValueLabels={shouldShowValueLabels} - areLayersAlreadyFormatted={areLayersAlreadyFormatted} + formattedDatatables={formattedDatatables} chartHasMoreThanOneBarSeries={chartHasMoreThanOneBarSeries} /> )} 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 1fbbbfb19f4ce..4cd8dca4241bc 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 @@ -22,7 +22,7 @@ import { FieldFormatParams, SerializedFieldFormat, } from '@kbn/field-formats-plugin/common'; -import { Datatable, DatatableRow } from '@kbn/expressions-plugin'; +import { Datatable } from '@kbn/expressions-plugin'; import { PaletteRegistry, SeriesLayer } from '@kbn/coloring'; import { CommonXYDataLayerConfig, XScaleType } from '../../common'; import { FormatFactory } from '../types'; @@ -40,12 +40,12 @@ type GetSeriesPropsFn = (config: { colorAssignments: ColorAssignments; columnToLabelMap: Record; paletteService: PaletteRegistry; - alreadyFormattedColumns: Record; syncColors?: boolean; yAxis?: GroupsConfiguration[number]; timeZone?: string; emphasizeFitting?: boolean; fillOpacity?: number; + formattedDatatableInfo: DatatableWithFormatInfo; }) => SeriesSpec; type GetSeriesNameFn = ( @@ -71,58 +71,85 @@ type GetColorFn = ( } ) => string | null; +export interface DatatableWithFormatInfo { + table: Datatable; + formattedColumns: Record; +} + +export type DatatablesWithFormatInfo = Record; + +export type FormattedDatatables = Record; + const isPrimitive = (value: unknown): boolean => value != null && typeof value !== 'object'; -export const getFormattedTable = ( - table: Datatable, +export const getFormattedRow = ( + row: Datatable['rows'][number], + columns: Datatable['columns'], 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]; +): { row: Datatable['rows'][number]; formattedColumns: Record } => + columns.reduce( + (formattedInfo, { id, meta }) => { + const record = formattedInfo.row[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')) + (!isPrimitive(record) || (id === xAccessor && xScaleType === 'ordinal')) ) { - newRow[column.id] = formatFactory(column.meta.params)!.convert(record); + return { + row: { ...formattedInfo.row, [id]: formatFactory(meta.params)!.convert(record) }, + formattedColumns: { ...formattedInfo.formattedColumns, [id]: true }, + }; } - } - return newRow; - }), -}); + return formattedInfo; + }, + { row, formattedColumns: {} } + ); -export const getIsAlreadyFormattedLayerInfo = ( - { table, xAccessor, xScaleType }: CommonXYDataLayerConfig, - formatFactory: FormatFactory -): Record => { - const formattedTable = getFormattedTable(table, formatFactory, xAccessor, xScaleType); - return table.columns.reduce>( - (alreadyFormatted: Record, { id }) => { - if (alreadyFormatted[id]) { - return alreadyFormatted; - } +export const getFormattedTable = ( + table: Datatable, + formatFactory: FormatFactory, + xAccessor: string | undefined, + xScaleType: XScaleType +): { table: Datatable; formattedColumns: Record } => { + const formattedTableInfo = table.rows.reduce<{ + rows: Datatable['rows']; + formattedColumns: Record; + }>( + ({ rows: formattedRows, formattedColumns }, row) => { + const formattedRowInfo = getFormattedRow( + row, + table.columns, + formatFactory, + xAccessor, + xScaleType + ); return { - ...alreadyFormatted, - [id]: table.rows.some((row, i) => row[id] !== formattedTable.rows[i][id]), + rows: [...formattedRows, formattedRowInfo.row], + formattedColumns: { ...formattedColumns, ...formattedRowInfo.formattedColumns }, }; }, - {} + { + rows: [], + formattedColumns: {}, + } ); + + return { + table: { ...table, rows: formattedTableInfo.rows }, + formattedColumns: formattedTableInfo.formattedColumns, + }; }; -export const getAreAlreadyFormattedLayersInfo = ( +export const getFormattedTablesByLayers = ( layers: CommonXYDataLayerConfig[], formatFactory: FormatFactory -): Record> => - layers.reduce>>( - (areAlreadyFormatted, layer) => ({ - ...areAlreadyFormatted, - [layer.layerId]: getIsAlreadyFormattedLayerInfo(layer, formatFactory), +): DatatablesWithFormatInfo => + layers.reduce( + (formattedDatatables, { layerId, table, xAccessor, xScaleType }) => ({ + ...formattedDatatables, + [layerId]: getFormattedTable(table, formatFactory, xAccessor, xScaleType), }), {} ); @@ -204,12 +231,12 @@ export const getSeriesProps: GetSeriesPropsFn = ({ formatFactory, columnToLabelMap, paletteService, - alreadyFormattedColumns, syncColors, yAxis, timeZone, emphasizeFitting, fillOpacity, + formattedDatatableInfo, }): SeriesSpec => { const { table } = layer; const isStacked = layer.seriesType.includes('stacked'); @@ -227,12 +254,7 @@ export const getSeriesProps: GetSeriesPropsFn = ({ // 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 - ); + const { table: formattedTable, formattedColumns } = formattedDatatableInfo; // 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. @@ -294,7 +316,7 @@ export const getSeriesProps: GetSeriesPropsFn = ({ layer, splitHint, splitFormatter, - alreadyFormattedColumns, + alreadyFormattedColumns: formattedColumns, columnToLabelMap, }); }, From 945c9e787cb41d0cb2aa2a652588efd549e3afad Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Tue, 26 Apr 2022 14:26:09 +0300 Subject: [PATCH 159/213] Fixed `isTimeViz` and `isHistogramViz` by replacing filteredLayers with dataLayers. --- .../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 49957dcea0d77..1bb0c878b1d87 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,8 +203,8 @@ export function XYChart({ filteredBarLayers.some((layer) => layer.accessors.length > 1) || filteredBarLayers.some((layer) => isDataLayer(layer) && layer.splitAccessor); - const isTimeViz = Boolean(filteredLayers.every((l) => isDataLayer(l) && l.xScaleType === 'time')); - const isHistogramViz = filteredLayers.every((l) => isDataLayer(l) && l.isHistogram); + const isTimeViz = Boolean(dataLayers.every((l) => l.xScaleType === 'time')); + const isHistogramViz = dataLayers.every((l) => l.isHistogram); const { baseDomain: rawXDomain, extendedDomain: xDomain } = getXDomain( dataLayers, From 083fd6b7e057683b06a791113cebf4ef7c320852 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Tue, 26 Apr 2022 14:30:17 +0300 Subject: [PATCH 160/213] Removed referenceLineLayers from the `groupAxesByType` fn. --- .../expression_xy/public/components/xy_chart.tsx | 2 +- .../public/helpers/axes_configuration.ts | 13 +++---------- 2 files changed, 4 insertions(+), 11 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 1bb0c878b1d87..c23393cd5ad96 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 @@ -180,7 +180,7 @@ export function XYChart({ filteredLayers.some((layer) => isDataLayer(layer) && layer.splitAccessor); const shouldRotate = isHorizontalChart(dataLayers); - const yAxesConfiguration = getAxesConfiguration(filteredLayers, shouldRotate, formatFactory); + const yAxesConfiguration = getAxesConfiguration(dataLayers, shouldRotate, formatFactory); const xTitle = args.xTitle || (xAxisColumn && xAxisColumn.name); const axisTitlesVisibilitySettings = args.axisTitlesVisibilitySettings || { 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 ea1b7d09709d6..a3120faf9d120 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,12 +8,7 @@ import type { IFieldFormat, SerializedFieldFormat } from '@kbn/field-formats-plugin/common'; import { FormatFactory } from '../types'; -import { - CommonXYDataLayerConfig, - CommonXYReferenceLineLayerConfig, - ExtendedYConfig, - YConfig, -} from '../../common'; +import { CommonXYDataLayerConfig, ExtendedYConfig, YConfig } from '../../common'; import { isDataLayer } from './visualization'; export interface Series { @@ -39,9 +34,7 @@ export function isFormatterCompatible( return formatter1.id === formatter2.id; } -export function groupAxesByType( - layers: Array -) { +export function groupAxesByType(layers: CommonXYDataLayerConfig[]) { const series: { auto: FormattedMetric[]; left: FormattedMetric[]; @@ -111,7 +104,7 @@ export function groupAxesByType( } export function getAxesConfiguration( - layers: Array, + layers: CommonXYDataLayerConfig[], shouldRotate: boolean, formatFactory?: FormatFactory ): GroupsConfiguration { From 854e5eeef8e59054aab56dd7e9c15371f9a3af6d Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Tue, 26 Apr 2022 15:05:54 +0300 Subject: [PATCH 161/213] Added validation to the layeredXyVis. --- .../expression_functions/layered_xy_vis.ts | 22 +++++ .../common/expression_functions/validate.ts | 90 +++++++++++++++++++ .../common/expression_functions/xy_vis_fn.ts | 76 ++++------------ .../expression_xy/common/helpers/index.ts | 2 +- .../expression_xy/common/helpers/layers.ts | 9 +- 5 files changed, 136 insertions(+), 63 deletions(-) create mode 100644 src/plugins/chart_expressions/expression_xy/common/expression_functions/validate.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 282b53fac03eb..2b47f006d311b 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,6 +7,7 @@ */ import { i18n } from '@kbn/i18n'; +import { getDataLayers } from '../helpers'; import { LayeredXyVisFn } from '../types'; import { XY_VIS_RENDERER, @@ -19,6 +20,14 @@ import { logDatatables } from '../utils'; import { commonXYArgs } from './common_xy_args'; import { strings } from '../i18n'; import { appendLayerIds } from '../helpers'; +import { + hasAreaLayer, + hasBarLayer, + hasHistogramBarLayer, + validateExtent, + validateFillOpacity, + validateValueLabels, +} from './validate'; export const layeredXyVisFunction: LayeredXyVisFn = { name: LAYERED_XY_VIS, @@ -40,6 +49,19 @@ export const layeredXyVisFunction: LayeredXyVisFn = { logDatatables(layers, handlers); + const dataLayers = getDataLayers(layers); + + const hasBar = hasBarLayer(dataLayers); + const hasArea = hasAreaLayer(dataLayers); + + validateExtent(args.yLeftExtent, hasBar || hasArea, dataLayers); + validateExtent(args.yRightExtent, hasBar || hasArea, dataLayers); + validateFillOpacity(args.fillOpacity, hasArea); + + const hasNotHistogramBars = !hasHistogramBarLayer(dataLayers); + + validateValueLabels(args.valueLabels, hasBar, hasNotHistogramBars); + return { type: 'render', as: XY_VIS_RENDERER, diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/validate.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/validate.ts new file mode 100644 index 0000000000000..cbd4dbd367303 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/validate.ts @@ -0,0 +1,90 @@ +/* + * 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 { AxisExtentModes, ValueLabelModes } from '../constants'; +import { + AxisExtentConfigResult, + DataLayerConfigResult, + ValueLabelMode, + CommonXYDataLayerConfig, +} from '../types'; + +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', + }), +}; + +export const hasBarLayer = (layers: Array) => + layers.filter(({ seriesType }) => seriesType.includes('bar')).length > 0; + +export const hasAreaLayer = (layers: Array) => + layers.filter(({ seriesType }) => seriesType.includes('area')).length > 0; + +export const hasHistogramBarLayer = ( + layers: Array +) => + layers.filter(({ seriesType, isHistogram }) => seriesType.includes('bar') && isHistogram).length > + 0; + +export const validateExtent = ( + extent: AxisExtentConfigResult, + hasBarOrArea: boolean, + dataLayers: Array +) => { + 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 validateFillOpacity = (fillOpacity: number | undefined, hasArea: boolean) => { + if (fillOpacity !== undefined && !hasArea) { + throw new Error(errors.notUsedFillOpacityError()); + } +}; + +export const validateValueLabels = ( + valueLabels: ValueLabelMode, + hasBar: boolean, + hasNotHistogramBars: boolean +) => { + if ((!hasBar || !hasNotHistogramBars) && valueLabels !== ValueLabelModes.HIDE) { + throw new Error(errors.valueLabelsForNotBarsOrHistogramBarsChartsError()); + } +}; 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 23516508dcb09..1bd75e1296c6c 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 @@ -6,58 +6,19 @@ * 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 { LayerTypes, XY_VIS_RENDERER } from '../constants'; import { appendLayerIds } from '../helpers'; -import { AxisExtentConfigResult, DataLayerConfigResult, XYLayerConfig, XyVisFn } from '../types'; +import { XYLayerConfig, 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()); - } -}; +import { + hasAreaLayer, + hasBarLayer, + hasHistogramBarLayer, + validateExtent, + validateFillOpacity, + validateValueLabels, +} from './validate'; export const xyVisFn: XyVisFn['fn'] = async (data, args, handlers) => { const { dataLayers = [], referenceLineLayers = [], annotationLayers = [], ...restArgs } = args; @@ -80,23 +41,16 @@ export const xyVisFn: XyVisFn['fn'] = async (data, args, handlers) => { 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; + const hasBar = hasBarLayer(dataLayers); + const hasArea = hasAreaLayer(dataLayers); validateExtent(args.yLeftExtent, hasBar || hasArea, dataLayers); validateExtent(args.yRightExtent, hasBar || hasArea, dataLayers); + validateFillOpacity(args.fillOpacity, hasArea); - if (!hasArea && args.fillOpacity !== undefined) { - throw new Error(errors.notUsedFillOpacityError()); - } - - const hasNotHistogramBars = - dataLayers.filter(({ seriesType, isHistogram }) => seriesType.includes('bar') && !isHistogram) - .length > 0; + const hasNotHistogramBars = !hasHistogramBarLayer(dataLayers); - if ((!hasBar || !hasNotHistogramBars) && args.valueLabels !== ValueLabelModes.HIDE) { - throw new Error(errors.valueLabelsForNotBarsOrHistogramBarsChartsError()); - } + validateValueLabels(args.valueLabels, hasBar, hasNotHistogramBars); return { type: 'render', diff --git a/src/plugins/chart_expressions/expression_xy/common/helpers/index.ts b/src/plugins/chart_expressions/expression_xy/common/helpers/index.ts index 55c4136e0c00d..19fade1937e39 100644 --- a/src/plugins/chart_expressions/expression_xy/common/helpers/index.ts +++ b/src/plugins/chart_expressions/expression_xy/common/helpers/index.ts @@ -6,4 +6,4 @@ * Side Public License, v 1. */ -export { appendLayerIds } from './layers'; +export { appendLayerIds, getDataLayers } 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 index 344fb3b460cdd..36d25a57edad7 100644 --- a/src/plugins/chart_expressions/expression_xy/common/helpers/layers.ts +++ b/src/plugins/chart_expressions/expression_xy/common/helpers/layers.ts @@ -5,7 +5,8 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import { WithLayerId } from '../types'; +import { LayerTypes } from '../constants'; +import { CommonXYDataLayerConfig, CommonXYLayerConfig, WithLayerId } from '../types'; function isWithLayerId(layer: T): layer is T & WithLayerId { return (layer as T & WithLayerId).layerId ? true : false; @@ -24,3 +25,9 @@ export function appendLayerIds( layerId: isWithLayerId(l) ? l.layerId : generateLayerId(keyword, index), })); } + +export const isDataLayer = (layer: CommonXYLayerConfig): layer is CommonXYDataLayerConfig => + layer.layerType === LayerTypes.DATA || !layer.layerType; + +export const getDataLayers = (layers: CommonXYLayerConfig[]) => + (layers || []).filter((layer): layer is CommonXYDataLayerConfig => isDataLayer(layer)); From 0612cadf255888b45474068966cadb0dd261a255 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Tue, 26 Apr 2022 18:00:55 +0300 Subject: [PATCH 162/213] Fixed extent validation. --- .../expression_functions/layered_xy_vis.ts | 33 +++++---- .../common/expression_functions/validate.ts | 35 +++++++--- .../expression_xy/common/helpers/index.ts | 2 +- .../public/components/data_layers.tsx | 23 +----- .../public/helpers/data_layers.tsx | 12 ++-- .../public/xy_visualization/to_expression.ts | 70 +++++++------------ 6 files changed, 79 insertions(+), 96 deletions(-) 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 2b47f006d311b..a17026053b9e6 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,14 +7,14 @@ */ import { i18n } from '@kbn/i18n'; -import { getDataLayers } from '../helpers'; -import { LayeredXyVisFn } from '../types'; +import { AxisExtentConfigResult, LayeredXyVisFn } from '../types'; import { XY_VIS_RENDERER, EXTENDED_DATA_LAYER, EXTENDED_REFERENCE_LINE_LAYER, LAYERED_XY_VIS, EXTENDED_ANNOTATION_LAYER, + AxisExtentModes, } from '../constants'; import { logDatatables } from '../utils'; import { commonXYArgs } from './common_xy_args'; @@ -23,11 +23,21 @@ import { appendLayerIds } from '../helpers'; import { hasAreaLayer, hasBarLayer, - hasHistogramBarLayer, - validateExtent, - validateFillOpacity, - validateValueLabels, + isValidExtentWithCustomMode, + validateExtentForDataBounds, } from './validate'; +import { getDataLayers } from '../helpers/layers'; + +const getCorrectExtent = (extent: AxisExtentConfigResult, hasBarOrArea: boolean) => { + if ( + extent.mode === AxisExtentModes.CUSTOM && + hasBarOrArea && + !isValidExtentWithCustomMode(extent) + ) { + return { ...extent, lowerBound: NaN, upperBound: NaN }; + } + return extent; +}; export const layeredXyVisFunction: LayeredXyVisFn = { name: LAYERED_XY_VIS, @@ -54,13 +64,10 @@ export const layeredXyVisFunction: LayeredXyVisFn = { const hasBar = hasBarLayer(dataLayers); const hasArea = hasAreaLayer(dataLayers); - validateExtent(args.yLeftExtent, hasBar || hasArea, dataLayers); - validateExtent(args.yRightExtent, hasBar || hasArea, dataLayers); - validateFillOpacity(args.fillOpacity, hasArea); - - const hasNotHistogramBars = !hasHistogramBarLayer(dataLayers); + const { yLeftExtent, yRightExtent } = args; - validateValueLabels(args.valueLabels, hasBar, hasNotHistogramBars); + validateExtentForDataBounds(yLeftExtent, dataLayers); + validateExtentForDataBounds(yRightExtent, dataLayers); return { type: 'render', @@ -69,6 +76,8 @@ export const layeredXyVisFunction: LayeredXyVisFn = { args: { ...args, layers, + yLeftExtent: getCorrectExtent(yLeftExtent, hasBar || hasArea), + yRightExtent: getCorrectExtent(yRightExtent, hasBar || hasArea), ariaLabel: args.ariaLabel ?? (handlers.variables?.embeddableTitle as string) ?? diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/validate.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/validate.ts index cbd4dbd367303..55d7cb12382c0 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/validate.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/validate.ts @@ -51,28 +51,41 @@ export const hasHistogramBarLayer = ( layers.filter(({ seriesType, isHistogram }) => seriesType.includes('bar') && isHistogram).length > 0; -export const validateExtent = ( - extent: AxisExtentConfigResult, - hasBarOrArea: boolean, - dataLayers: Array -) => { +export const isValidExtentWithCustomMode = (extent: AxisExtentConfigResult) => { 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()); - } + return isValidLowerBound && isValidUpperBound; +}; - const lineSeries = dataLayers.filter(({ seriesType }) => seriesType.includes('line')); +export const validateExtentForDataBounds = ( + extent: AxisExtentConfigResult, + layers: Array +) => { + const lineSeries = layers.filter(({ seriesType }) => seriesType.includes('line')); if (!lineSeries.length && extent.mode === AxisExtentModes.DATA_BOUNDS) { throw new Error(errors.dataBoundsForNotLineChartError()); } }; +export const validateExtent = ( + extent: AxisExtentConfigResult, + hasBarOrArea: boolean, + dataLayers: Array +) => { + if ( + extent.mode === AxisExtentModes.CUSTOM && + hasBarOrArea && + !isValidExtentWithCustomMode(extent) + ) { + throw new Error(errors.extendBoundsAreInvalidError()); + } + + validateExtentForDataBounds(extent, dataLayers); +}; + export const validateFillOpacity = (fillOpacity: number | undefined, hasArea: boolean) => { if (fillOpacity !== undefined && !hasArea) { throw new Error(errors.notUsedFillOpacityError()); diff --git a/src/plugins/chart_expressions/expression_xy/common/helpers/index.ts b/src/plugins/chart_expressions/expression_xy/common/helpers/index.ts index 19fade1937e39..55c4136e0c00d 100644 --- a/src/plugins/chart_expressions/expression_xy/common/helpers/index.ts +++ b/src/plugins/chart_expressions/expression_xy/common/helpers/index.ts @@ -6,4 +6,4 @@ * Side Public License, v 1. */ -export { appendLayerIds, getDataLayers } from './layers'; +export { appendLayerIds } from './layers'; 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 b646d4b5261ff..1166d41a9e402 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 @@ -13,7 +13,6 @@ import { LineSeries, } from '@elastic/charts'; import React, { FC } from 'react'; -import { i18n } from '@kbn/i18n'; import { PaletteRegistry } from '@kbn/coloring'; import { FormatFactory } from '@kbn/field-formats-plugin/common'; import { @@ -72,7 +71,7 @@ export const DataLayers: FC = ({ <> {layers.flatMap((layer) => layer.accessors.map((accessor, accessorIndex) => { - const { splitAccessor, seriesType, xAccessor, columnToLabel, layerId } = layer; + const { seriesType, columnToLabel, layerId } = layer; const columnToLabelMap: Record = columnToLabel ? JSON.parse(columnToLabel) : {}; @@ -84,26 +83,6 @@ export const DataLayers: FC = ({ 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 = formattedDatatableInfo.table.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) ); 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 4cd8dca4241bc..3c96f48dfb172 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 @@ -258,7 +258,7 @@ export const getSeriesProps: GetSeriesPropsFn = ({ // 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( + let rows = formattedTable.rows.filter( (row) => !(layer.xAccessor && typeof row[layer.xAccessor] === 'undefined') && !( @@ -269,12 +269,14 @@ export const getSeriesProps: GetSeriesPropsFn = ({ ); if (!layer.xAccessor) { - rows.forEach((row) => { - row.unifiedX = i18n.translate('expressionXY.xyChart.emptyXLabel', { + rows = rows.map((row) => ({ + ...row, + unifiedX: i18n.translate('expressionXY.xyChart.emptyXLabel', { defaultMessage: '(empty)', - }); - }); + }), + })); } + return { splitSeriesAccessors: layer.splitAccessor ? [layer.splitAccessor] : [], stackAccessors: isStacked ? [layer.xAccessor as 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 4b4546ae839ec..c3d5e496fcd4d 100644 --- a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts +++ b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts @@ -10,7 +10,7 @@ import { ScaleType } from '@elastic/charts'; import type { PaletteRegistry } from '@kbn/coloring'; import { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public'; -import type { ExtendedYConfig, YConfig } from '@kbn/expression-xy-plugin/common'; +import type { AxisExtentConfig, ExtendedYConfig, YConfig } from '@kbn/expression-xy-plugin/common'; import type { ExpressionAstExpression } from '@kbn/expressions-plugin/common'; import { State, @@ -252,50 +252,8 @@ export const buildExpression = ( emphasizeFitting: [state.emphasizeFitting || false], curveType: [state.curveType || 'LINEAR'], fillOpacity: [state.fillOpacity || 0.3], - yLeftExtent: [ - { - type: 'expression', - chain: [ - { - type: 'function', - function: 'axisExtentConfig', - arguments: { - mode: [state?.yLeftExtent?.mode || 'full'], - lowerBound: - state?.yLeftExtent?.lowerBound !== undefined - ? [state?.yLeftExtent?.lowerBound] - : [], - upperBound: - state?.yLeftExtent?.upperBound !== undefined - ? [state?.yLeftExtent?.upperBound] - : [], - }, - }, - ], - }, - ], - yRightExtent: [ - { - type: 'expression', - chain: [ - { - type: 'function', - function: 'axisExtentConfig', - arguments: { - mode: [state?.yRightExtent?.mode || 'full'], - lowerBound: - state?.yRightExtent?.lowerBound !== undefined - ? [state?.yRightExtent?.lowerBound] - : [], - upperBound: - state?.yRightExtent?.upperBound !== undefined - ? [state?.yRightExtent?.upperBound] - : [], - }, - }, - ], - }, - ], + yLeftExtent: [axisExtentConfigToExpression(state.yLeftExtent, validDataLayers)], + yRightExtent: [axisExtentConfigToExpression(state.yRightExtent, validDataLayers)], axisTitlesVisibilitySettings: [ { type: 'expression', @@ -570,3 +528,25 @@ const extendedYConfigToExpression = (yConfig: ExtendedYConfig, defaultColor?: st ], }; }; + +const axisExtentConfigToExpression = ( + extent: AxisExtentConfig | undefined, + layers: ValidXYDataLayerConfig[] +): Ast => { + const hasLine = layers.filter(({ seriesType }) => seriesType.includes('line')).length > 0; + const mode = !extent?.mode || (!hasLine && extent?.mode === 'dataBounds') ? 'full' : extent.mode; + return { + type: 'expression', + chain: [ + { + type: 'function', + function: 'axisExtentConfig', + arguments: { + mode: [mode], + lowerBound: extent?.lowerBound !== undefined ? [extent?.lowerBound] : [], + upperBound: extent?.upperBound !== undefined ? [extent?.upperBound] : [], + }, + }, + ], + }; +}; From ad0533ca19e24050f84b8166bc72102466972b8e Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Tue, 26 Apr 2022 18:09:58 +0300 Subject: [PATCH 163/213] Removed comments. --- .../expression_xy/common/types/expression_functions.ts | 3 --- 1 file changed, 3 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 5c311aed6798b..f84cb0a94450b 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 @@ -103,7 +103,6 @@ export interface DataLayerArgs { yScaleType: YScaleType; xScaleType: XScaleType; isHistogram: boolean; - // palette will always be set on the expression palette: PaletteOutput; yConfig?: YConfigResult[]; } @@ -123,9 +122,7 @@ export interface ExtendedDataLayerArgs { yScaleType: YScaleType; xScaleType: XScaleType; isHistogram: boolean; - // palette will always be set on the expression palette: PaletteOutput; - // palette will always be set on the expression yConfig?: YConfigResult[]; table?: Datatable; } From 95a8ed2596f7f5773b3e0b3bdb35e3fcaeb35379 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Tue, 26 Apr 2022 18:43:06 +0300 Subject: [PATCH 164/213] Reduced limit. --- packages/kbn-optimizer/limits.yml | 2 +- .../expression_functions/layered_xy_vis.ts | 58 ++--------------- .../expression_functions/layered_xy_vis_fn.ts | 62 +++++++++++++++++++ .../expression_xy/common/helpers/index.ts | 2 +- .../common/types/expression_functions.ts | 2 +- .../xy_chart_renderer.tsx | 2 +- 6 files changed, 70 insertions(+), 58 deletions(-) create mode 100644 src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis_fn.ts diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index 7a1bb9a267110..1487ee400635a 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -124,7 +124,7 @@ pageLoadAssetSize: visTypeGauge: 24113 unifiedSearch: 104869 data: 454087 - expressionXY: 44598 eventAnnotation: 19334 screenshotting: 22870 synthetics: 40958 + expressionXY: 43281 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 a17026053b9e6..6b926e1ceff05 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,37 +7,15 @@ */ import { i18n } from '@kbn/i18n'; -import { AxisExtentConfigResult, LayeredXyVisFn } from '../types'; +import { LayeredXyVisFn } from '../types'; import { - XY_VIS_RENDERER, EXTENDED_DATA_LAYER, EXTENDED_REFERENCE_LINE_LAYER, LAYERED_XY_VIS, EXTENDED_ANNOTATION_LAYER, - AxisExtentModes, } from '../constants'; -import { logDatatables } from '../utils'; import { commonXYArgs } from './common_xy_args'; import { strings } from '../i18n'; -import { appendLayerIds } from '../helpers'; -import { - hasAreaLayer, - hasBarLayer, - isValidExtentWithCustomMode, - validateExtentForDataBounds, -} from './validate'; -import { getDataLayers } from '../helpers/layers'; - -const getCorrectExtent = (extent: AxisExtentConfigResult, hasBarOrArea: boolean) => { - if ( - extent.mode === AxisExtentModes.CUSTOM && - hasBarOrArea && - !isValidExtentWithCustomMode(extent) - ) { - return { ...extent, lowerBound: NaN, upperBound: NaN }; - } - return extent; -}; export const layeredXyVisFunction: LayeredXyVisFn = { name: LAYERED_XY_VIS, @@ -54,36 +32,8 @@ export const layeredXyVisFunction: LayeredXyVisFn = { multi: true, }, }, - fn(data, args, handlers) { - const layers = appendLayerIds(args.layers ?? [], 'layers'); - - logDatatables(layers, handlers); - - const dataLayers = getDataLayers(layers); - - const hasBar = hasBarLayer(dataLayers); - const hasArea = hasAreaLayer(dataLayers); - - const { yLeftExtent, yRightExtent } = args; - - validateExtentForDataBounds(yLeftExtent, dataLayers); - validateExtentForDataBounds(yRightExtent, dataLayers); - - return { - type: 'render', - as: XY_VIS_RENDERER, - value: { - args: { - ...args, - layers, - yLeftExtent: getCorrectExtent(yLeftExtent, hasBar || hasArea), - yRightExtent: getCorrectExtent(yRightExtent, hasBar || hasArea), - ariaLabel: - args.ariaLabel ?? - (handlers.variables?.embeddableTitle as string) ?? - handlers.getExecutionContext?.()?.description, - }, - }, - }; + async fn(data, args, handlers) { + const { layeredXyVisFn } = await import('./layered_xy_vis_fn'); + return await layeredXyVisFn(data, args, handlers); }, }; 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 new file mode 100644 index 0000000000000..956458f5e0728 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/layered_xy_vis_fn.ts @@ -0,0 +1,62 @@ +/* + * 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 { AxisExtentModes, XY_VIS_RENDERER } from '../constants'; +import { appendLayerIds, getDataLayers } from '../helpers'; +import { AxisExtentConfigResult, LayeredXyVisFn } from '../types'; +import { logDatatables } from '../utils'; +import { + hasAreaLayer, + hasBarLayer, + isValidExtentWithCustomMode, + validateExtentForDataBounds, +} from './validate'; + +const getCorrectExtent = (extent: AxisExtentConfigResult, hasBarOrArea: boolean) => { + if ( + extent.mode === AxisExtentModes.CUSTOM && + hasBarOrArea && + !isValidExtentWithCustomMode(extent) + ) { + return { ...extent, lowerBound: NaN, upperBound: NaN }; + } + return extent; +}; + +export const layeredXyVisFn: LayeredXyVisFn['fn'] = async (data, args, handlers) => { + const layers = appendLayerIds(args.layers ?? [], 'layers'); + + logDatatables(layers, handlers); + + const dataLayers = getDataLayers(layers); + + const hasBar = hasBarLayer(dataLayers); + const hasArea = hasAreaLayer(dataLayers); + + const { yLeftExtent, yRightExtent } = args; + + validateExtentForDataBounds(yLeftExtent, dataLayers); + validateExtentForDataBounds(yRightExtent, dataLayers); + + return { + type: 'render', + as: XY_VIS_RENDERER, + value: { + args: { + ...args, + layers, + yLeftExtent: getCorrectExtent(yLeftExtent, hasBar || hasArea), + yRightExtent: getCorrectExtent(yRightExtent, hasBar || hasArea), + ariaLabel: + args.ariaLabel ?? + (handlers.variables?.embeddableTitle as string) ?? + handlers.getExecutionContext?.()?.description, + }, + }, + }; +}; diff --git a/src/plugins/chart_expressions/expression_xy/common/helpers/index.ts b/src/plugins/chart_expressions/expression_xy/common/helpers/index.ts index 55c4136e0c00d..19fade1937e39 100644 --- a/src/plugins/chart_expressions/expression_xy/common/helpers/index.ts +++ b/src/plugins/chart_expressions/expression_xy/common/helpers/index.ts @@ -6,4 +6,4 @@ * Side Public License, v 1. */ -export { appendLayerIds } from './layers'; +export { appendLayerIds, getDataLayers } from './layers'; 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 f84cb0a94450b..536f6f797bc11 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 @@ -386,7 +386,7 @@ export type LayeredXyVisFn = ExpressionFunctionDefinition< typeof LAYERED_XY_VIS, Datatable, LayeredXYArgs, - XYRender + Promise >; export type DataLayerFn = ExpressionFunctionDefinition< 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 a35216821c077..eb4622329a195 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 @@ -18,7 +18,6 @@ import { ExpressionRenderDefinition } from '@kbn/expressions-plugin'; import { FormatFactory } from '@kbn/field-formats-plugin/common'; import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; import type { XYChartProps } from '../../common'; -import { calculateMinInterval } from '../helpers/interval'; import type { BrushEvent, FilterEvent } from '../types'; export type GetStartDepsFn = () => Promise<{ @@ -57,6 +56,7 @@ export const getXyChartRenderer = ({ const deps = await getStartDeps(); const { XYChartReportable } = await import('../components/xy_chart'); + const { calculateMinInterval } = await import('../helpers/interval'); ReactDOM.render( From adb64430227a7a5cf8a859cad94eb0b3ed60b413 Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Wed, 27 Apr 2022 12:47:51 +0300 Subject: [PATCH 165/213] Fix validation --- .../expression_functions/layered_xy_vis_fn.ts | 12 +++---- .../common/expression_functions/validate.ts | 34 +++++++++++-------- 2 files changed, 26 insertions(+), 20 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 956458f5e0728..0cbee695b2808 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 @@ -38,10 +38,12 @@ export const layeredXyVisFn: LayeredXyVisFn['fn'] = async (data, args, handlers) const hasBar = hasBarLayer(dataLayers); const hasArea = hasAreaLayer(dataLayers); - const { yLeftExtent, yRightExtent } = args; - - validateExtentForDataBounds(yLeftExtent, dataLayers); - validateExtentForDataBounds(yRightExtent, dataLayers); + args.axes?.forEach((axis) => { + if (axis.extent) { + validateExtentForDataBounds(axis.extent, dataLayers); + axis.extent = getCorrectExtent(axis.extent, hasBar || hasArea); + } + }); return { type: 'render', @@ -50,8 +52,6 @@ export const layeredXyVisFn: LayeredXyVisFn['fn'] = async (data, args, handlers) args: { ...args, layers, - yLeftExtent: getCorrectExtent(yLeftExtent, hasBar || hasArea), - yRightExtent: getCorrectExtent(yRightExtent, hasBar || hasArea), ariaLabel: args.ariaLabel ?? (handlers.variables?.embeddableTitle as string) ?? diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/validate.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/validate.ts index 55d7cb12382c0..c628db45fc452 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/validate.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/validate.ts @@ -7,12 +7,13 @@ */ import { i18n } from '@kbn/i18n'; -import { AxisExtentModes, ValueLabelModes } from '../constants'; +import { AxisExtentModes, ValueLabelModes, SeriesTypes } from '../constants'; import { AxisExtentConfigResult, DataLayerConfigResult, ValueLabelMode, CommonXYDataLayerConfig, + YAxisConfigResult, } from '../types'; const errors = { @@ -40,10 +41,10 @@ const errors = { }; export const hasBarLayer = (layers: Array) => - layers.filter(({ seriesType }) => seriesType.includes('bar')).length > 0; + layers.filter(({ seriesType }) => seriesType === SeriesTypes.BAR).length > 0; export const hasAreaLayer = (layers: Array) => - layers.filter(({ seriesType }) => seriesType.includes('area')).length > 0; + layers.filter(({ seriesType }) => seriesType === SeriesTypes.AREA).length > 0; export const hasHistogramBarLayer = ( layers: Array @@ -70,20 +71,25 @@ export const validateExtentForDataBounds = ( } }; -export const validateExtent = ( - extent: AxisExtentConfigResult, +export const validateExtents = ( + dataLayers: Array, hasBarOrArea: boolean, - dataLayers: Array + axes?: YAxisConfigResult[] ) => { - if ( - extent.mode === AxisExtentModes.CUSTOM && - hasBarOrArea && - !isValidExtentWithCustomMode(extent) - ) { - throw new Error(errors.extendBoundsAreInvalidError()); - } + axes?.forEach((axis) => { + if (!axis.extent) { + return; + } + if ( + hasBarOrArea && + axis.extent?.mode === AxisExtentModes.CUSTOM && + !isValidExtentWithCustomMode(axis.extent) + ) { + throw new Error(errors.extendBoundsAreInvalidError()); + } - validateExtentForDataBounds(extent, dataLayers); + validateExtentForDataBounds(axis.extent, dataLayers); + }); }; export const validateFillOpacity = (fillOpacity: number | undefined, hasArea: boolean) => { From 08e4e3b55ca184de9b4bb3ffa9f7ddfa37c7e683 Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Wed, 27 Apr 2022 15:09:06 +0300 Subject: [PATCH 166/213] Fix test --- .../expression_xy/public/components/xy_chart.tsx | 7 ++++++- .../expression_xy/public/helpers/axes_configuration.ts | 8 ++++++-- 2 files changed, 12 insertions(+), 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 c06ee3e545716..76c2ff1d51731 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 @@ -187,7 +187,12 @@ export function XYChart({ filteredLayers.some((layer) => isDataLayer(layer) && layer.splitAccessor); const shouldRotate = isHorizontalChart(dataLayers); - const yAxesConfiguration = getAxesConfiguration(dataLayers, shouldRotate, axes, formatFactory); + const yAxesConfiguration = getAxesConfiguration( + filteredLayers, + shouldRotate, + axes, + formatFactory + ); const xTitle = xAxisConfig?.title || (xAxisColumn && xAxisColumn.name); 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 66e5df9d8a994..3eba46fbacaae 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,7 @@ import type { IFieldFormat, SerializedFieldFormat } from '@kbn/field-formats-plu import { FormatFactory } from '../types'; import { CommonXYDataLayerConfig, + CommonXYReferenceLineLayerConfig, ExtendedYConfigResult, ExtendedYConfig, YAxisConfig, @@ -49,7 +50,10 @@ export function isFormatterCompatible( return formatter1.id === formatter2.id; } -export function groupAxesByType(layers: CommonXYDataLayerConfig[], axes?: YAxisConfig[]) { +export function groupAxesByType( + layers: Array, + axes?: YAxisConfig[] +) { const series: AxesSeries = { auto: [], left: [], @@ -157,7 +161,7 @@ function axisGlobalConfig(position: Position, axes?: YAxisConfig[]) { } export function getAxesConfiguration( - layers: CommonXYDataLayerConfig[], + layers: Array, shouldRotate: boolean, axes?: YAxisConfig[], formatFactory?: FormatFactory From be512b81d901373c76d657f2dae3bafe7b1d6567 Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Wed, 4 May 2022 17:33:41 +0300 Subject: [PATCH 167/213] Some fixes after merging --- .../expression_functions/layered_xy_vis_fn.ts | 6 - .../common/types/expression_renderers.ts | 2 +- .../__snapshots__/xy_chart.test.tsx.snap | 140 +++++++++++++++--- .../public/components/xy_chart.tsx | 9 +- .../public/helpers/axes_configuration.ts | 15 ++ 5 files changed, 145 insertions(+), 27 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 406758a201817..4b7de0eba3166 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 @@ -16,12 +16,6 @@ export const layeredXyVisFn: LayeredXyVisFn['fn'] = async (data, args, handlers) logDatatables(layers, handlers); - args.axes?.forEach((axis) => { - if (axis.extent) { - validateExtentForDataBounds(axis.extent, layers); - } - }); - return { type: 'render', as: XY_VIS_RENDERER, 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 b03ea975b0143..5a2ccb5df6d68 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 @@ -26,7 +26,7 @@ export interface XYRender { export interface CollectiveConfig extends Omit { roundedTimestamp: number; - axisMode: 'bottom'; + position: 'bottom'; icon?: AvailableAnnotationIcon | string; customTooltipDetails?: AnnotationTooltipFormatter | undefined; } 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 3b11ee812da6f..9d874f274b9d0 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 @@ -18,8 +18,8 @@ exports[`XYChart component annotations should render basic line annotation 1`] = yConfig), ...groupedLineAnnotations, ].filter(Boolean); @@ -330,8 +330,11 @@ export function XYChart({ let min: number = NaN; let max: number = NaN; if (extent.mode === 'custom') { - min = extent.lowerBound ?? NaN; - max = extent.upperBound ?? NaN; + const { inclusiveZeroError, boundaryError } = validateExtent(hasBarOrArea, extent); + if (!inclusiveZeroError && !boundaryError) { + min = extent.lowerBound ?? NaN; + max = extent.upperBound ?? NaN; + } } return { 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 3eba46fbacaae..a8cb12716a523 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 @@ -16,6 +16,7 @@ import { ExtendedYConfig, YAxisConfig, YConfig, + AxisExtentConfig, } from '../../common'; import { AxisModes } from '../../common/constants'; import { isDataLayer } from './visualization'; @@ -210,6 +211,20 @@ 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 }; +} + export const getAxisGroupConfig = ( axesGroup?: GroupsConfiguration, yConfig?: ExtendedYConfigResult From 135c1bf06bee98b5e0585baea427836d22e65fc6 Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Wed, 4 May 2022 17:47:37 +0300 Subject: [PATCH 168/213] Fix types --- .../common/types/expression_functions.ts | 18 ------------------ 1 file changed, 18 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 4cf07c5b3a8cb..d9219b1f73d92 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 @@ -339,24 +339,6 @@ export type ExtendedDataLayerConfigResult = Omit & { - type: typeof EXTENDED_DATA_LAYER; - layerType: typeof LayerTypes.DATA; - palette: PaletteOutput; - table: Datatable; -}; - -export type YConfigResult = YConfig & { type: typeof Y_CONFIG }; -export type ExtendedYConfigResult = ExtendedYConfig & { type: typeof EXTENDED_Y_CONFIG }; - export type XAxisConfigResult = AxisConfig & { type: typeof X_AXIS_CONFIG }; export type YAxisConfigResult = YAxisConfig & { type: typeof Y_AXIS_CONFIG }; From 08f7f4bbb2078397ac0693e877d2c087dd941db2 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Wed, 4 May 2022 15:38:59 +0000 Subject: [PATCH 169/213] [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' --- x-pack/plugins/lens/public/xy_visualization/state_helpers.ts | 3 +-- 1 file changed, 1 insertion(+), 2 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 c80f1e0426043..975676e241b76 100644 --- a/x-pack/plugins/lens/public/xy_visualization/state_helpers.ts +++ b/x-pack/plugins/lens/public/xy_visualization/state_helpers.ts @@ -59,8 +59,7 @@ export const getSeriesColor = (layer: XYLayerConfig, accessor: string) => { return null; } return ( - layer?.yConfig?.find((yConfig: YConfig) => yConfig.forAccessor === accessor)?.color || - null + layer?.yConfig?.find((yConfig: YConfig) => yConfig.forAccessor === accessor)?.color || null ); }; From dcba4d7e190e02721ef7725473009ad63f14fd26 Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Thu, 5 May 2022 15:21:35 +0300 Subject: [PATCH 170/213] Fix CI --- src/plugins/chart_expressions/expression_xy/common/index.ts | 1 - x-pack/test/functional/apps/lens/smokescreen.ts | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/common/index.ts b/src/plugins/chart_expressions/expression_xy/common/index.ts index 9883ed245c1d4..db966538b92dd 100755 --- a/src/plugins/chart_expressions/expression_xy/common/index.ts +++ b/src/plugins/chart_expressions/expression_xy/common/index.ts @@ -44,7 +44,6 @@ export type { YAxisConfigResult, CommonXYLayerConfig, AnnotationLayerArgs, - XYLayerConfigResult, ExtendedYConfigResult, DataLayerConfigResult, AxisExtentConfigResult, diff --git a/x-pack/test/functional/apps/lens/smokescreen.ts b/x-pack/test/functional/apps/lens/smokescreen.ts index 36c939704724a..ee5d87a39a9c2 100644 --- a/x-pack/test/functional/apps/lens/smokescreen.ts +++ b/x-pack/test/functional/apps/lens/smokescreen.ts @@ -279,13 +279,13 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { clearWithKeyboard: true, }); - let data = await PageObjects.lens.getCurrentChartDebugState(); + let data = await PageObjects.lens.getCurrentChartDebugState('xyVisChart'); expect(data?.axes?.y?.[1].title).to.eql(axisTitle); // hide the gridlines await testSubjects.click('lnsshowyLeftAxisGridlines'); - data = await PageObjects.lens.getCurrentChartDebugState(); + data = await PageObjects.lens.getCurrentChartDebugState('xyVisChart'); expect(data?.axes?.y?.[1].gridlines.length).to.eql(0); }); From 2809baf0f63ed2f38aa85f2949cc9127ef4cb1a8 Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Thu, 5 May 2022 16:58:49 +0300 Subject: [PATCH 171/213] Fix translations --- .../translations/translations/fr-FR.json | 26 ------------------- .../translations/translations/ja-JP.json | 26 ------------------- .../translations/translations/zh-CN.json | 25 ------------------ 3 files changed, 77 deletions(-) diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index a3f72e12dd15a..31a928d73952a 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -3563,10 +3563,6 @@ "expressionXY.axisExtentConfig.help": "Configurer les étendues d’axe du graphique xy", "expressionXY.axisExtentConfig.lowerBound.help": "Limite inférieure", "expressionXY.axisExtentConfig.upperBound.help": "Limite supérieure", - "expressionXY.axisTitlesVisibilityConfig.help": "Configurer l’aspect des titres d’axe du graphique xy", - "expressionXY.axisTitlesVisibilityConfig.x.help": "Spécifie si le titre de l'axe X est visible ou non.", - "expressionXY.axisTitlesVisibilityConfig.yLeft.help": "Spécifie si le titre de l'axe Y de gauche est visible ou non.", - "expressionXY.axisTitlesVisibilityConfig.yRight.help": "Spécifie si le titre de l'axe Y de droite est visible ou non.", "expressionXY.dataLayer.accessors.help": "Les colonnes à afficher sur l’axe y.", "expressionXY.layer.columnToLabel.help": "Paires clé-valeur JSON de l’ID de colonne pour l’étiquette", "expressionXY.dataLayer.help": "Configurer un calque dans le graphique xy", @@ -3580,14 +3576,6 @@ "expressionXY.dataLayer.xScaleType.help": "Type d’échelle de l’axe x", "expressionXY.dataLayer.yConfig.help": "Configuration supplémentaire pour les axes y", "expressionXY.dataLayer.yScaleType.help": "Type d’échelle des axes y", - "expressionXY.gridlinesConfig.help": "Configurer l’aspect du quadrillage du graphique xy", - "expressionXY.gridlinesConfig.x.help": "Spécifie si le quadrillage de l'axe X est visible ou non.", - "expressionXY.gridlinesConfig.yLeft.help": "Spécifie si le quadrillage de l'axe Y de gauche est visible ou non.", - "expressionXY.gridlinesConfig.yRight.help": "Spécifie si le quadrillage de l'axe Y de droite est visible ou non.", - "expressionXY.labelsOrientationConfig.help": "Configurer l’orientation des étiquettes de coche du graphique xy", - "expressionXY.labelsOrientationConfig.x.help": "Spécifie l'orientation des étiquettes de l'axe X.", - "expressionXY.labelsOrientationConfig.yLeft.help": "Spécifie l'orientation des étiquettes de l'axe Y de gauche.", - "expressionXY.labelsOrientationConfig.yRight.help": "Spécifie l'orientation des étiquettes de l'axe Y de droite.", "expressionXY.legend.filterForValueButtonAriaLabel": "Filtrer sur la valeur", "expressionXY.legend.filterOptionsLegend": "{legendDataLabel}, options de filtre", "expressionXY.legend.filterOutValueButtonAriaLabel": "Exclure la valeur", @@ -3605,10 +3593,6 @@ "expressionXY.referenceLineLayer.accessors.help": "Les colonnes à afficher sur l’axe y.", "expressionXY.referenceLineLayer.help": "Configurer une ligne de référence dans le graphique xy", "expressionXY.referenceLineLayer.yConfig.help": "Configuration supplémentaire pour les axes y", - "expressionXY.tickLabelsConfig.help": "Configurer l’aspect des étiquettes de coche du graphique xy", - "expressionXY.tickLabelsConfig.x.help": "Spécifie si les étiquettes de graduation de l'axe X sont visibles ou non.", - "expressionXY.tickLabelsConfig.yLeft.help": "Spécifie si les étiquettes de graduation de l'axe Y de gauche sont visibles ou non.", - "expressionXY.tickLabelsConfig.yRight.help": "Spécifie si les étiquettes de graduation de l'axe Y de droite sont visibles ou non.", "expressionXY.xyChart.emptyXLabel": "(vide)", "expressionXY.xyChart.iconSelect.alertIconLabel": "Alerte", "expressionXY.xyChart.iconSelect.asteriskIconLabel": "Astérisque", @@ -3625,30 +3609,20 @@ "expressionXY.xyChart.iconSelect.tagIconLabel": "Balise", "expressionXY.xyChart.iconSelect.triangleIconLabel": "Triangle", "expressionXY.xyVis.ariaLabel.help": "Spécifie l’attribut aria-label du graphique xy", - "expressionXY.xyVis.axisTitlesVisibilitySettings.help": "Afficher les titres des axes X et Y", "expressionXY.xyVis.curveType.help": "Définir de quelle façon le type de courbe est rendu pour un graphique linéaire", "expressionXY.xyVis.endValue.help": "Valeur de fin", "expressionXY.xyVis.fillOpacity.help": "Définir l'opacité du remplissage du graphique en aires", "expressionXY.xyVis.fittingFunction.help": "Définir le mode de traitement des valeurs manquantes", - "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.xyVis.labelsOrientation.help": "Définit la rotation des étiquettes des axes", "expressionXY.layeredXyVis.layers.help": "Calques de série visuelle", "expressionXY.xyVis.legend.help": "Configurez la légende du graphique.", "expressionXY.xyVis.logDatatable.breakDown": "Répartir par", "expressionXY.xyVis.logDatatable.metric": "Axe vertical", "expressionXY.xyVis.logDatatable.x": "Axe horizontal", "expressionXY.xyVis.renderer.help": "Outil de rendu de graphique X/Y", - "expressionXY.xyVis.tickLabelsVisibilitySettings.help": "Afficher les étiquettes de graduation des axes X et Y", "expressionXY.xyVis.valueLabels.help": "Mode des étiquettes de valeur", "expressionXY.xyVis.valuesInLegend.help": "Afficher les valeurs dans la légende", - "expressionXY.xyVis.xTitle.help": "Titre de l'axe X", - "expressionXY.xyVis.yLeftExtent.help": "Portée de l'axe Y de gauche", - "expressionXY.xyVis.yLeftTitle.help": "Titre de l'axe Y de gauche", - "expressionXY.xyVis.yRightExtent.help": "Portée de l'axe Y de droite", - "expressionXY.xyVis.yRightTitle.help": "Titre de l'axe Y de droite", - "expressionXY.yConfig.axisMode.help": "Le mode axe de l’indicateur", "expressionXY.yConfig.color.help": "La couleur des séries", "expressionXY.yConfig.fill.help": "Remplir", "expressionXY.yConfig.forAccessor.help": "L’accesseur auquel cette configuration s’applique", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 4b8f0b344b2b5..6377ca47e0dd0 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -3549,10 +3549,6 @@ "expressionXY.axisExtentConfig.help": "xyグラフの軸範囲を構成", "expressionXY.axisExtentConfig.lowerBound.help": "下界", "expressionXY.axisExtentConfig.upperBound.help": "上界", - "expressionXY.axisTitlesVisibilityConfig.help": "xyグラフの軸タイトル表示を構成", - "expressionXY.axisTitlesVisibilityConfig.x.help": "x軸のタイトルを表示するかどうかを指定します。", - "expressionXY.axisTitlesVisibilityConfig.yLeft.help": "左y軸のタイトルを表示するかどうかを指定します。", - "expressionXY.axisTitlesVisibilityConfig.yRight.help": "右y軸のタイトルを表示するかどうかを指定します。", "expressionXY.dataLayer.accessors.help": "y軸に表示する列。", "expressionXY.layer.columnToLabel.help": "ラベリングする列IDのJSONキー値のペア", "expressionXY.dataLayer.help": "xyグラフでレイヤーを構成", @@ -3566,14 +3562,6 @@ "expressionXY.dataLayer.xScaleType.help": "x軸の目盛タイプ", "expressionXY.dataLayer.yConfig.help": "y軸の詳細構成", "expressionXY.dataLayer.yScaleType.help": "y軸の目盛タイプ", - "expressionXY.gridlinesConfig.help": "xyグラフのグリッド線表示を構成", - "expressionXY.gridlinesConfig.x.help": "x 軸のグリッド線を表示するかどうかを指定します。", - "expressionXY.gridlinesConfig.yLeft.help": "左y軸のグリッド線を表示するかどうかを指定します。", - "expressionXY.gridlinesConfig.yRight.help": "右y軸のグリッド線を表示するかどうかを指定します。", - "expressionXY.labelsOrientationConfig.help": "xyグラフのティックラベルの向きを構成", - "expressionXY.labelsOrientationConfig.x.help": "x軸のラベルの向きを指定します。", - "expressionXY.labelsOrientationConfig.yLeft.help": "左y軸のラベルの向きを指定します。", - "expressionXY.labelsOrientationConfig.yRight.help": "右y軸のラベルの向きを指定します。", "expressionXY.legend.filterForValueButtonAriaLabel": "値でフィルター", "expressionXY.legend.filterOptionsLegend": "{legendDataLabel}、フィルターオプション", "expressionXY.legend.filterOutValueButtonAriaLabel": "値を除外", @@ -3591,10 +3579,6 @@ "expressionXY.referenceLineLayer.accessors.help": "y軸に表示する列。", "expressionXY.referenceLineLayer.help": "xyグラフで基準線を構成", "expressionXY.referenceLineLayer.yConfig.help": "y軸の詳細構成", - "expressionXY.tickLabelsConfig.help": "xyグラフのティックラベルの表示を構成", - "expressionXY.tickLabelsConfig.x.help": "x軸の目盛ラベルを表示するかどうかを指定します。", - "expressionXY.tickLabelsConfig.yLeft.help": "左y軸の目盛ラベルを表示するかどうかを指定します。", - "expressionXY.tickLabelsConfig.yRight.help": "右y軸の目盛ラベルを表示するかどうかを指定します。", "expressionXY.xyChart.emptyXLabel": "(空)", "expressionXY.xyChart.iconSelect.alertIconLabel": "アラート", "expressionXY.xyChart.iconSelect.asteriskIconLabel": "アスタリスク", @@ -3611,30 +3595,20 @@ "expressionXY.xyChart.iconSelect.tagIconLabel": "タグ", "expressionXY.xyChart.iconSelect.triangleIconLabel": "三角形", "expressionXY.xyVis.ariaLabel.help": "xyグラフのariaラベルを指定します", - "expressionXY.xyVis.axisTitlesVisibilitySettings.help": "xおよびy軸のタイトルを表示", "expressionXY.xyVis.curveType.help": "折れ線グラフで曲線タイプをレンダリングする方法を定義します", "expressionXY.xyVis.endValue.help": "終了値", "expressionXY.xyVis.fillOpacity.help": "エリアグラフの塗りつぶしの透明度を定義", "expressionXY.xyVis.fittingFunction.help": "欠測値の処理方法を定義", - "expressionXY.xyVis.gridlinesVisibilitySettings.help": "xおよびy軸のグリッド線を表示", "expressionXY.xyVis.help": "X/Y チャート", "expressionXY.xyVis.hideEndzones.help": "部分データの終了ゾーンマーカーを非表示", - "expressionXY.xyVis.labelsOrientation.help": "軸ラベルの回転を定義します", "expressionXY.layeredXyVis.layers.help": "視覚的な系列のレイヤー", "expressionXY.xyVis.legend.help": "チャートの凡例を構成します。", "expressionXY.xyVis.logDatatable.breakDown": "内訳の基準", "expressionXY.xyVis.logDatatable.metric": "縦軸", "expressionXY.xyVis.logDatatable.x": "横軸", "expressionXY.xyVis.renderer.help": "X/Y チャートを再レンダリング", - "expressionXY.xyVis.tickLabelsVisibilitySettings.help": "xおよびy軸の目盛ラベルを表示", "expressionXY.xyVis.valueLabels.help": "値ラベルモード", "expressionXY.xyVis.valuesInLegend.help": "凡例に値を表示", - "expressionXY.xyVis.xTitle.help": "x軸のタイトル", - "expressionXY.xyVis.yLeftExtent.help": "Y左軸範囲", - "expressionXY.xyVis.yLeftTitle.help": "左y軸のタイトル", - "expressionXY.xyVis.yRightExtent.help": "Y右軸範囲", - "expressionXY.xyVis.yRightTitle.help": "右 y 軸のタイトル", - "expressionXY.yConfig.axisMode.help": "メトリックの軸モード", "expressionXY.yConfig.color.help": "系列の色", "expressionXY.yConfig.fill.help": "塗りつぶし", "expressionXY.yConfig.forAccessor.help": "この構成のアクセサー", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 649d6e648c0ec..02ff6b1a95a5a 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -3560,10 +3560,6 @@ "expressionXY.axisExtentConfig.help": "配置 xy 图表的轴范围", "expressionXY.axisExtentConfig.lowerBound.help": "下边界", "expressionXY.axisExtentConfig.upperBound.help": "上边界", - "expressionXY.axisTitlesVisibilityConfig.help": "配置 xy 图表的轴标题外观", - "expressionXY.axisTitlesVisibilityConfig.x.help": "指定 x 轴的标题是否可见。", - "expressionXY.axisTitlesVisibilityConfig.yLeft.help": "指定左侧 y 轴的标题是否可见。", - "expressionXY.axisTitlesVisibilityConfig.yRight.help": "指定右侧 y 轴的标题是否可见。", "expressionXY.dataLayer.accessors.help": "要在 y 轴上显示的列。", "expressionXY.layer.columnToLabel.help": "要标记的列 ID 的 JSON 键值对", "expressionXY.dataLayer.help": "配置 xy 图表中的图层", @@ -3577,14 +3573,6 @@ "expressionXY.dataLayer.xScaleType.help": "x 轴的缩放类型", "expressionXY.dataLayer.yConfig.help": "y 轴的其他配置", "expressionXY.dataLayer.yScaleType.help": "y 轴的缩放类型", - "expressionXY.gridlinesConfig.help": "配置 xy 图表的网格线外观", - "expressionXY.gridlinesConfig.x.help": "指定 x 轴的网格线是否可见。", - "expressionXY.gridlinesConfig.yLeft.help": "指定左侧 y 轴的网格线是否可见。", - "expressionXY.gridlinesConfig.yRight.help": "指定右侧 y 轴的网格线是否可见。", - "expressionXY.labelsOrientationConfig.help": "配置 xy 图表的刻度标签方向", - "expressionXY.labelsOrientationConfig.x.help": "指定 x 轴的标签方向。", - "expressionXY.labelsOrientationConfig.yLeft.help": "指定左 y 轴的标签方向。", - "expressionXY.labelsOrientationConfig.yRight.help": "指定右 y 轴的标签方向。", "expressionXY.legend.filterForValueButtonAriaLabel": "筛留值", "expressionXY.legend.filterOptionsLegend": "{legendDataLabel}, 筛选选项", "expressionXY.legend.filterOutValueButtonAriaLabel": "筛除值", @@ -3602,10 +3590,6 @@ "expressionXY.referenceLineLayer.accessors.help": "要在 y 轴上显示的列。", "expressionXY.referenceLineLayer.help": "配置 xy 图表中的参考线", "expressionXY.referenceLineLayer.yConfig.help": "y 轴的其他配置", - "expressionXY.tickLabelsConfig.help": "配置 xy 图表的刻度标签外观", - "expressionXY.tickLabelsConfig.x.help": "指定 x 轴的刻度标签是否可见。", - "expressionXY.tickLabelsConfig.yLeft.help": "指定左侧 y 轴的刻度标签是否可见。", - "expressionXY.tickLabelsConfig.yRight.help": "指定右侧 y 轴的刻度标签是否可见。", "expressionXY.xyChart.emptyXLabel": "(空)", "expressionXY.xyChart.iconSelect.alertIconLabel": "告警", "expressionXY.xyChart.iconSelect.asteriskIconLabel": "星号", @@ -3622,12 +3606,10 @@ "expressionXY.xyChart.iconSelect.tagIconLabel": "标签", "expressionXY.xyChart.iconSelect.triangleIconLabel": "三角形", "expressionXY.xyVis.ariaLabel.help": "指定 xy 图表的 aria 标签", - "expressionXY.xyVis.axisTitlesVisibilitySettings.help": "显示 x 和 y 轴标题", "expressionXY.xyVis.curveType.help": "定义为折线图渲染曲线类型的方式", "expressionXY.xyVis.endValue.help": "结束值", "expressionXY.xyVis.fillOpacity.help": "定义面积图填充透明度", "expressionXY.xyVis.fittingFunction.help": "定义处理缺失值的方式", - "expressionXY.xyVis.gridlinesVisibilitySettings.help": "显示 x 和 y 轴网格线", "expressionXY.xyVis.help": "X/Y 图表", "expressionXY.xyVis.hideEndzones.help": "隐藏部分数据的末日区域标记", "expressionXY.xyVis.labelsOrientation.help": "定义轴标签的旋转", @@ -3637,15 +3619,8 @@ "expressionXY.xyVis.logDatatable.metric": "垂直轴", "expressionXY.xyVis.logDatatable.x": "水平轴", "expressionXY.xyVis.renderer.help": "X/Y 图表呈现器", - "expressionXY.xyVis.tickLabelsVisibilitySettings.help": "显示 x 和 y 轴刻度标签", "expressionXY.xyVis.valueLabels.help": "值标签模式", "expressionXY.xyVis.valuesInLegend.help": "在图例中显示值", - "expressionXY.xyVis.xTitle.help": "X 轴标题", - "expressionXY.xyVis.yLeftExtent.help": "左侧 Y 轴范围", - "expressionXY.xyVis.yLeftTitle.help": "左侧 Y 轴标题", - "expressionXY.xyVis.yRightExtent.help": "右侧 Y 轴范围", - "expressionXY.xyVis.yRightTitle.help": "右侧 Y 轴标题", - "expressionXY.yConfig.axisMode.help": "指标的轴模式", "expressionXY.yConfig.color.help": "序列的颜色", "expressionXY.yConfig.fill.help": "填充", "expressionXY.yConfig.forAccessor.help": "此配置针对的访问器", From 341135ac8cd00d1cf5eb9c7d3322ae672292fb09 Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Thu, 5 May 2022 17:54:31 +0300 Subject: [PATCH 172/213] Fix CI --- x-pack/plugins/translations/translations/zh-CN.json | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 20510286e1bdf..0704e16397313 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -3608,7 +3608,6 @@ "expressionXY.xyVis.fittingFunction.help": "定义处理缺失值的方式", "expressionXY.xyVis.help": "X/Y 图表", "expressionXY.xyVis.hideEndzones.help": "隐藏部分数据的末日区域标记", - "expressionXY.xyVis.labelsOrientation.help": "定义轴标签的旋转", "expressionXY.layeredXyVis.layers.help": "可视序列的图层", "expressionXY.xyVis.legend.help": "配置图表图例。", "expressionXY.xyVis.logDatatable.breakDown": "细分方式", From 06efe27f35cfb4644873f5fa7e13cdb78d83e382 Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Fri, 13 May 2022 12:49:33 +0300 Subject: [PATCH 173/213] Some fixes after resolve conflicts --- .../expression_xy/common/expression_functions/xy_vis_fn.ts | 6 ++++++ 1 file changed, 6 insertions(+) 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 ab753a9aa087b..de9d10115f300 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 @@ -33,6 +33,9 @@ const createDataLayer = (args: XYArgs, table: Datatable): DataLayerConfigResult yScaleType: args.yScaleType, xScaleType: args.xScaleType, isHistogram: args.isHistogram, + isPercentage: args.isPercentage, + isStacked: args.isStacked, + isHorizontal: args.isHorizontal, palette: args.palette, yConfig: args.yConfig, layerType: LayerTypes.DATA, @@ -57,6 +60,9 @@ export const xyVisFn: XyVisFn['fn'] = async (data, args, handlers) => { yScaleType, xScaleType, isHistogram, + isHorizontal, + isPercentage, + isStacked, yConfig, palette, ...restArgs From ebdacc4d38f451f3493f54fbb82fc846f8f67a9e Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Fri, 13 May 2022 14:29:27 +0300 Subject: [PATCH 174/213] Fix CI --- .../__snapshots__/xy_chart.test.tsx.snap | 54 +++++++++++++++++++ .../public/components/xy_chart.tsx | 2 +- 2 files changed, 55 insertions(+), 1 deletion(-) 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 a788409f22f60..414683188ca0a 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 @@ -4264,6 +4264,7 @@ exports[`XYChart component split chart should render split chart if both, splitR "visible": true, }, "tickLabel": Object { + "fill": undefined, "padding": undefined, "rotation": 0, "visible": true, @@ -4536,6 +4537,7 @@ exports[`XYChart component split chart should render split chart if both, splitR "includeDataFromIds": Array [], "max": NaN, "min": NaN, + "padding": undefined, } } gridLine={ @@ -4554,6 +4556,7 @@ exports[`XYChart component split chart should render split chart if both, splitR "visible": true, }, "tickLabel": Object { + "fill": undefined, "padding": undefined, "rotation": -90, "visible": false, @@ -4854,6 +4857,9 @@ exports[`XYChart component split chart should render split chart if both, splitR ], "columnToLabel": "{\\"a\\": \\"Label A\\", \\"b\\": \\"Label B\\", \\"d\\": \\"Label D\\"}", "isHistogram": false, + "isHorizontal": false, + "isPercentage": false, + "isStacked": false, "layerId": "first", "layerType": "data", "palette": Object { @@ -4952,6 +4958,10 @@ exports[`XYChart component split chart should render split chart if both, splitR yAxesConfiguration={ Array [ Object { + "extent": Object { + "mode": "full", + "type": "axisExtentConfig", + }, "formatter": Object { "convert": [MockFunction] { "calls": Array [ @@ -4975,17 +4985,25 @@ exports[`XYChart component split chart should render split chart if both, splitR }, }, "groupId": "left", + "labelsOrientation": -90, "position": "left", "series": Array [ Object { "accessor": "a", + "axisId": undefined, "layer": "first", }, Object { "accessor": "b", + "axisId": undefined, "layer": "first", }, ], + "showGridLines": false, + "showLabels": false, + "showTitle": true, + "title": "", + "type": "yAxisConfig", }, ] } @@ -5063,6 +5081,7 @@ exports[`XYChart component split chart should render split chart if splitColumnA "visible": true, }, "tickLabel": Object { + "fill": undefined, "padding": undefined, "rotation": 0, "visible": true, @@ -5334,6 +5353,7 @@ exports[`XYChart component split chart should render split chart if splitColumnA "includeDataFromIds": Array [], "max": NaN, "min": NaN, + "padding": undefined, } } gridLine={ @@ -5352,6 +5372,7 @@ exports[`XYChart component split chart should render split chart if splitColumnA "visible": true, }, "tickLabel": Object { + "fill": undefined, "padding": undefined, "rotation": -90, "visible": false, @@ -5652,6 +5673,9 @@ exports[`XYChart component split chart should render split chart if splitColumnA ], "columnToLabel": "{\\"a\\": \\"Label A\\", \\"b\\": \\"Label B\\", \\"d\\": \\"Label D\\"}", "isHistogram": false, + "isHorizontal": false, + "isPercentage": false, + "isStacked": false, "layerId": "first", "layerType": "data", "palette": Object { @@ -5750,6 +5774,10 @@ exports[`XYChart component split chart should render split chart if splitColumnA yAxesConfiguration={ Array [ Object { + "extent": Object { + "mode": "full", + "type": "axisExtentConfig", + }, "formatter": Object { "convert": [MockFunction] { "calls": Array [ @@ -5773,17 +5801,25 @@ exports[`XYChart component split chart should render split chart if splitColumnA }, }, "groupId": "left", + "labelsOrientation": -90, "position": "left", "series": Array [ Object { "accessor": "a", + "axisId": undefined, "layer": "first", }, Object { "accessor": "b", + "axisId": undefined, "layer": "first", }, ], + "showGridLines": false, + "showLabels": false, + "showTitle": true, + "title": "", + "type": "yAxisConfig", }, ] } @@ -5861,6 +5897,7 @@ exports[`XYChart component split chart should render split chart if splitRowAcce "visible": true, }, "tickLabel": Object { + "fill": undefined, "padding": undefined, "rotation": 0, "visible": true, @@ -6132,6 +6169,7 @@ exports[`XYChart component split chart should render split chart if splitRowAcce "includeDataFromIds": Array [], "max": NaN, "min": NaN, + "padding": undefined, } } gridLine={ @@ -6150,6 +6188,7 @@ exports[`XYChart component split chart should render split chart if splitRowAcce "visible": true, }, "tickLabel": Object { + "fill": undefined, "padding": undefined, "rotation": -90, "visible": false, @@ -6450,6 +6489,9 @@ exports[`XYChart component split chart should render split chart if splitRowAcce ], "columnToLabel": "{\\"a\\": \\"Label A\\", \\"b\\": \\"Label B\\", \\"d\\": \\"Label D\\"}", "isHistogram": false, + "isHorizontal": false, + "isPercentage": false, + "isStacked": false, "layerId": "first", "layerType": "data", "palette": Object { @@ -6548,6 +6590,10 @@ exports[`XYChart component split chart should render split chart if splitRowAcce yAxesConfiguration={ Array [ Object { + "extent": Object { + "mode": "full", + "type": "axisExtentConfig", + }, "formatter": Object { "convert": [MockFunction] { "calls": Array [ @@ -6571,17 +6617,25 @@ exports[`XYChart component split chart should render split chart if splitRowAcce }, }, "groupId": "left", + "labelsOrientation": -90, "position": "left", "series": Array [ Object { "accessor": "a", + "axisId": undefined, "layer": "first", }, Object { "accessor": "b", + "axisId": undefined, "layer": "first", }, ], + "showGridLines": false, + "showLabels": false, + "showTitle": true, + "title": "", + "type": "yAxisConfig", }, ] } 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 03bfa91f4c801..153ce1a740dc5 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 @@ -185,7 +185,7 @@ export function XYChart({ [dataLayers, formatFactory] ); - if (filteredLayers.length === 0) { + if (dataLayers.length === 0) { const icon: IconType = getIconForSeriesType(getDataLayers(layers)?.[0]); return ; } From 998e9408195ca4a4b88fe37f74af26bfe770bc07 Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Fri, 3 Jun 2022 11:36:01 +0300 Subject: [PATCH 175/213] Some updates for reference lines --- .../expression_functions/common_axis_args.ts | 4 +- .../expression_functions/extended_y_config.ts | 11 ++ .../reference_line.test.ts | 6 +- .../expression_functions/reference_line.ts | 20 ++- .../common/expression_functions/validate.ts | 12 +- .../expression_functions/x_axis_config.ts | 2 + .../expression_functions/y_axis_config.ts | 2 + .../expression_xy/common/helpers/layers.ts | 4 +- .../common/types/expression_functions.ts | 1 + .../expression_xy/public/__mocks__/index.tsx | 5 +- .../__snapshots__/xy_chart.test.tsx.snap | 30 ----- .../reference_lines/reference_line.tsx | 25 ++-- .../reference_line_annotations.tsx | 21 ++-- .../reference_lines/reference_line_layer.tsx | 27 ++-- .../reference_lines/reference_lines.test.tsx | 116 ++++++++++-------- .../reference_lines/reference_lines.tsx | 6 +- .../components/reference_lines/utils.tsx | 38 ++++-- .../public/components/xy_chart.test.tsx | 28 ++++- .../public/components/xy_chart.tsx | 60 +++++---- .../public/helpers/axes_configuration.test.ts | 22 ++-- .../public/helpers/axes_configuration.ts | 31 +++-- .../expression_xy/public/helpers/layers.ts | 31 ++--- 22 files changed, 278 insertions(+), 224 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_axis_args.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_axis_args.ts index 8e45f0a158cd8..5cf1349a64b32 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_axis_args.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_axis_args.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import { Position } from '@elastic/charts'; import { strings } from '../i18n'; import { XAxisConfigFn, YAxisConfigFn } from '../types'; @@ -25,8 +26,9 @@ export const commonAxisConfigArgs: Omit< }, position: { types: ['string'], + options: [Position.Top, Position.Right, Position.Bottom, Position.Left], help: strings.getAxisPositionHelp(), - default: 'left', + strict: true, }, hide: { types: ['boolean'], diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_y_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_y_config.ts index 1e0090f1b5e40..cf1248e6d6195 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_y_config.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_y_config.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import { Position } from '@elastic/charts'; import { i18n } from '@kbn/i18n'; import { AvailableReferenceLineIcons, @@ -26,6 +27,16 @@ export const extendedYConfigFunction: ExtendedYConfigFn = { inputTypes: ['null'], args: { ...commonYConfigArgs, + position: { + types: ['string'], + options: [Position.Top, Position.Right, Position.Bottom, Position.Left], + help: i18n.translate('expressionXY.referenceLine.axisId.help', { + defaultMessage: + 'Position of axis (first axis of that position) to which the reference line belongs.', + }), + default: Position.Left, + strict: true, + }, lineStyle: { types: ['string'], options: [...Object.values(LineStyles)], diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line.test.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line.test.ts index 4c7c2e3dc628f..663c20f308b02 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line.test.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line.test.ts @@ -15,6 +15,7 @@ describe('referenceLine', () => { const args: ReferenceLineArgs = { value: 100, fill: 'above', + position: 'bottom', }; const result = referenceLineFunction.fn(null, args, createMockExecutionContext()); @@ -40,7 +41,7 @@ describe('referenceLine', () => { value: 100, icon: 'alert', iconPosition: 'below', - axisMode: 'bottom', + position: 'bottom', lineStyle: 'solid', lineWidth: 10, color: '#fff', @@ -69,6 +70,7 @@ describe('referenceLine', () => { name: 'some name', value: 100, fill: 'none', + position: 'bottom', }; const result = referenceLineFunction.fn(null, args, createMockExecutionContext()); @@ -93,6 +95,7 @@ describe('referenceLine', () => { value: 100, textVisibility: true, fill: 'none', + position: 'bottom', }; const result = referenceLineFunction.fn(null, args, createMockExecutionContext()); @@ -119,6 +122,7 @@ describe('referenceLine', () => { name: 'some text', textVisibility, fill: 'none', + position: 'bottom', }; const result = referenceLineFunction.fn(null, args, createMockExecutionContext()); diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line.ts index c294d6ca5aaec..1107d8369d961 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import { Position } from '@elastic/charts'; import { i18n } from '@kbn/i18n'; import { AvailableReferenceLineIcons, @@ -15,7 +16,6 @@ import { LineStyles, REFERENCE_LINE, REFERENCE_LINE_Y_CONFIG, - YAxisModes, } from '../constants'; import { ReferenceLineFn } from '../types'; import { strings } from '../i18n'; @@ -36,13 +36,23 @@ export const referenceLineFunction: ReferenceLineFn = { help: strings.getReferenceLineValueHelp(), required: true, }, - axisMode: { + position: { types: ['string'], - options: [...Object.values(YAxisModes)], - help: strings.getAxisModeHelp(), - default: YAxisModes.AUTO, + options: [Position.Top, Position.Right, Position.Bottom, Position.Left], + help: i18n.translate('expressionXY.referenceLine.axisId.help', { + defaultMessage: + 'Position of axis (first axis of that position) to which the reference line belongs.', + }), + default: Position.Left, strict: true, }, + axisId: { + types: ['string'], + help: i18n.translate('expressionXY.referenceLine.axisId.help', { + defaultMessage: + 'Id of axis to which the reference line belongs. Have more priority than "position"', + }), + }, color: { types: ['string'], help: strings.getColorHelp(), diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/validate.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/validate.ts index 3116e6a5123cb..4859c954d3c23 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/validate.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/validate.ts @@ -115,8 +115,8 @@ export const hasAreaLayer = (layers: Array ) => - layers.filter(({ seriesType, isHistogram }) => seriesType.includes('bar') && isHistogram).length > - 0; + layers.filter(({ seriesType, isHistogram }) => seriesType === SeriesTypes.BAR && isHistogram) + .length > 0; export const isValidExtentWithCustomMode = (extent: AxisExtentConfigResult) => { const isValidLowerBound = @@ -131,7 +131,7 @@ export const validateExtentForDataBounds = ( extent: AxisExtentConfigResult, layers: Array ) => { - const lineSeries = layers.filter(({ seriesType }) => seriesType.includes('line')); + const lineSeries = layers.filter(({ seriesType }) => seriesType === SeriesTypes.LINE); if (!lineSeries.length && extent.mode === AxisExtentModes.DATA_BOUNDS) { throw new Error(errors.dataBoundsForNotLineChartError()); } @@ -175,7 +175,7 @@ export const validateValueLabels = ( }; const isAreaOrLineChart = (seriesType: SeriesType) => - seriesType.includes('line') || seriesType.includes('area'); + seriesType === SeriesTypes.LINE || seriesType === SeriesTypes.AREA; export const validateAddTimeMarker = ( dataLayers: Array, @@ -190,7 +190,7 @@ export const validateMarkSizeForChartType = ( markSizeAccessor: ExpressionValueVisDimension | string | undefined, seriesType: SeriesType ) => { - if (markSizeAccessor && !seriesType.includes('line') && !seriesType.includes('area')) { + if (markSizeAccessor && seriesType !== SeriesTypes.LINE && seriesType !== SeriesTypes.AREA) { throw new Error(errors.markSizeAccessorForNonLineOrAreaChartsError()); } }; @@ -232,7 +232,7 @@ export const validateLinesVisibilityForChartType = ( showLines: boolean | undefined, seriesType: SeriesType ) => { - if (showLines && !(seriesType.includes('line') || seriesType.includes('area'))) { + if (showLines && !(seriesType === SeriesTypes.LINE || seriesType === SeriesTypes.AREA)) { throw new Error(errors.linesVisibilityForNonLineChartError()); } }; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/x_axis_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/x_axis_config.ts index d6fbe3b52b2ed..54ac25294c766 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/x_axis_config.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/x_axis_config.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import { Position } from '@elastic/charts'; import { strings } from '../i18n'; import { XAxisConfigFn } from '../types'; import { X_AXIS_CONFIG } from '../constants'; @@ -24,6 +25,7 @@ export const xAxisConfigFunction: XAxisConfigFn = { return { type: X_AXIS_CONFIG, ...args, + position: args.position ?? Position.Bottom, }; }, }; 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 6e1b2d17915dc..58c1ec8cbb5c9 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 { Position } from '@elastic/charts'; import { strings } from '../i18n'; import { Y_AXIS_CONFIG, AxisModes, AXIS_EXTENT_CONFIG, YScaleTypes } from '../constants'; import { YAxisConfigFn } from '../types'; @@ -43,6 +44,7 @@ export const yAxisConfigFunction: YAxisConfigFn = { return { type: Y_AXIS_CONFIG, ...args, + position: args.position ?? Position.Left, }; }, }; diff --git a/src/plugins/chart_expressions/expression_xy/common/helpers/layers.ts b/src/plugins/chart_expressions/expression_xy/common/helpers/layers.ts index d538db2d3c496..679fbfcc1a339 100644 --- a/src/plugins/chart_expressions/expression_xy/common/helpers/layers.ts +++ b/src/plugins/chart_expressions/expression_xy/common/helpers/layers.ts @@ -14,7 +14,7 @@ import { ExtendedDataLayerArgs, DataLayerArgs, } from '../types'; -import { LayerTypes } from '../constants'; +import { LayerTypes, SeriesTypes } from '../constants'; function isWithLayerId(layer: T): layer is T & WithLayerId { return (layer as T & WithLayerId).layerId ? true : false; @@ -35,7 +35,7 @@ export function appendLayerIds( } export const getShowLines = (args: DataLayerArgs | ExtendedDataLayerArgs) => - args.seriesType.includes('line') || args.seriesType.includes('area') + args.seriesType === SeriesTypes.LINE || args.seriesType !== SeriesTypes.AREA ? args.showLines ?? true : args.showLines; 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 3f9e6e5fc74e0..cab3d1d948305 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 @@ -102,6 +102,7 @@ export interface ExtendedYConfig extends YConfig { fill?: FillStyle; iconPosition?: IconPosition; textVisibility?: boolean; + position?: Position; } export interface YConfig { 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 835fdc9edf337..9a084b45ca328 100644 --- a/src/plugins/chart_expressions/expression_xy/public/__mocks__/index.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/__mocks__/index.tsx @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import { Position } from '@elastic/charts'; import { Datatable } from '@kbn/expressions-plugin/common'; import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; import { LayerTypes } from '../../common/constants'; @@ -200,7 +201,9 @@ export function sampleArgsWithReferenceLine(value: number = 150) { type: 'referenceLineLayer', layerType: LayerTypes.REFERENCELINE, accessors: ['referenceLine-a'], - yConfig: [{ forAccessor: 'referenceLine-a', type: 'extendedYConfig' }], + yConfig: [ + { forAccessor: 'referenceLine-a', type: 'extendedYConfig', position: Position.Left }, + ], table: data, }, ], 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 b1daf77d8fbf4..a9fcc8484f814 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 @@ -844,16 +844,13 @@ exports[`XYChart component it renders area 1`] = ` "groupId": "left", "labelsOrientation": -90, "position": "left", - "scale": "linear", "series": Array [ Object { "accessor": "a", - "axisId": undefined, "layer": "first", }, Object { "accessor": "b", - "axisId": undefined, "layer": "first", }, ], @@ -1433,16 +1430,13 @@ exports[`XYChart component it renders bar 1`] = ` "groupId": "left", "labelsOrientation": -90, "position": "left", - "scale": "linear", "series": Array [ Object { "accessor": "a", - "axisId": undefined, "layer": "first", }, Object { "accessor": "b", - "axisId": undefined, "layer": "first", }, ], @@ -2017,16 +2011,13 @@ exports[`XYChart component it renders horizontal bar 1`] = ` }, "groupId": "left", "position": "bottom", - "scale": "linear", "series": Array [ Object { "accessor": "a", - "axisId": undefined, "layer": "first", }, Object { "accessor": "b", - "axisId": undefined, "layer": "first", }, ], @@ -2601,16 +2592,13 @@ exports[`XYChart component it renders line 1`] = ` "groupId": "left", "labelsOrientation": -90, "position": "left", - "scale": "linear", "series": Array [ Object { "accessor": "a", - "axisId": undefined, "layer": "first", }, Object { "accessor": "b", - "axisId": undefined, "layer": "first", }, ], @@ -3190,16 +3178,13 @@ exports[`XYChart component it renders stacked area 1`] = ` "groupId": "left", "labelsOrientation": -90, "position": "left", - "scale": "linear", "series": Array [ Object { "accessor": "a", - "axisId": undefined, "layer": "first", }, Object { "accessor": "b", - "axisId": undefined, "layer": "first", }, ], @@ -3779,16 +3764,13 @@ exports[`XYChart component it renders stacked bar 1`] = ` "groupId": "left", "labelsOrientation": -90, "position": "left", - "scale": "linear", "series": Array [ Object { "accessor": "a", - "axisId": undefined, "layer": "first", }, Object { "accessor": "b", - "axisId": undefined, "layer": "first", }, ], @@ -4363,16 +4345,13 @@ exports[`XYChart component it renders stacked horizontal bar 1`] = ` }, "groupId": "left", "position": "bottom", - "scale": "linear", "series": Array [ Object { "accessor": "a", - "axisId": undefined, "layer": "first", }, Object { "accessor": "b", - "axisId": undefined, "layer": "first", }, ], @@ -5219,16 +5198,13 @@ exports[`XYChart component split chart should render split chart if both, splitR "groupId": "left", "labelsOrientation": -90, "position": "left", - "scale": "linear", "series": Array [ Object { "accessor": "a", - "axisId": undefined, "layer": "first", }, Object { "accessor": "b", - "axisId": undefined, "layer": "first", }, ], @@ -6074,16 +6050,13 @@ exports[`XYChart component split chart should render split chart if splitColumnA "groupId": "left", "labelsOrientation": -90, "position": "left", - "scale": "linear", "series": Array [ Object { "accessor": "a", - "axisId": undefined, "layer": "first", }, Object { "accessor": "b", - "axisId": undefined, "layer": "first", }, ], @@ -6929,16 +6902,13 @@ exports[`XYChart component split chart should render split chart if splitRowAcce "groupId": "left", "labelsOrientation": -90, "position": "left", - "scale": "linear", "series": Array [ Object { "accessor": "a", - "axisId": undefined, "layer": "first", }, Object { "accessor": "b", - "axisId": undefined, "layer": "first", }, ], diff --git a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_line.tsx b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_line.tsx index 30f4a97986ec3..44efd1b89ff8f 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_line.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_line.tsx @@ -10,22 +10,23 @@ import React, { FC } from 'react'; import { Position } from '@elastic/charts'; import { FieldFormat } from '@kbn/field-formats-plugin/common'; import { ReferenceLineConfig } from '../../../common/types'; -import { getGroupId } from './utils'; import { ReferenceLineAnnotations } from './reference_line_annotations'; +import { GroupsConfiguration } from '../../helpers'; +import { getAxisGroupForReferenceLine } from './utils'; interface ReferenceLineProps { layer: ReferenceLineConfig; paddingMap: Partial>; - formatters: Record<'left' | 'right' | 'bottom', FieldFormat | undefined>; - axesMap: Record<'left' | 'right', boolean>; + xAxisFormatter: FieldFormat; + yAxesConfiguration: GroupsConfiguration; isHorizontal: boolean; nextValue?: number; } export const ReferenceLine: FC = ({ layer, - axesMap, - formatters, + yAxesConfiguration, + xAxisFormatter, paddingMap, isHorizontal, nextValue, @@ -38,17 +39,21 @@ export const ReferenceLine: FC = ({ return null; } - const { axisMode, value } = yConfig; + const { value } = yConfig; - // Find the formatter for the given axis - const groupId = getGroupId(axisMode); + const axisGroup = getAxisGroupForReferenceLine(yAxesConfiguration, yConfig); - const formatter = formatters[groupId || 'bottom']; + const formatter = axisGroup?.formatter || xAxisFormatter; const id = `${layer.layerId}-${value}`; + const axesMap = { + left: yAxesConfiguration.some((axes) => axes.position === 'left'), + right: yAxesConfiguration.some((axes) => axes.position === 'right'), + }; + return ( { - const { name, value, nextValue, fill, axisMode } = annotationConfig; + const { name, value, nextValue, fill, axisGroup } = annotationConfig; const isFillAbove = fill === 'above'; - if (axisMode === 'bottom') { + if (axisGroup?.position === Position.Bottom) { return getBottomRect(name, isFillAbove, formatter, value, nextValue); } @@ -71,13 +69,11 @@ export const ReferenceLineAnnotations: FC = ({ paddingMap, isHorizontal, }) => { - const { id, axisMode, iconPosition, name, textVisibility, value, fill, color } = config; + const { id, axisGroup, iconPosition, name, textVisibility, value, fill, color } = config; - // Find the formatter for the given axis - const groupId = getGroupId(axisMode); const defaultColor = euiLightVars.euiColorDarkShade; // get the position for vertical chart - const markerPositionVertical = getBaseIconPlacement(iconPosition, axesMap, axisMode); + const markerPositionVertical = getBaseIconPlacement(iconPosition, axesMap, axisGroup?.position); // the padding map is built for vertical chart const hasReducedPadding = paddingMap[markerPositionVertical] === LINES_MARKER_SIZE; @@ -89,7 +85,6 @@ export const ReferenceLineAnnotations: FC = ({ }, axesMap, paddingMap, - groupId, isHorizontal ); @@ -108,7 +103,9 @@ export const ReferenceLineAnnotations: FC = ({ key={`${id}-line`} dataValues={[dataValues]} domainType={ - axisMode === 'bottom' ? AnnotationDomainType.XDomain : AnnotationDomainType.YDomain + axisGroup?.position === Position.Bottom + ? AnnotationDomainType.XDomain + : AnnotationDomainType.YDomain } style={{ line: { ...sharedStyle, opacity: 1 } }} /> diff --git a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_line_layer.tsx b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_line_layer.tsx index 73aa3a3d8ba4f..9c4058bdeeea0 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_line_layer.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_line_layer.tsx @@ -11,24 +11,24 @@ import { FieldFormat } from '@kbn/field-formats-plugin/common'; import { groupBy } from 'lodash'; import { Position } from '@elastic/charts'; import { ReferenceLineLayerConfig } from '../../../common/types'; -import { getGroupId } from './utils'; import { ReferenceLineAnnotations } from './reference_line_annotations'; -import { LayerAccessorsTitles } from '../../helpers'; +import { LayerAccessorsTitles, GroupsConfiguration } from '../../helpers'; +import { getAxisGroupForReferenceLine } from './utils'; interface ReferenceLineLayerProps { layer: ReferenceLineLayerConfig; - formatters: Record<'left' | 'right' | 'bottom', FieldFormat | undefined>; paddingMap: Partial>; - axesMap: Record<'left' | 'right', boolean>; isHorizontal: boolean; titles?: LayerAccessorsTitles; + xAxisFormatter: FieldFormat; + yAxesConfiguration: GroupsConfiguration; } export const ReferenceLineLayer: FC = ({ layer, - formatters, + yAxesConfiguration, + xAxisFormatter, paddingMap, - axesMap, isHorizontal, titles, }) => { @@ -51,12 +51,9 @@ export const ReferenceLineLayer: FC = ({ } const referenceLineElements = yConfigByValue.flatMap((yConfig) => { - const { axisMode } = yConfig; + const axisGroup = getAxisGroupForReferenceLine(yAxesConfiguration, yConfig); - // Find the formatter for the given axis - const groupId = getGroupId(axisMode); - - const formatter = formatters[groupId || 'bottom']; + const formatter = axisGroup?.formatter || xAxisFormatter; const name = columnToLabelMap[yConfig.forAccessor] ?? titles?.yTitles?.[yConfig.forAccessor]; const value = row[yConfig.forAccessor]; const yConfigsWithSameDirection = groupedByDirection[yConfig.fill!]; @@ -73,6 +70,11 @@ export const ReferenceLineLayer: FC = ({ const { forAccessor, type, ...restAnnotationConfig } = yConfig; const id = `${layer.layerId}-${yConfig.forAccessor}`; + const axesMap = { + left: yAxesConfiguration.some((axes) => axes.position === 'left'), + right: yAxesConfiguration.some((axes) => axes.position === 'right'), + }; + return ( = ({ nextValue, name, ...restAnnotationConfig, + axisGroup, }} - paddingMap={paddingMap} axesMap={axesMap} + paddingMap={paddingMap} formatter={formatter} isHorizontal={isHorizontal} /> diff --git a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_lines.test.tsx b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_lines.test.tsx index ec657ee293e69..ab6d863ee15ce 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_lines.test.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_lines.test.tsx @@ -82,7 +82,7 @@ interface XCoords { x1: number | undefined; } -function getAxisFromId(layerPrefix: string): ExtendedYConfig['axisMode'] { +function getAxisFromId(layerPrefix: string): ExtendedYConfig['position'] { return /left/i.test(layerPrefix) ? 'left' : /right/i.test(layerPrefix) ? 'right' : 'bottom'; } @@ -90,20 +90,29 @@ const emptyCoords = { x0: undefined, x1: undefined, y0: undefined, y1: undefined describe('ReferenceLines', () => { describe('referenceLineLayers', () => { - 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, + xAxisFormatter: { convert: jest.fn((x) => x) } as unknown as FieldFormat, isHorizontal: false, - axesMap: { left: true, right: false }, + yAxesConfiguration: [ + { + groupId: 'left', + position: 'left', + series: [], + }, + { + groupId: 'right', + position: 'right', + series: [], + }, + { + groupId: 'bottom', + position: 'bottom', + series: [], + }, + ], paddingMap: {}, }; }); @@ -116,14 +125,14 @@ describe('ReferenceLines', () => { ] as Array<[string, Exclude]>)( 'should render a RectAnnotation for a reference line with fill set: %s %s', (layerPrefix, fill) => { - const axisMode = getAxisFromId(layerPrefix); + const position = getAxisFromId(layerPrefix); const wrapper = shallow( { layers={createLayers([ { forAccessor: `${layerPrefix}FirstId`, - axisMode: 'bottom', + position: 'bottom', lineStyle: 'solid', type: 'extendedYConfig', fill, @@ -199,21 +208,21 @@ describe('ReferenceLines', () => { ] as Array<[string, Exclude, 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 position = getAxisFromId(layerPrefix); const wrapper = shallow( { layers={createLayers([ { forAccessor: `${layerPrefix}FirstId`, - axisMode: 'bottom', + position: 'bottom', lineStyle: 'solid', type: 'extendedYConfig', fill, }, { forAccessor: `${layerPrefix}SecondId`, - axisMode: 'bottom', + position: 'bottom', lineStyle: 'solid', type: 'extendedYConfig', fill, @@ -307,7 +316,7 @@ describe('ReferenceLines', () => { it.each(['yAccessorLeft', 'yAccessorRight', 'xAccessor'])( 'should let areas in different directions overlap: %s', (layerPrefix) => { - const axisMode = getAxisFromId(layerPrefix); + const position = getAxisFromId(layerPrefix); const wrapper = shallow( { layers={createLayers([ { forAccessor: `${layerPrefix}FirstId`, - axisMode, + position, lineStyle: 'solid', fill: 'above', type: 'extendedYConfig', }, { forAccessor: `${layerPrefix}SecondId`, - axisMode, + position, lineStyle: 'solid', fill: 'below', type: 'extendedYConfig', @@ -338,8 +347,8 @@ describe('ReferenceLines', () => { ).toEqual( expect.arrayContaining([ { - coordinates: { ...emptyCoords, ...(axisMode === 'bottom' ? { x0: 1 } : { y0: 5 }) }, - details: axisMode === 'bottom' ? 1 : 5, + coordinates: { ...emptyCoords, ...(position === 'bottom' ? { x0: 1 } : { y0: 5 }) }, + details: position === 'bottom' ? 1 : 5, header: undefined, }, ]) @@ -349,8 +358,8 @@ describe('ReferenceLines', () => { ).toEqual( expect.arrayContaining([ { - coordinates: { ...emptyCoords, ...(axisMode === 'bottom' ? { x1: 2 } : { y1: 10 }) }, - details: axisMode === 'bottom' ? 2 : 10, + coordinates: { ...emptyCoords, ...(position === 'bottom' ? { x1: 2 } : { y1: 10 }) }, + details: position === 'bottom' ? 2 : 10, header: undefined, }, ]) @@ -370,14 +379,14 @@ describe('ReferenceLines', () => { layers={createLayers([ { forAccessor: `yAccessorLeftFirstId`, - axisMode: 'left', + position: 'left', lineStyle: 'solid', fill, type: 'extendedYConfig', }, { forAccessor: `yAccessorRightSecondId`, - axisMode: 'right', + position: 'right', lineStyle: 'solid', fill, type: 'extendedYConfig', @@ -415,20 +424,29 @@ describe('ReferenceLines', () => { }); describe('referenceLines', () => { - 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, + xAxisFormatter: { convert: jest.fn((x) => x) } as unknown as FieldFormat, isHorizontal: false, - axesMap: { left: true, right: false }, + yAxesConfiguration: [ + { + groupId: 'left', + position: 'left', + series: [], + }, + { + groupId: 'right', + position: 'right', + series: [], + }, + { + groupId: 'bottom', + position: 'bottom', + series: [], + }, + ], paddingMap: {}, }; }); @@ -441,14 +459,14 @@ describe('ReferenceLines', () => { ] as Array<[string, Exclude]>)( 'should render a RectAnnotation for a reference line with fill set: %s %s', (layerPrefix, fill) => { - const axisMode = getAxisFromId(layerPrefix); + const position = getAxisFromId(layerPrefix); const value = 5; const wrapper = shallow( { {...defaultProps} layers={[ createReferenceLine(layerPrefix, 1, { - axisMode: 'bottom', + position: 'bottom', lineStyle: 'solid', fill, value, @@ -522,20 +540,20 @@ describe('ReferenceLines', () => { ] as Array<[string, Exclude, 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 position = getAxisFromId(layerPrefix); const value = coordsA.y0 ?? coordsA.y1!; const wrapper = shallow( { {...defaultProps} layers={[ createReferenceLine(layerPrefix, 10, { - axisMode: 'bottom', + position: 'bottom', lineStyle: 'solid', fill, value, }), createReferenceLine(layerPrefix, 10, { - axisMode: 'bottom', + position: 'bottom', lineStyle: 'solid', fill, value, @@ -624,7 +642,7 @@ describe('ReferenceLines', () => { it.each(['yAccessorLeft', 'yAccessorRight', 'xAccessor'])( 'should let areas in different directions overlap: %s', (layerPrefix) => { - const axisMode = getAxisFromId(layerPrefix); + const position = getAxisFromId(layerPrefix); const value1 = 1; const value2 = 10; const wrapper = shallow( @@ -632,13 +650,13 @@ describe('ReferenceLines', () => { {...defaultProps} layers={[ createReferenceLine(layerPrefix, 10, { - axisMode, + position, lineStyle: 'solid', fill: 'above', value: value1, }), createReferenceLine(layerPrefix, 10, { - axisMode, + position, lineStyle: 'solid', fill: 'below', value: value2, @@ -654,7 +672,7 @@ describe('ReferenceLines', () => { { coordinates: { ...emptyCoords, - ...(axisMode === 'bottom' ? { x0: value1 } : { y0: value1 }), + ...(position === 'bottom' ? { x0: value1 } : { y0: value1 }), }, details: value1, header: undefined, @@ -670,7 +688,7 @@ describe('ReferenceLines', () => { { coordinates: { ...emptyCoords, - ...(axisMode === 'bottom' ? { x1: value2 } : { y1: value2 }), + ...(position === 'bottom' ? { x1: value2 } : { y1: value2 }), }, details: value2, header: undefined, diff --git a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_lines.tsx b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_lines.tsx index a95d1942e4659..dce4eb9733e3e 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_lines.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_lines.tsx @@ -12,15 +12,15 @@ import React from 'react'; import { Position } from '@elastic/charts'; import type { FieldFormat } from '@kbn/field-formats-plugin/common'; import type { CommonXYReferenceLineLayerConfig, ReferenceLineConfig } from '../../../common/types'; -import { isReferenceLine, LayersAccessorsTitles } from '../../helpers'; +import { GroupsConfiguration, isReferenceLine, LayersAccessorsTitles } from '../../helpers'; import { ReferenceLineLayer } from './reference_line_layer'; import { ReferenceLine } from './reference_line'; import { getNextValuesForReferenceLines } from './utils'; export interface ReferenceLinesProps { layers: CommonXYReferenceLineLayerConfig[]; - formatters: Record<'left' | 'right' | 'bottom', FieldFormat | undefined>; - axesMap: Record<'left' | 'right', boolean>; + xAxisFormatter: FieldFormat; + yAxesConfiguration: GroupsConfiguration; isHorizontal: boolean; paddingMap: Partial>; titles?: LayersAccessorsTitles; diff --git a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/utils.tsx b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/utils.tsx index 85d96c573f314..d36297d7b9e44 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/utils.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/utils.tsx @@ -11,9 +11,16 @@ import { Position } from '@elastic/charts'; import { euiLightVars } from '@kbn/ui-theme'; import { FieldFormat } from '@kbn/field-formats-plugin/common'; import { groupBy, orderBy } from 'lodash'; -import { IconPosition, ReferenceLineConfig, YAxisMode, FillStyle } from '../../../common/types'; +import { + IconPosition, + ReferenceLineConfig, + FillStyle, + ReferenceLineYConfig, + ExtendedYConfigResult, +} from '../../../common/types'; import { FillStyles } from '../../../common/constants'; import { + GroupsConfiguration, LINES_MARKER_SIZE, mapVerticalToHorizontalPlacement, Marker, @@ -27,14 +34,14 @@ import { ReferenceLineAnnotationConfig } from './reference_line_annotations'; export function getBaseIconPlacement( iconPosition: IconPosition | undefined, axesMap?: Record, - axisMode?: YAxisMode + position?: Position ) { if (iconPosition === 'auto') { - if (axisMode === 'bottom') { + if (position === Position.Bottom) { return Position.Top; } if (axesMap) { - if (axisMode === 'left') { + if (position === Position.Left) { return axesMap.right ? Position.Left : Position.Right; } return axesMap.left ? Position.Right : Position.Left; @@ -69,20 +76,19 @@ export const getLineAnnotationProps = ( labels: { markerLabel?: string; markerBodyLabel?: string }, axesMap: Record<'left' | 'right', boolean>, paddingMap: Partial>, - groupId: 'left' | 'right' | undefined, isHorizontal: boolean ) => { // get the position for vertical chart const markerPositionVertical = getBaseIconPlacement( config.iconPosition, axesMap, - config.axisMode + config.axisGroup?.position ); // the padding map is built for vertical chart const hasReducedPadding = paddingMap[markerPositionVertical] === LINES_MARKER_SIZE; return { - groupId, + groupId: config.axisGroup?.groupId || 'bottom', marker: ( ), @@ -107,9 +113,6 @@ export const getLineAnnotationProps = ( }; }; -export const getGroupId = (axisMode: YAxisMode | undefined) => - axisMode === 'bottom' ? undefined : axisMode === 'right' ? 'right' : 'left'; - export const getBottomRect = ( headerLabel: string | undefined, isFillAbove: boolean, @@ -212,3 +215,14 @@ export const computeChartMargins = ( } return result; }; + +export function getAxisGroupForReferenceLine( + yAxesConfiguration: GroupsConfiguration, + yConfig: ReferenceLineYConfig | ExtendedYConfigResult +) { + return yAxesConfiguration.find( + (axis) => + (yConfig.axisId && axis.groupId.includes(yConfig.axisId)) || + yConfig.position === axis.position + ); +} 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 89c3974b327a2..d8933c5f377c7 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 @@ -2102,7 +2102,14 @@ describe('XYChart component', () => { {...defaultProps} args={{ ...args, - yLeftScale: 'sqrt', + axes: [ + { + type: 'yAxisConfig', + position: 'left', + showLabels: true, + scaleType: 'sqrt', + }, + ], }} /> ); @@ -2167,6 +2174,7 @@ describe('XYChart component', () => { type: 'xAxisConfig', id: 'x', showLabels: false, + position: 'bottom', }; const instance = shallow(); @@ -2227,6 +2235,7 @@ describe('XYChart component', () => { type: 'xAxisConfig', id: 'x', showLabels: true, + position: 'bottom', }; const instance = shallow(); @@ -2261,6 +2270,7 @@ describe('XYChart component', () => { id: 'x', showLabels: true, labelsOrientation: -45, + position: 'bottom', }; const instance = shallow(); @@ -2294,6 +2304,7 @@ describe('XYChart component', () => { type: 'xAxisConfig', id: 'x', showLabels: true, + position: 'bottom', }; const instance = shallow(); @@ -2328,6 +2339,7 @@ describe('XYChart component', () => { id: 'x', showLabels: true, labelsOrientation: -45, + position: 'bottom', }; const instance = shallow(); @@ -2405,6 +2417,7 @@ describe('XYChart component', () => { showLabels: true, showGridLines: true, labelsOrientation: 0, + position: 'bottom', }, markSizeRatio: 1, layers: [ @@ -2481,6 +2494,7 @@ describe('XYChart component', () => { showGridLines: false, showLabels: false, title: '', + scaleType: 'linear', extent: { mode: 'full', type: 'axisExtentConfig', @@ -2492,6 +2506,7 @@ describe('XYChart component', () => { labelsOrientation: 0, showGridLines: false, showLabels: false, + scaleType: 'linear', title: '', extent: { mode: 'full', @@ -2506,11 +2521,10 @@ describe('XYChart component', () => { showLabels: true, showGridLines: true, labelsOrientation: 0, + position: 'bottom', }, showTooltip: true, markSizeRatio: 1, - yLeftScale: 'linear', - yRightScale: 'linear', layers: [ { layerId: 'first', @@ -2566,6 +2580,7 @@ describe('XYChart component', () => { showGridLines: false, showLabels: false, title: '', + scaleType: 'linear', extent: { mode: 'full', type: 'axisExtentConfig', @@ -2577,6 +2592,7 @@ describe('XYChart component', () => { labelsOrientation: 0, showGridLines: false, showLabels: false, + scaleType: 'linear', title: '', extent: { mode: 'full', @@ -2591,10 +2607,9 @@ describe('XYChart component', () => { showGridLines: true, labelsOrientation: 0, title: '', + position: 'bottom', }, markSizeRatio: 1, - yLeftScale: 'linear', - yRightScale: 'linear', layers: [ { layerId: 'first', @@ -2782,6 +2797,7 @@ describe('XYChart component', () => { id: 'x', showLabels: true, title: 'My custom x-axis title', + position: 'bottom', }; const component = shallow(); @@ -2811,6 +2827,7 @@ describe('XYChart component', () => { showLabels: true, showTitle: false, title: 'My custom x-axis title', + position: 'bottom', }; const component = shallow(); @@ -2846,6 +2863,7 @@ describe('XYChart component', () => { showLabels: true, showGridLines: true, title: 'My custom x-axis title', + position: 'bottom', }; 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 3a04f2d31c149..cd602cb73ea77 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 @@ -47,7 +47,7 @@ import type { FilterEvent, BrushEvent, FormatFactory } from '../types'; import { isTimeChart } from '../../common/helpers'; import type { CommonXYDataLayerConfig, - ExtendedYConfig, + ExtendedYConfigResult, ReferenceLineYConfig, XYChartProps, } from '../../common/types'; @@ -69,10 +69,15 @@ import { getLinesCausedPaddings, getAxisGroupConfig, validateExtent, + Series, } from '../helpers'; import { getXDomain, XyEndzones } from './x_domain'; import { getLegendAction } from './legend_action'; -import { ReferenceLines, computeChartMargins } from './reference_lines'; +import { + ReferenceLines, + computeChartMargins, + getAxisGroupForReferenceLine, +} from './reference_lines'; import { visualizationDefinitions } from '../definitions'; import { CommonXYLayerConfig } from '../../common/types'; import { SplitChart } from './split_chart'; @@ -235,11 +240,9 @@ export function XYChart({ const yAxesConfiguration = getAxesConfiguration( dataLayers, shouldRotate, - axes, formatFactory, fieldFormats, - yLeftScale, - yRightScale + axes ); const xTitle = xAxisConfig?.title || (xAxisColumn && xAxisColumn.name); @@ -251,7 +254,7 @@ export function XYChart({ const titles = getLayersTitles( dataLayers, { splitColumnAccessor, splitRowAccessor }, - { xTitle: args.xTitle, yTitle: args.yTitle, yRightTitle: args.yRightTitle }, + { xTitle }, yAxesConfiguration ); @@ -284,24 +287,11 @@ export function XYChart({ yRight: yAxesMap?.right?.showLabels ?? true, }; - const getYAxesTitles = (axis: AxisConfiguration) => { - return ( - axis.title || - axis.series - .map( - (series) => - filteredLayers - .find(({ layerId }) => series.layer === layerId) - ?.table.columns.find((column) => column.id === series.accessor)?.name - ) - .filter((name) => Boolean(name))[0] - ); + const getYAxesTitles = (axisSeries: Series[]) => { + return axisSeries + .map(({ layer, accessor }) => titles?.[layer]?.yTitles?.[accessor]) + .filter((name) => Boolean(name))[0]; }; - // const getYAxesTitles = (axisSeries: Series[]) => { - // return axisSeries - // .map(({ layer, accessor }) => titles?.[layer]?.yTitles?.[accessor]) - // .filter((name) => Boolean(name))[0]; - // }; const referenceLineLayers = getReferenceLayers(layers); @@ -321,12 +311,14 @@ export function XYChart({ const rangeAnnotations = getRangeAnnotations(annotationsLayers); const visualConfigs = [ - ...referenceLineLayers.flatMap( - ({ yConfig }) => yConfig - ).map((config) => ({ - ...config, - position: getAxisGroupConfig(yAxesConfiguration, config)?.position, - })), + ...referenceLineLayers + .flatMap(({ yConfig }) => yConfig) + .map((config) => ({ + ...config, + position: config + ? getAxisGroupForReferenceLine(yAxesConfiguration, config)?.position + : Position.Bottom, + })), ...groupedLineAnnotations, ].filter(Boolean); @@ -399,7 +391,13 @@ export function XYChart({ .flatMap((l) => l.yConfig ? l.yConfig.map((yConfig) => ({ layerId: l.layerId, yConfig })) : [] ) - .filter(({ yConfig }) => axis.series.some((s) => s.accessor === yConfig.forAccessor)) + .filter(({ yConfig }) => { + if (yConfig.axisId) { + return axis.groupId.includes(yConfig.axisId); + } + + return axis.position === yConfig.position; + }) .map(({ layerId, yConfig }) => isReferenceLineYConfig(yConfig) ? `${layerId}-${yConfig.value}-${yConfig.fill !== 'none' ? 'rect' : 'line'}` @@ -724,7 +722,7 @@ export function XYChart({ id={axis.groupId} groupId={axis.groupId} position={axis.position} - title={getYAxesTitles(axis)} + title={getYAxesTitles(axis.series)} gridLine={{ visible: axis.showGridLines, }} 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 a165fd55af73e..dc5d8aaa37244 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 @@ -263,7 +263,7 @@ describe('axes_configuration', () => { it('should map auto series to left axis', () => { const formatFactory = jest.fn(); - const groups = getAxesConfiguration([sampleLayer], false, [], formatFactory, fieldFormats); + const groups = getAxesConfiguration([sampleLayer], false, formatFactory, fieldFormats, []); expect(groups.length).toEqual(1); expect(groups[0].position).toEqual('left'); expect(groups[0].series[0].accessor).toEqual('yAccessorId'); @@ -273,7 +273,7 @@ describe('axes_configuration', () => { 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, [], formatFactory, fieldFormats); + const groups = getAxesConfiguration([twoSeriesLayer], false, formatFactory, fieldFormats, []); expect(groups.length).toEqual(2); expect(groups[0].position).toEqual('left'); expect(groups[1].position).toEqual('right'); @@ -287,7 +287,7 @@ describe('axes_configuration', () => { ...sampleLayer, accessors: ['yAccessorId', 'yAccessorId2', 'yAccessorId3'], }; - const groups = getAxesConfiguration([threeSeriesLayer], false, [], formatFactory, fieldFormats); + const groups = getAxesConfiguration([threeSeriesLayer], false, formatFactory, fieldFormats, []); expect(groups.length).toEqual(2); expect(groups[0].position).toEqual('left'); expect(groups[1].position).toEqual('right'); @@ -306,9 +306,9 @@ describe('axes_configuration', () => { }, ], false, - axes, formatFactory, - fieldFormats + fieldFormats, + axes ); expect(groups.length).toEqual(1); expect(groups[0].position).toEqual('right'); @@ -327,9 +327,9 @@ describe('axes_configuration', () => { }, ], false, - axes, formatFactory, - fieldFormats + fieldFormats, + axes ); expect(groups.length).toEqual(2); expect(groups[0].position).toEqual('right'); @@ -337,8 +337,8 @@ describe('axes_configuration', () => { expect(groups[1].position).toEqual('left'); expect(groups[1].series[0].accessor).toEqual('yAccessorId3'); expect(groups[1].series[1].accessor).toEqual('yAccessorId4'); - expect(formatFactory).toHaveBeenCalledWith({ id: 'number' }); - expect(formatFactory).toHaveBeenCalledWith({ id: 'currency' }); + expect(formatFactory).toHaveBeenCalledWith({ id: 'number', params: {} }); + expect(formatFactory).toHaveBeenCalledWith({ id: 'currency', params: {} }); }); it('should create one formatter per series group', () => { @@ -352,9 +352,9 @@ describe('axes_configuration', () => { }, ], false, - axes, formatFactory, - fieldFormats + fieldFormats, + axes ); expect(formatFactory).toHaveBeenCalledTimes(2); expect(formatFactory).toHaveBeenCalledWith({ id: 'number', params: {} }); 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 84f6d453f0a39..c66c00ecd10fe 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 @@ -10,6 +10,7 @@ import { Position } from '@elastic/charts'; import type { IFieldFormat, SerializedFieldFormat } from '@kbn/field-formats-plugin/common'; import { getAccessorByDimension } from '@kbn/visualizations-plugin/common/utils'; import { FormatFactory } from '../types'; +import type { ReferenceLineYConfig } from '../../common/types'; import { AxisExtentConfig, CommonXYDataLayerConfig, @@ -19,6 +20,7 @@ import { ExtendedYConfigResult, } from '../../common'; import { LayersFieldFormats } from './layers'; +import { isReferenceLineYConfig } from './visualization'; export interface Series { layer: string; @@ -53,7 +55,7 @@ export function isFormatterCompatible( export function groupAxesByType( layers: CommonXYDataLayerConfig[], fieldFormats: LayersFieldFormats, - axes?: YAxisConfig[], + axes?: YAxisConfig[] ) { const series: AxesSeries = { auto: [], @@ -72,6 +74,9 @@ export function groupAxesByType( ); const key = axisConfigById?.id || 'auto'; const fieldFormat = fieldFormats[layerId].yAccessors[yAccessor]!; + if (!series[key]) { + series[key] = []; + } series[key].push({ layer: layer.layerId, accessor: yAccessor, fieldFormat }); }); }); @@ -144,9 +149,9 @@ function axisGlobalConfig(position: Position, axes?: YAxisConfig[]) { export function getAxesConfiguration( layers: CommonXYDataLayerConfig[], shouldRotate: boolean, - axes?: YAxisConfig[], formatFactory: FormatFactory | undefined, fieldFormats: LayersFieldFormats, + axes?: YAxisConfig[] ): GroupsConfiguration { const series = groupAxesByType(layers, fieldFormats, axes); @@ -158,11 +163,11 @@ export function getAxesConfiguration( position = getAxisPosition(axis.position || Position.Left, shouldRotate); axisGroups.push({ groupId: `axis-${axis.id}`, - position, formatter: formatFactory?.(series[axis.id][0].fieldFormat), series: series[axis.id].map(({ fieldFormat, ...currentSeries }) => currentSeries), ...axisGlobalConfig(position, axes), ...axis, + position, }); } }); @@ -171,10 +176,10 @@ export function getAxesConfiguration( position = shouldRotate ? 'bottom' : 'left'; axisGroups.push({ groupId: 'left', - position, formatter: formatFactory?.(series.left[0].fieldFormat), series: series.left.map(({ fieldFormat, ...currentSeries }) => currentSeries), ...axisGlobalConfig(position, axes), + position, }); } @@ -182,10 +187,10 @@ export function getAxesConfiguration( position = shouldRotate ? 'top' : 'right'; axisGroups.push({ groupId: 'right', - position, formatter: formatFactory?.(series.right[0].fieldFormat), series: series.right.map(({ fieldFormat, ...currentSeries }) => currentSeries), ...axisGlobalConfig(position, axes), + position, }); } @@ -208,11 +213,15 @@ export function validateExtent(hasBarOrArea: boolean, extent?: AxisExtentConfig) export const getAxisGroupConfig = ( axesGroup?: GroupsConfiguration, - yConfig?: ExtendedYConfigResult + yConfig?: ExtendedYConfigResult | ReferenceLineYConfig ) => { - return axesGroup?.find( - (axis) => - (yConfig?.axisId && yConfig.axisId === axis.groupId) || - axis.series.some(({ accessor }) => accessor === yConfig?.forAccessor) - ); + return axesGroup?.find((axis) => { + if (yConfig?.axisId) { + return axis.groupId.includes(yConfig.axisId); + } + + return yConfig && isReferenceLineYConfig(yConfig) + ? yConfig.position === axis.position + : axis.series.some(({ accessor }) => accessor === yConfig?.forAccessor); + }); }; 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 dd3969c1b6412..ca952736af6f7 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/layers.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/layers.ts @@ -110,7 +110,7 @@ const getAccessorWithFieldFormat = ( const getYAccessorWithFieldFormat = ( dimension: string | ExpressionValueVisDimension | undefined, columns: Datatable['columns'], - seriesType: SeriesType + isPercentage: boolean ) => { if (!dimension) { return {}; @@ -118,7 +118,7 @@ const getYAccessorWithFieldFormat = ( const accessor = getAccessorByDimension(dimension, columns); let format = getFormat(columns, dimension) ?? { id: 'number' }; - if (format?.id !== 'percent' && seriesType.includes('percentage')) { + if (format?.id !== 'percent' && isPercentage) { format = { id: 'percent', params: { pattern: '0.[00]%' } }; } @@ -126,7 +126,7 @@ const getYAccessorWithFieldFormat = ( }; export const getLayerFormats = ( - { xAccessor, accessors, splitAccessor, table, seriesType }: CommonXYDataLayerConfig, + { xAccessor, accessors, splitAccessor, table, isPercentage }: CommonXYDataLayerConfig, { splitColumnAccessor, splitRowAccessor }: SplitAccessors ): LayerFieldFormats => { const yAccessors: Array = accessors; @@ -135,7 +135,7 @@ export const getLayerFormats = ( yAccessors: yAccessors.reduce( (formatters, a) => ({ ...formatters, - ...getYAccessorWithFieldFormat(a, table.columns, seriesType), + ...getYAccessorWithFieldFormat(a, table.columns, isPercentage), }), {} ), @@ -160,28 +160,21 @@ export const getLayersFormats = ( const getTitleForYAccessor = ( layerId: string, yAccessor: string | ExpressionValueVisDimension, - { yTitle, yRightTitle }: Omit, groups: GroupsConfiguration, columns: Datatable['columns'] ) => { const column = getColumnByAccessor(yAccessor, columns); - const isRight = groups.some((group) => - group.series.some( - ({ accessor, layer }) => - accessor === yAccessor && layer === layerId && group.groupId === 'right' - ) + const axisGroup = groups.find((group) => + group.series.some(({ accessor, layer }) => accessor === yAccessor && layer === layerId) ); - if (isRight) { - return yRightTitle || column!.name; - } - return yTitle || column!.name; + return axisGroup?.title || column!.name; }; export const getLayerTitles = ( { xAccessor, accessors, splitAccessor, table, layerId }: CommonXYDataLayerConfig, { splitColumnAccessor, splitRowAccessor }: SplitAccessors, - { xTitle, yTitle, yRightTitle }: CustomTitles, + { xTitle }: CustomTitles, groups: GroupsConfiguration ): LayerAccessorsTitles => { const mapTitle = (dimension?: string | ExpressionValueVisDimension) => { @@ -194,13 +187,7 @@ export const getLayerTitles = ( }; const getYTitle = (accessor: string) => ({ - [accessor]: getTitleForYAccessor( - layerId, - accessor, - { yTitle, yRightTitle }, - groups, - table.columns - ), + [accessor]: getTitleForYAccessor(layerId, accessor, groups, table.columns), }); const xColumnId = xAccessor && getAccessorByDimension(xAccessor, table.columns); From 0c15458f4f314d54b43e35ea824528350751617a Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Fri, 3 Jun 2022 08:41:39 +0000 Subject: [PATCH 176/213] [CI] Auto-commit changed files from 'node scripts/precommit_hook.js --ref HEAD~1..HEAD --fix' --- .../expression_xy/public/components/xy_chart.tsx | 1 - .../chart_expressions/expression_xy/public/helpers/layers.ts | 1 - 2 files changed, 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 cd602cb73ea77..9ffc828ba9f23 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 @@ -67,7 +67,6 @@ import { getAxesConfiguration, GroupsConfiguration, getLinesCausedPaddings, - getAxisGroupConfig, validateExtent, Series, } from '../helpers'; 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 ca952736af6f7..eba7cf7c8a9f3 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/layers.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/layers.ts @@ -17,7 +17,6 @@ import { CommonXYDataLayerConfig, CommonXYLayerConfig, ReferenceLineLayerConfig, - SeriesType, } from '../../common/types'; import { GroupsConfiguration } from './axes_configuration'; import { getFormat } from './format'; From d54334ef9892d4ce4b1ef15d601293fdc8940d42 Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Fri, 3 Jun 2022 11:55:23 +0300 Subject: [PATCH 177/213] Fix translations --- .../common/expression_functions/extended_y_config.ts | 2 +- .../expression_xy/common/expression_functions/reference_line.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_y_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_y_config.ts index cf1248e6d6195..a0b3d48df1c21 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_y_config.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_y_config.ts @@ -30,7 +30,7 @@ export const extendedYConfigFunction: ExtendedYConfigFn = { position: { types: ['string'], options: [Position.Top, Position.Right, Position.Bottom, Position.Left], - help: i18n.translate('expressionXY.referenceLine.axisId.help', { + help: i18n.translate('expressionXY.referenceLine.position.help', { defaultMessage: 'Position of axis (first axis of that position) to which the reference line belongs.', }), diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line.ts index 1107d8369d961..5835bf013147c 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line.ts @@ -39,7 +39,7 @@ export const referenceLineFunction: ReferenceLineFn = { position: { types: ['string'], options: [Position.Top, Position.Right, Position.Bottom, Position.Left], - help: i18n.translate('expressionXY.referenceLine.axisId.help', { + help: i18n.translate('expressionXY.referenceLine.position.help', { defaultMessage: 'Position of axis (first axis of that position) to which the reference line belongs.', }), From cb0ed0af62dfa83daef65795cdcdb37bf5822a33 Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Fri, 3 Jun 2022 12:13:04 +0300 Subject: [PATCH 178/213] Fix CI --- .../common/expression_functions/extended_data_layer.test.ts | 3 +++ .../expression_xy/common/helpers/layers.test.ts | 3 +++ .../expression_xy/common/helpers/visualization.test.ts | 3 +++ 3 files changed, 9 insertions(+) diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_data_layer.test.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_data_layer.test.ts index 56163d165b458..5ec11188058f5 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_data_layer.test.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_data_layer.test.ts @@ -20,6 +20,9 @@ describe('extendedDataLayerConfig', () => { splitAccessor: 'd', xScaleType: 'linear', isHistogram: false, + isHorizontal: false, + isPercentage: false, + isStacked: false, palette: mockPaletteOutput, }; 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 index 895abdb7a60df..d821f2aa8d147 100644 --- a/src/plugins/chart_expressions/expression_xy/common/helpers/layers.test.ts +++ b/src/plugins/chart_expressions/expression_xy/common/helpers/layers.test.ts @@ -59,6 +59,9 @@ describe('#getDataLayers', () => { seriesType: 'bar', xScaleType: 'time', isHistogram: false, + isHorizontal: false, + isPercentage: false, + isStacked: false, table: { rows: [], columns: [], type: 'datatable' }, palette: { type: 'system_palette', name: 'system' }, }, diff --git a/src/plugins/chart_expressions/expression_xy/common/helpers/visualization.test.ts b/src/plugins/chart_expressions/expression_xy/common/helpers/visualization.test.ts index 678c342e38b49..14903c0c54a27 100644 --- a/src/plugins/chart_expressions/expression_xy/common/helpers/visualization.test.ts +++ b/src/plugins/chart_expressions/expression_xy/common/helpers/visualization.test.ts @@ -20,6 +20,9 @@ describe('#isTimeChart', () => { seriesType: 'bar', xScaleType: 'time', isHistogram: false, + isHorizontal: false, + isPercentage: false, + isStacked: false, table: { rows: [], columns: [ From d5ba3b349feb1dc7157b9721396f437e42137591 Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Fri, 3 Jun 2022 12:52:40 +0300 Subject: [PATCH 179/213] Fix types --- .../exploratory_view/configurations/lens_attributes.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.ts index 65daef7ac85f6..740c45993ef04 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.ts @@ -29,6 +29,7 @@ import { TypedLensByValueInput, XYCurveType, XYState, + YAxisMode, } from '@kbn/lens-plugin/public'; import type { DataView } from '@kbn/data-views-plugin/common'; import { PersistableFilter } from '@kbn/lens-plugin/common'; @@ -900,8 +901,8 @@ export class LensAttributes { this.layerConfigs[0].indexPattern.fieldFormatMap[ this.layerConfigs[0].selectedMetricField ]?.id - ? 'left' - : 'right', + ? ('left' as YAxisMode) + : ('right' as YAxisMode), }, ], xAccessor: `x-axis-column-layer${index}`, From 0801c8ef8da71f5227f214810cd71c3dd91dfc9f Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Mon, 6 Jun 2022 19:09:16 +0300 Subject: [PATCH 180/213] Add validations for axes and rename `axes` arg to `yAxisConfigs` --- .../expression_xy/common/__mocks__/index.ts | 2 +- .../expression_functions/common_xy_args.ts | 4 +- .../expression_functions/layered_xy_vis_fn.ts | 3 ++ .../common/expression_functions/validate.ts | 26 ++++++++++++- .../common/expression_functions/xy_vis_fn.ts | 4 +- .../expression_xy/common/i18n/index.tsx | 4 +- .../common/types/expression_functions.ts | 6 +-- .../public/components/xy_chart.test.tsx | 36 +++++++++--------- .../public/components/xy_chart.tsx | 4 +- .../public/helpers/axes_configuration.test.ts | 8 ++-- .../public/helpers/axes_configuration.ts | 20 +++++----- .../public/xy_visualization/to_expression.ts | 38 ++++++++++--------- 12 files changed, 93 insertions(+), 62 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 f883fa36b9104..1bde83c1822b7 100644 --- a/src/plugins/chart_expressions/expression_xy/common/__mocks__/index.ts +++ b/src/plugins/chart_expressions/expression_xy/common/__mocks__/index.ts @@ -103,7 +103,7 @@ export const createArgsWithLayers = ( showTitle: true, title: '', }, - axes: [ + yAxisConfigs: [ { type: 'yAxisConfig', position: 'right', 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 d2348b70e28fd..df9c4abdfe22e 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 @@ -78,9 +78,9 @@ export const commonXYArgs: CommonXYFn['args'] = { types: [X_AXIS_CONFIG], help: strings.getXAxisConfigHelp(), }, - axes: { + yAxisConfigs: { types: [Y_AXIS_CONFIG], - help: strings.getAxesHelp(), + help: strings.getyAxisConfigsHelp(), multi: true, }, detailedTooltip: { 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 fb7c91c682847..9e1ef1cc0cb9f 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 @@ -15,6 +15,7 @@ import { validateMinTimeBarInterval, hasBarLayer, errors, + validateAxes, } from './validate'; import { appendLayerIds, getDataLayers } from '../helpers'; @@ -35,6 +36,8 @@ export const layeredXyVisFn: LayeredXyVisFn['fn'] = async (data, args, handlers) throw new Error(errors.markSizeRatioWithoutAccessor()); } + validateAxes(dataLayers, args.yAxisConfigs); + return { type: 'render', as: XY_VIS_RENDERER, diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/validate.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/validate.ts index 4859c954d3c23..1738137478303 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/validate.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/validate.ts @@ -104,6 +104,12 @@ export const errors = { defaultMessage: '`minTimeBarInterval` argument is applicable only for time bar charts.', } ), + axisIsNotAssignedError: (axisId: string) => + i18n.translate('expressionXY.reusable.function.xyVis.errors.axisIsNotAssignedError', { + defaultMessage: + 'Axis with id: "{axisId}" is not assigned to any accessor. Please assign axis using the following construction: `yConfig=\\{yConfig forAccessor="your-accessor" axisId="{axisId}"\\}`', + values: { axisId }, + }), }; export const hasBarLayer = (layers: Array) => @@ -140,9 +146,9 @@ export const validateExtentForDataBounds = ( export const validateExtents = ( dataLayers: Array, hasBarOrArea: boolean, - axes?: YAxisConfigResult[] + yAxisConfigs?: YAxisConfigResult[] ) => { - axes?.forEach((axis) => { + yAxisConfigs?.forEach((axis) => { if (!axis.extent) { return; } @@ -158,6 +164,22 @@ export const validateExtents = ( }); }; +export const validateAxes = ( + dataLayers: Array, + yAxisConfigs?: YAxisConfigResult[] +) => { + yAxisConfigs?.forEach((axis) => { + if ( + axis.id && + dataLayers.every( + (layer) => !layer.yConfig || layer.yConfig?.every((config) => config.axisId !== axis.id) + ) + ) { + throw new Error(errors.axisIsNotAssignedError(axis.id)); + } + }); +}; + export const validateFillOpacity = (fillOpacity: number | undefined, hasArea: boolean) => { if (fillOpacity !== undefined && !hasArea) { throw new Error(errors.notUsedFillOpacityError()); 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 6cf363d111781..40ce134371544 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 @@ -33,6 +33,7 @@ import { validateLineWidthForChartType, validatePointsRadiusForChartType, validateLinesVisibilityForChartType, + validateAxes, } from './validate'; const createDataLayer = (args: XYArgs, table: Datatable): DataLayerConfigResult => { @@ -126,7 +127,7 @@ export const xyVisFn: XyVisFn['fn'] = async (data, args, handlers) => { const hasBar = hasBarLayer(dataLayers); const hasArea = hasAreaLayer(dataLayers); - validateExtents(dataLayers, hasBar || hasArea, args.axes); + validateExtents(dataLayers, hasBar || hasArea, args.yAxisConfigs); validateFillOpacity(args.fillOpacity, hasArea); validateAddTimeMarker(dataLayers, args.addTimeMarker); validateMinTimeBarInterval(dataLayers, hasBar, args.minTimeBarInterval); @@ -139,6 +140,7 @@ export const xyVisFn: XyVisFn['fn'] = async (data, args, handlers) => { validateLineWidthForChartType(lineWidth, args.seriesType); validateShowPointsForChartType(showPoints, args.seriesType); validatePointsRadiusForChartType(pointsRadius, args.seriesType); + validateAxes(dataLayers, args.yAxisConfigs); 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 index 7d0a80c17e54b..5fc8f2d554229 100644 --- a/src/plugins/chart_expressions/expression_xy/common/i18n/index.tsx +++ b/src/plugins/chart_expressions/expression_xy/common/i18n/index.tsx @@ -81,8 +81,8 @@ export const strings = { i18n.translate('expressionXY.xyVis.xAxisConfig.help', { defaultMessage: 'Specifies x-axis config', }), - getAxesHelp: () => - i18n.translate('expressionXY.xyVis.axes.help', { + getyAxisConfigsHelp: () => + i18n.translate('expressionXY.xyVis.yAxisConfigs.help', { defaultMessage: 'Specifies y-axes configs', }), getDetailedTooltipHelp: () => 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 cab3d1d948305..c207abd47e4e3 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 @@ -220,7 +220,7 @@ export interface XYArgs extends DataLayerArgs { hideEndzones?: boolean; valuesInLegend?: boolean; ariaLabel?: string; - axes?: YAxisConfigResult[]; + yAxisConfigs?: YAxisConfigResult[]; xAxisConfig?: XAxisConfigResult; addTimeMarker?: boolean; markSizeRatio?: number; @@ -244,7 +244,7 @@ export interface LayeredXYArgs { hideEndzones?: boolean; valuesInLegend?: boolean; ariaLabel?: string; - axes?: YAxisConfigResult[]; + yAxisConfigs?: YAxisConfigResult[]; xAxisConfig?: XAxisConfigResult; detailedTooltip?: boolean; addTimeMarker?: boolean; @@ -266,7 +266,7 @@ export interface XYProps { hideEndzones?: boolean; valuesInLegend?: boolean; ariaLabel?: string; - axes?: YAxisConfigResult[]; + yAxisConfigs?: YAxisConfigResult[]; xAxisConfig?: XAxisConfigResult; addTimeMarker?: boolean; markSizeRatio?: 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 d8933c5f377c7..597c2e514e8ba 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 @@ -512,7 +512,7 @@ describe('XYChart component', () => { {...defaultProps} args={{ ...args, - axes: [ + yAxisConfigs: [ { type: 'yAxisConfig', position: 'left', @@ -541,7 +541,7 @@ describe('XYChart component', () => { {...defaultProps} args={{ ...args, - axes: [ + yAxisConfigs: [ { type: 'yAxisConfig', position: 'left', @@ -568,7 +568,7 @@ describe('XYChart component', () => { {...defaultProps} args={{ ...args, - axes: [ + yAxisConfigs: [ { type: 'yAxisConfig', position: 'left', @@ -601,7 +601,7 @@ describe('XYChart component', () => { {...defaultProps} args={{ ...args, - axes: [ + yAxisConfigs: [ { type: 'yAxisConfig', position: 'left', @@ -1661,7 +1661,7 @@ describe('XYChart component', () => { table: dataWithoutFormats, }, ], - axes: [ + yAxisConfigs: [ { type: 'yAxisConfig', id: '1', @@ -1727,7 +1727,7 @@ describe('XYChart component', () => { table: dataWithoutFormats, }, ], - axes: [ + yAxisConfigs: [ { type: 'yAxisConfig', id: '1', @@ -2102,7 +2102,7 @@ describe('XYChart component', () => { {...defaultProps} args={{ ...args, - axes: [ + yAxisConfigs: [ { type: 'yAxisConfig', position: 'left', @@ -2157,7 +2157,7 @@ describe('XYChart component', () => { test('it should set the tickLabel visibility on the x axis if the tick labels is hidden', () => { const { args } = sampleArgs(); - args.axes = [ + args.yAxisConfigs = [ { type: 'yAxisConfig', position: 'left', @@ -2191,7 +2191,7 @@ describe('XYChart component', () => { test('it should set the tickLabel visibility on the y axis if the tick labels is hidden', () => { const { args } = sampleArgs(); - args.axes = [ + args.yAxisConfigs = [ { type: 'yAxisConfig', position: 'left', @@ -2218,7 +2218,7 @@ describe('XYChart component', () => { test('it should set the tickLabel visibility on the x axis if the tick labels is shown', () => { const { args } = sampleArgs(); - args.axes = [ + args.yAxisConfigs = [ { type: 'yAxisConfig', position: 'left', @@ -2252,7 +2252,7 @@ describe('XYChart component', () => { test('it should set the tickLabel orientation on the x axis', () => { const { args } = sampleArgs(); - args.axes = [ + args.yAxisConfigs = [ { type: 'yAxisConfig', position: 'left', @@ -2287,7 +2287,7 @@ describe('XYChart component', () => { test('it should set the tickLabel visibility on the y axis if the tick labels is shown', () => { const { args } = sampleArgs(); - args.axes = [ + args.yAxisConfigs = [ { type: 'yAxisConfig', position: 'left', @@ -2321,7 +2321,7 @@ describe('XYChart component', () => { test('it should set the tickLabel orientation on the y axis', () => { const { args } = sampleArgs(); - args.axes = [ + args.yAxisConfigs = [ { type: 'yAxisConfig', position: 'left', @@ -2384,7 +2384,7 @@ describe('XYChart component', () => { showTooltip: true, legend: { type: 'legendConfig', isVisible: false, position: Position.Top }, valueLabels: 'hide', - axes: [ + yAxisConfigs: [ { type: 'yAxisConfig', position: 'left', @@ -2486,7 +2486,7 @@ describe('XYChart component', () => { const args: XYProps = { legend: { type: 'legendConfig', isVisible: false, position: Position.Top }, valueLabels: 'hide', - axes: [ + yAxisConfigs: [ { type: 'yAxisConfig', position: 'left', @@ -2572,7 +2572,7 @@ describe('XYChart component', () => { showTooltip: true, legend: { type: 'legendConfig', isVisible: true, position: Position.Top }, valueLabels: 'hide', - axes: [ + yAxisConfigs: [ { type: 'yAxisConfig', position: 'left', @@ -2808,7 +2808,7 @@ describe('XYChart component', () => { test('it should hide the X axis title if the corresponding switch is off', () => { const { args } = sampleArgs(); - args.axes = [ + args.yAxisConfigs = [ { type: 'yAxisConfig', position: 'left', @@ -2844,7 +2844,7 @@ describe('XYChart component', () => { test('it should show the X axis gridlines if the setting is on', () => { const { args } = sampleArgs(); - args.axes = [ + args.yAxisConfigs = [ { type: 'yAxisConfig', position: 'left', 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 9ffc828ba9f23..ab3b9f0efa433 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 @@ -180,7 +180,7 @@ export function XYChart({ valueLabels, hideEndzones, valuesInLegend, - axes, + yAxisConfigs, xAxisConfig, splitColumnAccessor, splitRowAccessor, @@ -241,7 +241,7 @@ export function XYChart({ shouldRotate, formatFactory, fieldFormats, - axes + yAxisConfigs ); const xTitle = xAxisConfig?.title || (xAxisColumn && xAxisColumn.name); 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 dc5d8aaa37244..26810055fb992 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 axes: AxisConfig[] = [ + const yAxisConfigs: AxisConfig[] = [ { id: '1', position: 'right', @@ -308,7 +308,7 @@ describe('axes_configuration', () => { false, formatFactory, fieldFormats, - axes + yAxisConfigs ); expect(groups.length).toEqual(1); expect(groups[0].position).toEqual('right'); @@ -329,7 +329,7 @@ describe('axes_configuration', () => { false, formatFactory, fieldFormats, - axes + yAxisConfigs ); expect(groups.length).toEqual(2); expect(groups[0].position).toEqual('right'); @@ -354,7 +354,7 @@ describe('axes_configuration', () => { false, formatFactory, fieldFormats, - axes + yAxisConfigs ); expect(formatFactory).toHaveBeenCalledTimes(2); expect(formatFactory).toHaveBeenCalledWith({ id: 'number', params: {} }); 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 c66c00ecd10fe..95aaa81ddaafe 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 @@ -55,7 +55,7 @@ export function isFormatterCompatible( export function groupAxesByType( layers: CommonXYDataLayerConfig[], fieldFormats: LayersFieldFormats, - axes?: YAxisConfig[] + yAxisConfigs?: YAxisConfig[] ) { const series: AxesSeries = { auto: [], @@ -69,7 +69,7 @@ export function groupAxesByType( const yConfig: Array | undefined = layer.yConfig; const yAccessor = getAccessorByDimension(accessor, table.columns); const yConfigByAccessor = yConfig?.find((config) => config.forAccessor === yAccessor); - const axisConfigById = axes?.find( + const axisConfigById = yAxisConfigs?.find( (axis) => yConfigByAccessor?.axisId && axis.id && axis.id === yConfigByAccessor?.axisId ); const key = axisConfigById?.id || 'auto'; @@ -142,8 +142,8 @@ export function getAxisPosition(position: Position, shouldRotate: boolean) { return position; } -function axisGlobalConfig(position: Position, axes?: YAxisConfig[]) { - return axes?.find((axis) => !axis.id && axis.position === position) || {}; +function axisGlobalConfig(position: Position, yAxisConfigs?: YAxisConfig[]) { + return yAxisConfigs?.find((axis) => !axis.id && axis.position === position) || {}; } export function getAxesConfiguration( @@ -151,21 +151,21 @@ export function getAxesConfiguration( shouldRotate: boolean, formatFactory: FormatFactory | undefined, fieldFormats: LayersFieldFormats, - axes?: YAxisConfig[] + yAxisConfigs?: YAxisConfig[] ): GroupsConfiguration { - const series = groupAxesByType(layers, fieldFormats, axes); + const series = groupAxesByType(layers, fieldFormats, yAxisConfigs); const axisGroups: GroupsConfiguration = []; let position: Position; - axes?.forEach((axis) => { + yAxisConfigs?.forEach((axis) => { if (axis.id && series[axis.id] && series[axis.id].length > 0) { position = getAxisPosition(axis.position || Position.Left, shouldRotate); axisGroups.push({ groupId: `axis-${axis.id}`, formatter: formatFactory?.(series[axis.id][0].fieldFormat), series: series[axis.id].map(({ fieldFormat, ...currentSeries }) => currentSeries), - ...axisGlobalConfig(position, axes), + ...axisGlobalConfig(position, yAxisConfigs), ...axis, position, }); @@ -178,7 +178,7 @@ export function getAxesConfiguration( groupId: 'left', formatter: formatFactory?.(series.left[0].fieldFormat), series: series.left.map(({ fieldFormat, ...currentSeries }) => currentSeries), - ...axisGlobalConfig(position, axes), + ...axisGlobalConfig(position, yAxisConfigs), position, }); } @@ -189,7 +189,7 @@ export function getAxesConfiguration( groupId: 'right', formatter: formatFactory?.(series.right[0].fieldFormat), series: series.right.map(({ fieldFormat, ...currentSeries }) => currentSeries), - ...axisGlobalConfig(position, axes), + ...axisGlobalConfig(position, yAxisConfigs), position, }); } 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 fbe4c214c620f..534a0d3c76ea5 100644 --- a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts +++ b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts @@ -199,7 +199,7 @@ export const buildExpression = ( yConfig?.some((config) => config.axisMode === 'right') ); - const axes: YAxisConfig[] = [ + const yAxisConfigs: YAxisConfig[] = [ { position: 'left', extent: state?.yLeftExtent ? { ...state?.yLeftExtent, type: 'axisExtentConfig' } : undefined, @@ -225,14 +225,14 @@ export const buildExpression = ( ]; if (isLeftAxis) { - axes.push({ + yAxisConfigs.push({ id: 'left', position: 'left', }); } if (isRightAxis) { - axes.push({ + yAxisConfigs.push({ id: 'right', position: 'right', }); @@ -296,7 +296,7 @@ export const buildExpression = ( valueLabels: [state?.valueLabels || 'hide'], hideEndzones: [state?.hideEndzones || false], valuesInLegend: [state?.valuesInLegend || false], - axes: [...axesToExpression(axes, validDataLayers)], + yAxisConfigs: [...yAxisConfigsToExpression(yAxisConfigs, validDataLayers)], xAxisConfig: [ { type: 'expression', @@ -321,7 +321,7 @@ export const buildExpression = ( ...validDataLayers.map((layer) => dataLayerToExpression( layer, - axes, + yAxisConfigs, datasourceLayers[layer.layerId], metadata, paletteService, @@ -331,7 +331,7 @@ export const buildExpression = ( ...validReferenceLayers.map((layer) => referenceLineLayerToExpression( layer, - axes, + yAxisConfigs, datasourceLayers[(layer as XYReferenceLineLayerConfig).layerId], datasourceExpressionsByLayers[layer.layerId] ) @@ -346,11 +346,11 @@ export const buildExpression = ( }; }; -const axesToExpression = ( - axes: YAxisConfig[], +const yAxisConfigsToExpression = ( + yAxisConfigs: YAxisConfig[], validDataLayers: ValidXYDataLayerConfig[] ): Ast[] => { - return axes.map((axis) => ({ + return yAxisConfigs.map((axis) => ({ type: 'expression', chain: [ { @@ -373,7 +373,7 @@ const axesToExpression = ( const referenceLineLayerToExpression = ( layer: XYReferenceLineLayerConfig, - axes: YAxisConfig[], + yAxisConfigs: YAxisConfig[], datasourceLayer: DatasourcePublicAPI, datasourceExpression: Ast ): Ast => { @@ -387,7 +387,7 @@ const referenceLineLayerToExpression = ( layerId: [layer.layerId], yConfig: layer.yConfig ? layer.yConfig.map((yConfig) => - extendedYConfigToExpression(yConfig, axes, defaultReferenceLineColor) + extendedYConfigToExpression(yConfig, yAxisConfigs, defaultReferenceLineColor) ) : [], accessors: layer.accessors, @@ -423,7 +423,7 @@ const annotationLayerToExpression = ( const dataLayerToExpression = ( layer: ValidXYDataLayerConfig, - axes: YAxisConfig[], + yAxisConfigs: YAxisConfig[], datasourceLayer: DatasourcePublicAPI, metadata: Record>, paletteService: PaletteRegistry, @@ -463,7 +463,7 @@ const dataLayerToExpression = ( isHorizontal: isHorizontal ? [isHorizontal] : [], splitAccessor: layer.collapseFn || !layer.splitAccessor ? [] : [layer.splitAccessor], yConfig: layer.yConfig - ? layer.yConfig.map((yConfig) => yConfigToExpression(yConfig, axes)) + ? layer.yConfig.map((yConfig) => yConfigToExpression(yConfig, yAxisConfigs)) : [], seriesType: [seriesType], accessors: layer.accessors, @@ -524,8 +524,12 @@ const dataLayerToExpression = ( }; }; -const yConfigToExpression = (yConfig: YConfig, axes: YAxisConfig[], defaultColor?: string): Ast => { - const axisId = axes.find((axis) => axis.id && axis.position === yConfig.axisMode)?.id; +const yConfigToExpression = ( + yConfig: YConfig, + yAxisConfigs: YAxisConfig[], + defaultColor?: string +): Ast => { + const axisId = yAxisConfigs.find((axis) => axis.id && axis.position === yConfig.axisMode)?.id; return { type: 'expression', chain: [ @@ -544,10 +548,10 @@ const yConfigToExpression = (yConfig: YConfig, axes: YAxisConfig[], defaultColor const extendedYConfigToExpression = ( yConfig: YConfig, - axes: YAxisConfig[], + yAxisConfigs: YAxisConfig[], defaultColor?: string ): Ast => { - const axisId = axes.find((axis) => axis.id && axis.position === yConfig.axisMode)?.id; + const axisId = yAxisConfigs.find((axis) => axis.id && axis.position === yConfig.axisMode)?.id; return { type: 'expression', chain: [ From d583c919e87aa032b206b5d65aacb557e14d0080 Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Mon, 6 Jun 2022 22:58:39 +0300 Subject: [PATCH 181/213] Fix CI --- .../__snapshots__/to_expression.test.ts.snap | 162 +++++++++--------- .../xy_visualization/to_expression.test.ts | 16 +- 2 files changed, 89 insertions(+), 89 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 fed89a99f37a0..c0b84eb09bbea 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 @@ -5,87 +5,6 @@ Object { "chain": Array [ Object { "arguments": Object { - "axes": Array [ - Object { - "chain": Array [ - Object { - "arguments": Object { - "extent": Array [], - "id": Array [], - "labelsOrientation": Array [ - -90, - ], - "position": Array [ - "left", - ], - "showGridLines": Array [ - true, - ], - "showLabels": Array [ - true, - ], - "showTitle": Array [ - true, - ], - "title": Array [], - }, - "function": "yAxisConfig", - "type": "function", - }, - ], - "type": "expression", - }, - Object { - "chain": Array [ - Object { - "arguments": Object { - "extent": Array [ - Object { - "chain": Array [ - Object { - "arguments": Object { - "lowerBound": Array [ - 123, - ], - "mode": Array [ - "custom", - ], - "upperBound": Array [ - 456, - ], - }, - "function": "axisExtentConfig", - "type": "function", - }, - ], - "type": "expression", - }, - ], - "id": Array [], - "labelsOrientation": Array [ - -45, - ], - "position": Array [ - "right", - ], - "showGridLines": Array [ - true, - ], - "showLabels": Array [ - true, - ], - "showTitle": Array [ - true, - ], - "title": Array [], - }, - "function": "yAxisConfig", - "type": "function", - }, - ], - "type": "expression", - }, - ], "curveType": Array [ "LINEAR", ], @@ -240,6 +159,87 @@ Object { "type": "expression", }, ], + "yAxisConfigs": Array [ + Object { + "chain": Array [ + Object { + "arguments": Object { + "extent": Array [], + "id": Array [], + "labelsOrientation": Array [ + -90, + ], + "position": Array [ + "left", + ], + "showGridLines": Array [ + true, + ], + "showLabels": Array [ + true, + ], + "showTitle": Array [ + true, + ], + "title": Array [], + }, + "function": "yAxisConfig", + "type": "function", + }, + ], + "type": "expression", + }, + Object { + "chain": Array [ + Object { + "arguments": Object { + "extent": Array [ + Object { + "chain": Array [ + Object { + "arguments": Object { + "lowerBound": Array [ + 123, + ], + "mode": Array [ + "custom", + ], + "upperBound": Array [ + 456, + ], + }, + "function": "axisExtentConfig", + "type": "function", + }, + ], + "type": "expression", + }, + ], + "id": Array [], + "labelsOrientation": Array [ + -45, + ], + "position": Array [ + "right", + ], + "showGridLines": Array [ + true, + ], + "showLabels": Array [ + true, + ], + "showTitle": Array [ + true, + ], + "title": Array [], + }, + "function": "yAxisConfig", + "type": "function", + }, + ], + "type": "expression", + }, + ], }, "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 332132f2f4c16..6376c5f30eaf2 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 @@ -156,13 +156,13 @@ describe('#toExpression', () => { undefined, datasourceExpressionsByLayers ) as Ast; - expect((expression.chain[0].arguments.axes[0] as Ast).chain[0].arguments).toEqual( + expect((expression.chain[0].arguments.yAxisConfigs[0] as Ast).chain[0].arguments).toEqual( expect.objectContaining({ showTitle: [true], position: ['left'], }) ); - expect((expression.chain[0].arguments.axes[1] as Ast).chain[0].arguments).toEqual( + expect((expression.chain[0].arguments.yAxisConfigs[1] as Ast).chain[0].arguments).toEqual( expect.objectContaining({ showTitle: [true], position: ['right'], @@ -283,13 +283,13 @@ describe('#toExpression', () => { undefined, datasourceExpressionsByLayers ) as Ast; - expect((expression.chain[0].arguments.axes[0] as Ast).chain[0].arguments).toEqual( + expect((expression.chain[0].arguments.yAxisConfigs[0] as Ast).chain[0].arguments).toEqual( expect.objectContaining({ showLabels: [true], position: ['left'], }) ); - expect((expression.chain[0].arguments.axes[1] as Ast).chain[0].arguments).toEqual( + expect((expression.chain[0].arguments.yAxisConfigs[1] as Ast).chain[0].arguments).toEqual( expect.objectContaining({ showLabels: [true], position: ['right'], @@ -323,13 +323,13 @@ describe('#toExpression', () => { undefined, datasourceExpressionsByLayers ) as Ast; - expect((expression.chain[0].arguments.axes[0] as Ast).chain[0].arguments).toEqual( + expect((expression.chain[0].arguments.yAxisConfigs[0] as Ast).chain[0].arguments).toEqual( expect.objectContaining({ labelsOrientation: [0], position: ['left'], }) ); - expect((expression.chain[0].arguments.axes[1] as Ast).chain[0].arguments).toEqual( + expect((expression.chain[0].arguments.yAxisConfigs[1] as Ast).chain[0].arguments).toEqual( expect.objectContaining({ labelsOrientation: [0], position: ['right'], @@ -363,13 +363,13 @@ describe('#toExpression', () => { undefined, datasourceExpressionsByLayers ) as Ast; - expect((expression.chain[0].arguments.axes[0] as Ast).chain[0].arguments).toEqual( + expect((expression.chain[0].arguments.yAxisConfigs[0] as Ast).chain[0].arguments).toEqual( expect.objectContaining({ showGridLines: [true], position: ['left'], }) ); - expect((expression.chain[0].arguments.axes[1] as Ast).chain[0].arguments).toEqual( + expect((expression.chain[0].arguments.yAxisConfigs[1] as Ast).chain[0].arguments).toEqual( expect.objectContaining({ showGridLines: [true], position: ['right'], From 2d9bd8c6d973817d01d1524722be4cae3b40b587 Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Wed, 8 Jun 2022 16:43:50 +0300 Subject: [PATCH 182/213] Rename yConfig to decorations --- .../expression_xy/common/constants.ts | 6 +- .../expression_functions/common_axis_args.ts | 7 -- .../common_data_layer_args.ts | 8 +- .../common_y_config_args.ts | 6 +- .../data_decoration_config.ts | 37 +++++++++ .../extended_y_axis_config.ts | 80 ------------------- .../common/expression_functions/index.ts | 4 +- .../reference_line.test.ts | 20 ++--- .../expression_functions/reference_line.ts | 18 ++--- ...ts => reference_line_decoration_config.ts} | 32 ++++---- .../reference_line_layer.ts | 8 +- .../common/expression_functions/validate.ts | 6 +- .../expression_functions/x_axis_config.ts | 6 ++ .../common/expression_functions/xy_vis_fn.ts | 4 +- .../expression_functions/y_axis_config.ts | 6 ++ .../common/expression_functions/y_config.ts | 37 --------- .../expression_xy/common/i18n/index.tsx | 22 +++-- .../expression_xy/common/index.ts | 6 +- .../common/types/expression_functions.ts | 48 ++++++----- .../expression_xy/public/__mocks__/index.tsx | 8 +- .../reference_lines/reference_line.tsx | 10 +-- .../reference_lines/reference_line_layer.tsx | 32 ++++---- .../reference_lines/reference_lines.test.tsx | 52 ++++++------ .../reference_lines/reference_lines.tsx | 4 +- .../components/reference_lines/utils.tsx | 21 ++--- .../public/components/xy_chart.test.tsx | 28 +++---- .../public/components/xy_chart.tsx | 30 ++++--- .../public/helpers/annotations.tsx | 23 ++++-- .../public/helpers/axes_configuration.test.ts | 6 +- .../public/helpers/axes_configuration.ts | 33 ++++---- .../expression_xy/public/helpers/state.ts | 14 +++- .../public/helpers/visualization.ts | 13 +-- .../expression_xy/public/plugin.ts | 8 +- .../expression_xy/server/plugin.ts | 8 +- .../__snapshots__/to_expression.test.ts.snap | 10 ++- .../xy_visualization/to_expression.test.ts | 2 +- .../public/xy_visualization/to_expression.ts | 60 +++++++------- .../lens/public/xy_visualization/types.ts | 5 ++ 38 files changed, 353 insertions(+), 375 deletions(-) create mode 100644 src/plugins/chart_expressions/expression_xy/common/expression_functions/data_decoration_config.ts delete mode 100644 src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_y_axis_config.ts rename src/plugins/chart_expressions/expression_xy/common/expression_functions/{extended_y_config.ts => reference_line_decoration_config.ts} (67%) delete mode 100644 src/plugins/chart_expressions/expression_xy/common/expression_functions/y_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 863deb4c5324c..5d9d7fb70d478 100644 --- a/src/plugins/chart_expressions/expression_xy/common/constants.ts +++ b/src/plugins/chart_expressions/expression_xy/common/constants.ts @@ -8,11 +8,11 @@ export const XY_VIS = 'xyVis'; export const LAYERED_XY_VIS = 'layeredXyVis'; -export const Y_CONFIG = 'yConfig'; +export const DATA_DECORATION_CONFIG = 'dataDecorationConfig'; +export const REFERENCE_LINE_DECORATION_CONFIG = 'referenceLineDecorationConfig'; +export const EXTENDED_REFERENCE_LINE_DECORATION_CONFIG = 'extendedReferenceLineDecorationConfig'; export const X_AXIS_CONFIG = 'xAxisConfig'; export const Y_AXIS_CONFIG = 'yAxisConfig'; -export const REFERENCE_LINE_Y_CONFIG = 'referenceLineYConfig'; -export const EXTENDED_Y_CONFIG = 'extendedYConfig'; export const DATA_LAYER = 'dataLayer'; export const EXTENDED_DATA_LAYER = 'extendedDataLayer'; export const LEGEND_CONFIG = 'legendConfig'; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_axis_args.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_axis_args.ts index 5cf1349a64b32..292279af5fa3f 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_axis_args.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_axis_args.ts @@ -6,7 +6,6 @@ * Side Public License, v 1. */ -import { Position } from '@elastic/charts'; import { strings } from '../i18n'; import { XAxisConfigFn, YAxisConfigFn } from '../types'; @@ -24,12 +23,6 @@ export const commonAxisConfigArgs: Omit< types: ['string'], help: strings.getAxisIdHelp(), }, - position: { - types: ['string'], - options: [Position.Top, Position.Right, Position.Bottom, Position.Left], - help: strings.getAxisPositionHelp(), - strict: true, - }, hide: { types: ['boolean'], help: strings.getAxisHideHelp(), 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 64343324518c7..330e31327873a 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 @@ -7,7 +7,7 @@ */ import { ArgumentType } from '@kbn/expressions-plugin/common'; -import { SeriesTypes, XScaleTypes, Y_CONFIG } from '../constants'; +import { SeriesTypes, XScaleTypes, DATA_DECORATION_CONFIG } from '../constants'; import { strings } from '../i18n'; import { DataLayerArgs, ExtendedDataLayerArgs } from '../types'; @@ -74,9 +74,9 @@ export const commonDataLayerArgs: Omit< types: ['boolean'], help: strings.getShowLinesHelp(), }, - yConfig: { - types: [Y_CONFIG], - help: strings.getYConfigHelp(), + decorations: { + types: [DATA_DECORATION_CONFIG], + help: strings.getDecorationsHelp(), multi: true, }, columnToLabel: { 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 2304ccd76446b..dd6a1ae2f113a 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 @@ -7,11 +7,11 @@ */ import { strings } from '../i18n'; -import { YConfigFn, ExtendedYConfigFn } from '../types'; +import { DataDecorationConfigFn, ReferenceLineDecorationConfigFn } from '../types'; -type CommonYConfigFn = YConfigFn | ExtendedYConfigFn; +type CommonDecorationConfigFn = DataDecorationConfigFn | ReferenceLineDecorationConfigFn; -export const commonYConfigArgs: CommonYConfigFn['args'] = { +export const commonDecorationConfigArgs: CommonDecorationConfigFn['args'] = { forAccessor: { types: ['string'], help: strings.getForAccessorHelp(), diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_decoration_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_decoration_config.ts new file mode 100644 index 0000000000000..12eb8e578cc1f --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/data_decoration_config.ts @@ -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 { i18n } from '@kbn/i18n'; +import type { ExpressionFunctionDefinition } from '@kbn/expressions-plugin/common'; +import { DATA_DECORATION_CONFIG } from '../constants'; +import { DataDecorationConfig, DataDecorationConfigResult } from '../types'; +import { commonDecorationConfigArgs } from './common_y_config_args'; + +export const dataDecorationConfigFunction: ExpressionFunctionDefinition< + typeof DATA_DECORATION_CONFIG, + null, + DataDecorationConfig, + DataDecorationConfigResult +> = { + name: DATA_DECORATION_CONFIG, + aliases: [], + type: DATA_DECORATION_CONFIG, + help: i18n.translate('expressionXY.dataDecorationConfig.help', { + defaultMessage: `Configure the decoration of data`, + }), + inputTypes: ['null'], + args: { + ...commonDecorationConfigArgs, + }, + fn(input, args) { + return { + type: DATA_DECORATION_CONFIG, + ...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 deleted file mode 100644 index 606cdd84ac710..0000000000000 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_y_axis_config.ts +++ /dev/null @@ -1,80 +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 { - AvailableReferenceLineIcons, - EXTENDED_Y_CONFIG, - FillStyles, - IconPositions, - LineStyles, -} from '../constants'; -import { strings } from '../i18n'; -import { ExtendedYConfigFn } from '../types'; -import { commonYConfigArgs } from './common_y_config_args'; - -export const extendedYAxisConfigFunction: ExtendedYConfigFn = { - name: EXTENDED_Y_CONFIG, - aliases: [], - type: EXTENDED_Y_CONFIG, - help: strings.getYConfigFnHelp(), - inputTypes: ['null'], - args: { - ...commonYConfigArgs, - 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', - }), - options: [...Object.values(AvailableReferenceLineIcons)], - strict: true, - }, - 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 f0172d13563db..f58f59ab7b1a0 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 @@ -11,10 +11,10 @@ export * from './layered_xy_vis'; export * from './legend_config'; export * from './annotation_layer'; export * from './extended_annotation_layer'; -export * from './y_config'; +export * from './data_decoration_config'; export * from './y_axis_config'; export * from './x_axis_config'; -export * from './extended_y_config'; +export * from './reference_line_decoration_config'; export * from './extended_data_layer'; export * from './axis_extent_config'; export * from './reference_line'; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line.test.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line.test.ts index 663c20f308b02..7e3230d075fcf 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line.test.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line.test.ts @@ -24,9 +24,9 @@ describe('referenceLine', () => { type: 'referenceLine', layerType: 'referenceLine', lineLength: 0, - yConfig: [ + decorations: [ { - type: 'referenceLineYConfig', + type: 'extendedReferenceLineDecorationConfig', ...args, textVisibility: false, }, @@ -55,9 +55,9 @@ describe('referenceLine', () => { type: 'referenceLine', layerType: 'referenceLine', lineLength: 0, - yConfig: [ + decorations: [ { - type: 'referenceLineYConfig', + type: 'extendedReferenceLineDecorationConfig', ...args, }, ], @@ -79,9 +79,9 @@ describe('referenceLine', () => { type: 'referenceLine', layerType: 'referenceLine', lineLength: 0, - yConfig: [ + decorations: [ { - type: 'referenceLineYConfig', + type: 'extendedReferenceLineDecorationConfig', ...args, textVisibility: true, }, @@ -104,9 +104,9 @@ describe('referenceLine', () => { type: 'referenceLine', layerType: 'referenceLine', lineLength: 0, - yConfig: [ + decorations: [ { - type: 'referenceLineYConfig', + type: 'extendedReferenceLineDecorationConfig', ...args, textVisibility: false, }, @@ -131,9 +131,9 @@ describe('referenceLine', () => { type: 'referenceLine', layerType: 'referenceLine', lineLength: 0, - yConfig: [ + decorations: [ { - type: 'referenceLineYConfig', + type: 'extendedReferenceLineDecorationConfig', ...args, textVisibility, }, diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line.ts index 5835bf013147c..d7efd8d58cdc8 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line.ts @@ -15,7 +15,7 @@ import { LayerTypes, LineStyles, REFERENCE_LINE, - REFERENCE_LINE_Y_CONFIG, + EXTENDED_REFERENCE_LINE_DECORATION_CONFIG, } from '../constants'; import { ReferenceLineFn } from '../types'; import { strings } from '../i18n'; @@ -38,7 +38,7 @@ export const referenceLineFunction: ReferenceLineFn = { }, position: { types: ['string'], - options: [Position.Top, Position.Right, Position.Bottom, Position.Left], + options: [Position.Right, Position.Left], help: i18n.translate('expressionXY.referenceLine.position.help', { defaultMessage: 'Position of axis (first axis of that position) to which the reference line belongs.', @@ -60,7 +60,7 @@ export const referenceLineFunction: ReferenceLineFn = { lineStyle: { types: ['string'], options: [...Object.values(LineStyles)], - help: i18n.translate('expressionXY.yConfig.lineStyle.help', { + help: i18n.translate('expressionXY.decorationConfig.lineStyle.help', { defaultMessage: 'The style of the reference line', }), default: LineStyles.SOLID, @@ -68,14 +68,14 @@ export const referenceLineFunction: ReferenceLineFn = { }, lineWidth: { types: ['number'], - help: i18n.translate('expressionXY.yConfig.lineWidth.help', { + help: i18n.translate('expressionXY.decorationConfig.lineWidth.help', { defaultMessage: 'The width of the reference line', }), default: 1, }, icon: { types: ['string'], - help: i18n.translate('expressionXY.yConfig.icon.help', { + help: i18n.translate('expressionXY.decorationConfig.icon.help', { defaultMessage: 'An optional icon used for reference lines', }), options: [...Object.values(AvailableReferenceLineIcons)], @@ -84,7 +84,7 @@ export const referenceLineFunction: ReferenceLineFn = { iconPosition: { types: ['string'], options: [...Object.values(IconPositions)], - help: i18n.translate('expressionXY.yConfig.iconPosition.help', { + help: i18n.translate('expressionXY.decorationConfig.iconPosition.help', { defaultMessage: 'The placement of the icon for the reference line', }), default: IconPositions.AUTO, @@ -92,14 +92,14 @@ export const referenceLineFunction: ReferenceLineFn = { }, textVisibility: { types: ['boolean'], - help: i18n.translate('expressionXY.yConfig.textVisibility.help', { + help: i18n.translate('expressionXY.decorationConfig.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', { + help: i18n.translate('expressionXY.decorationConfig.fill.help', { defaultMessage: 'Fill', }), default: FillStyles.NONE, @@ -118,7 +118,7 @@ export const referenceLineFunction: ReferenceLineFn = { type: REFERENCE_LINE, layerType: LayerTypes.REFERENCELINE, lineLength: table?.rows.length ?? 0, - yConfig: [{ ...args, textVisibility, type: REFERENCE_LINE_Y_CONFIG }], + decorations: [{ ...args, textVisibility, type: EXTENDED_REFERENCE_LINE_DECORATION_CONFIG }], }; }, }; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_y_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line_decoration_config.ts similarity index 67% rename from src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_y_config.ts rename to src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line_decoration_config.ts index a0b3d48df1c21..36a00ea768476 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_y_config.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line_decoration_config.ts @@ -10,26 +10,26 @@ import { Position } from '@elastic/charts'; import { i18n } from '@kbn/i18n'; import { AvailableReferenceLineIcons, - EXTENDED_Y_CONFIG, + REFERENCE_LINE_DECORATION_CONFIG, FillStyles, IconPositions, LineStyles, } from '../constants'; import { strings } from '../i18n'; -import { ExtendedYConfigFn } from '../types'; -import { commonYConfigArgs } from './common_y_config_args'; +import { ReferenceLineDecorationConfigFn } from '../types'; +import { commonDecorationConfigArgs } from './common_y_config_args'; -export const extendedYConfigFunction: ExtendedYConfigFn = { - name: EXTENDED_Y_CONFIG, +export const referenceLineDecorationConfigFunction: ReferenceLineDecorationConfigFn = { + name: REFERENCE_LINE_DECORATION_CONFIG, aliases: [], - type: EXTENDED_Y_CONFIG, - help: strings.getYConfigFnHelp(), + type: REFERENCE_LINE_DECORATION_CONFIG, + help: strings.getDecorationsHelp(), inputTypes: ['null'], args: { - ...commonYConfigArgs, + ...commonDecorationConfigArgs, position: { types: ['string'], - options: [Position.Top, Position.Right, Position.Bottom, Position.Left], + options: [Position.Right, Position.Left], help: i18n.translate('expressionXY.referenceLine.position.help', { defaultMessage: 'Position of axis (first axis of that position) to which the reference line belongs.', @@ -40,20 +40,20 @@ export const extendedYConfigFunction: ExtendedYConfigFn = { lineStyle: { types: ['string'], options: [...Object.values(LineStyles)], - help: i18n.translate('expressionXY.yConfig.lineStyle.help', { + help: i18n.translate('expressionXY.decorationConfig.lineStyle.help', { defaultMessage: 'The style of the reference line', }), strict: true, }, lineWidth: { types: ['number'], - help: i18n.translate('expressionXY.yConfig.lineWidth.help', { + help: i18n.translate('expressionXY.decorationConfig.lineWidth.help', { defaultMessage: 'The width of the reference line', }), }, icon: { types: ['string'], - help: i18n.translate('expressionXY.yConfig.icon.help', { + help: i18n.translate('expressionXY.decorationConfig.icon.help', { defaultMessage: 'An optional icon used for reference lines', }), options: [...Object.values(AvailableReferenceLineIcons)], @@ -62,21 +62,21 @@ export const extendedYConfigFunction: ExtendedYConfigFn = { iconPosition: { types: ['string'], options: [...Object.values(IconPositions)], - help: i18n.translate('expressionXY.yConfig.iconPosition.help', { + help: i18n.translate('expressionXY.decorationConfig.iconPosition.help', { defaultMessage: 'The placement of the icon for the reference line', }), strict: true, }, textVisibility: { types: ['boolean'], - help: i18n.translate('expressionXY.yConfig.textVisibility.help', { + help: i18n.translate('expressionXY.decorationConfig.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', { + help: i18n.translate('expressionXY.decorationConfig.fill.help', { defaultMessage: 'Fill', }), strict: true, @@ -84,7 +84,7 @@ export const extendedYConfigFunction: ExtendedYConfigFn = { }, fn(input, args) { return { - type: EXTENDED_Y_CONFIG, + type: REFERENCE_LINE_DECORATION_CONFIG, ...args, }; }, 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 234001015d73a..d797acc647f0d 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,7 +6,7 @@ * Side Public License, v 1. */ -import { REFERENCE_LINE_LAYER, EXTENDED_Y_CONFIG } from '../constants'; +import { REFERENCE_LINE_LAYER, REFERENCE_LINE_DECORATION_CONFIG } from '../constants'; import { ReferenceLineLayerFn } from '../types'; import { strings } from '../i18n'; @@ -22,9 +22,9 @@ export const referenceLineLayerFunction: ReferenceLineLayerFn = { help: strings.getRLAccessorsHelp(), multi: true, }, - yConfig: { - types: [EXTENDED_Y_CONFIG], - help: strings.getRLYConfigHelp(), + decorations: { + types: [REFERENCE_LINE_DECORATION_CONFIG], + help: strings.getRLDecorationConfigHelp(), multi: true, }, columnToLabel: { diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/validate.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/validate.ts index 1738137478303..c8f3e9051a581 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/validate.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/validate.ts @@ -107,7 +107,7 @@ export const errors = { axisIsNotAssignedError: (axisId: string) => i18n.translate('expressionXY.reusable.function.xyVis.errors.axisIsNotAssignedError', { defaultMessage: - 'Axis with id: "{axisId}" is not assigned to any accessor. Please assign axis using the following construction: `yConfig=\\{yConfig forAccessor="your-accessor" axisId="{axisId}"\\}`', + 'Axis with id: "{axisId}" is not assigned to any accessor. Please assign axis using the following construction: `decorations=\\{dataDecorationConfig forAccessor="your-accessor" axisId="{axisId}"\\}`', values: { axisId }, }), }; @@ -172,7 +172,9 @@ export const validateAxes = ( if ( axis.id && dataLayers.every( - (layer) => !layer.yConfig || layer.yConfig?.every((config) => config.axisId !== axis.id) + (layer) => + !layer.decorations || + layer.decorations?.every((decorationConfig) => decorationConfig.axisId !== axis.id) ) ) { throw new Error(errors.axisIsNotAssignedError(axis.id)); diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/x_axis_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/x_axis_config.ts index 54ac25294c766..cf7309cfa56bf 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/x_axis_config.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/x_axis_config.ts @@ -20,6 +20,12 @@ export const xAxisConfigFunction: XAxisConfigFn = { inputTypes: ['null'], args: { ...commonAxisConfigArgs, + position: { + types: ['string'], + options: [Position.Top, Position.Bottom], + help: strings.getAxisPositionHelp(), + strict: true, + }, }, fn(input, args) { return { 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 40ce134371544..5d56da5e92bf0 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 @@ -50,7 +50,7 @@ const createDataLayer = (args: XYArgs, table: Datatable): DataLayerConfigResult isHorizontal: args.isHorizontal, isStacked: args.isStacked, palette: args.palette, - yConfig: args.yConfig, + decorations: args.decorations, showPoints: args.showPoints, pointsRadius: args.pointsRadius, lineWidth: args.lineWidth, @@ -80,7 +80,7 @@ export const xyVisFn: XyVisFn['fn'] = async (data, args, handlers) => { isHorizontal, isPercentage, isStacked, - yConfig, + decorations, palette, markSizeAccessor, showPoints, 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 58c1ec8cbb5c9..6088ed4e3c86e 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 @@ -39,6 +39,12 @@ export const yAxisConfigFunction: YAxisConfigFn = { help: strings.getAxisScaleTypeHelp(), default: YScaleTypes.LINEAR, }, + position: { + types: ['string'], + options: [Position.Right, Position.Left], + help: strings.getAxisPositionHelp(), + strict: true, + }, }, fn(input, args) { return { diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/y_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/y_config.ts deleted file mode 100644 index 0e92f8f73704d..0000000000000 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/y_config.ts +++ /dev/null @@ -1,37 +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 '@kbn/expressions-plugin/common'; -import { Y_CONFIG } from '../constants'; -import { YConfig, YConfigResult } from '../types'; -import { commonYConfigArgs } from './common_y_config_args'; - -export const yConfigFunction: ExpressionFunctionDefinition< - typeof Y_CONFIG, - null, - YConfig, - YConfigResult -> = { - 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`, - }), - inputTypes: ['null'], - args: { - ...commonYConfigArgs, - }, - fn(input, args) { - return { - type: Y_CONFIG, - ...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 5fc8f2d554229..9d1388829de3c 100644 --- a/src/plugins/chart_expressions/expression_xy/common/i18n/index.tsx +++ b/src/plugins/chart_expressions/expression_xy/common/i18n/index.tsx @@ -185,9 +185,9 @@ export const strings = { i18n.translate('expressionXY.dataLayer.showLines.help', { defaultMessage: 'Show lines between points', }), - getYConfigHelp: () => - i18n.translate('expressionXY.dataLayer.yConfig.help', { - defaultMessage: 'Additional configuration for y axes', + getDecorationsHelp: () => + i18n.translate('expressionXY.dataLayer.decorations.help', { + defaultMessage: 'Additional decoration for data', }), getColumnToLabelHelp: () => i18n.translate('expressionXY.layer.columnToLabel.help', { @@ -209,28 +209,24 @@ export const strings = { 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', + getRLDecorationConfigHelp: () => + i18n.translate('expressionXY.referenceLineLayer.decorationConfig.help', { + defaultMessage: 'Additional decoration for reference line', }), 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', { + i18n.translate('expressionXY.decorationConfig.forAccessor.help', { defaultMessage: 'The accessor this configuration is for', }), getColorHelp: () => - i18n.translate('expressionXY.yConfig.color.help', { + i18n.translate('expressionXY.decorationConfig.color.help', { defaultMessage: 'The color of the series', }), getAxisIdHelp: () => - i18n.translate('expressionXY.yConfig.axisId.help', { + i18n.translate('expressionXY.decorationConfig.axisId.help', { defaultMessage: 'Id of axis', }), getAnnotationLayerFnHelp: () => diff --git a/src/plugins/chart_expressions/expression_xy/common/index.ts b/src/plugins/chart_expressions/expression_xy/common/index.ts index 588e4182f71c6..d9c415f488839 100755 --- a/src/plugins/chart_expressions/expression_xy/common/index.ts +++ b/src/plugins/chart_expressions/expression_xy/common/index.ts @@ -11,7 +11,6 @@ export const PLUGIN_NAME = 'expressionXy'; export type { XYArgs, - YConfig, EndValue, XYRender, LayerType, @@ -34,7 +33,6 @@ export type { AxisExtentMode, DataLayerConfig, FittingFunction, - ExtendedYConfig, AxisExtentConfig, CollectiveConfig, LegendConfigResult, @@ -42,8 +40,8 @@ export type { XAxisConfigResult, YAxisConfigResult, CommonXYLayerConfig, + DataDecorationConfig, AnnotationLayerArgs, - ExtendedYConfigResult, DataLayerConfigResult, AxisExtentConfigResult, ReferenceLineLayerArgs, @@ -55,6 +53,8 @@ export type { ExtendedDataLayerConfigResult, CommonXYDataLayerConfigResult, ReferenceLineLayerConfigResult, + ReferenceLineDecorationConfig, CommonXYReferenceLineLayerConfig, + ReferenceLineDecorationConfigResult, 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 c207abd47e4e3..2528eaa8a9c03 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 @@ -28,7 +28,8 @@ import { YScaleTypes, AxisModes, REFERENCE_LINE, - Y_CONFIG, + DATA_DECORATION_CONFIG, + REFERENCE_LINE_DECORATION_CONFIG, LEGEND_CONFIG, DATA_LAYER, AXIS_EXTENT_CONFIG, @@ -38,12 +39,11 @@ import { EndValues, X_AXIS_CONFIG, Y_AXIS_CONFIG, - EXTENDED_Y_CONFIG, AvailableReferenceLineIcons, XY_VIS, LAYERED_XY_VIS, EXTENDED_ANNOTATION_LAYER, - REFERENCE_LINE_Y_CONFIG, + EXTENDED_REFERENCE_LINE_DECORATION_CONFIG, } from '../constants'; import { XYRender } from './expression_renderers'; @@ -95,7 +95,7 @@ export interface YAxisConfig extends AxisConfig { scaleType?: YScaleType; } -export interface ExtendedYConfig extends YConfig { +export interface ReferenceLineDecorationConfig extends DataDecorationConfig { icon?: AvailableReferenceLineIcon; lineWidth?: number; lineStyle?: LineStyle; @@ -105,7 +105,7 @@ export interface ExtendedYConfig extends YConfig { position?: Position; } -export interface YConfig { +export interface DataDecorationConfig { forAccessor: string; color?: string; axisId?: string; @@ -129,7 +129,7 @@ export interface DataLayerArgs { isStacked: boolean; isHorizontal: boolean; palette: PaletteOutput; - yConfig?: YConfigResult[]; + decorations?: DataDecorationConfigResult[]; } export interface ValidLayer extends DataLayerConfigResult { @@ -156,7 +156,7 @@ export interface ExtendedDataLayerArgs { isHorizontal: boolean; palette: PaletteOutput; // palette will always be set on the expression - yConfig?: YConfigResult[]; + decorations?: DataDecorationConfigResult[]; table?: Datatable; } @@ -297,7 +297,8 @@ export type ExtendedAnnotationLayerConfigResult = ExtendedAnnotationLayerArgs & layerType: typeof LayerTypes.ANNOTATIONS; }; -export interface ReferenceLineArgs extends Omit { +export interface ReferenceLineArgs + extends Omit { name?: string; value: number; fill: FillStyle; @@ -307,7 +308,7 @@ export interface ReferenceLineLayerArgs { layerId?: string; accessors: string[]; columnToLabel?: string; - yConfig?: ExtendedYConfigResult[]; + decorations?: ReferenceLineDecorationConfigResult[]; table?: Datatable; } @@ -323,15 +324,15 @@ export type XYExtendedLayerConfigResult = | ReferenceLineLayerConfigResult | ExtendedAnnotationLayerConfigResult; -export interface ReferenceLineYConfig extends ReferenceLineArgs { - type: typeof REFERENCE_LINE_Y_CONFIG; +export interface ExtendedReferenceLineDecorationConfig extends ReferenceLineArgs { + type: typeof EXTENDED_REFERENCE_LINE_DECORATION_CONFIG; } export interface ReferenceLineConfigResult { type: typeof REFERENCE_LINE; layerType: typeof LayerTypes.REFERENCELINE; lineLength: number; - yConfig: [ReferenceLineYConfig]; + decorations: [ExtendedReferenceLineDecorationConfig]; } export type ReferenceLineLayerConfigResult = ReferenceLineLayerArgs & { @@ -366,8 +367,12 @@ export type ExtendedDataLayerConfigResult = Omit >; -export type YConfigFn = ExpressionFunctionDefinition; -export type ExtendedYConfigFn = ExpressionFunctionDefinition< - typeof EXTENDED_Y_CONFIG, +export type DataDecorationConfigFn = ExpressionFunctionDefinition< + typeof DATA_DECORATION_CONFIG, + null, + DataDecorationConfig, + DataDecorationConfigResult +>; +export type ReferenceLineDecorationConfigFn = ExpressionFunctionDefinition< + typeof REFERENCE_LINE_DECORATION_CONFIG, null, - ExtendedYConfig, - ExtendedYConfigResult + ReferenceLineDecorationConfig, + ReferenceLineDecorationConfigResult >; export type LegendConfigFn = ExpressionFunctionDefinition< 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 9a084b45ca328..a7c6494a33542 100644 --- a/src/plugins/chart_expressions/expression_xy/public/__mocks__/index.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/__mocks__/index.tsx @@ -201,8 +201,12 @@ export function sampleArgsWithReferenceLine(value: number = 150) { type: 'referenceLineLayer', layerType: LayerTypes.REFERENCELINE, accessors: ['referenceLine-a'], - yConfig: [ - { forAccessor: 'referenceLine-a', type: 'extendedYConfig', position: Position.Left }, + decorations: [ + { + forAccessor: 'referenceLine-a', + type: 'referenceLineDecorationConfig', + position: Position.Left, + }, ], table: data, }, diff --git a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_line.tsx b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_line.tsx index 44efd1b89ff8f..6611f775ef48e 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_line.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_line.tsx @@ -32,16 +32,16 @@ export const ReferenceLine: FC = ({ nextValue, }) => { const { - yConfig: [yConfig], + decorations: [decorationConfig], } = layer; - if (!yConfig) { + if (!decorationConfig) { return null; } - const { value } = yConfig; + const { value } = decorationConfig; - const axisGroup = getAxisGroupForReferenceLine(yAxesConfiguration, yConfig); + const axisGroup = getAxisGroupForReferenceLine(yAxesConfiguration, decorationConfig); const formatter = axisGroup?.formatter || xAxisFormatter; const id = `${layer.layerId}-${value}`; @@ -53,7 +53,7 @@ export const ReferenceLine: FC = ({ return ( = ({ isHorizontal, titles, }) => { - if (!layer.yConfig) { + if (!layer.decorations) { return null; } - const { columnToLabel, yConfig: yConfigs, table } = layer; + const { columnToLabel, decorations, table } = layer; const columnToLabelMap: Record = columnToLabel ? JSON.parse(columnToLabel) : {}; const row = table.rows[0]; - const yConfigByValue = yConfigs.sort( + const decorationConfigsByValue = decorations.sort( ({ forAccessor: idA }, { forAccessor: idB }) => row[idA] - row[idB] ); - const groupedByDirection = groupBy(yConfigByValue, 'fill'); + const groupedByDirection = groupBy(decorationConfigsByValue, 'fill'); if (groupedByDirection.below) { groupedByDirection.below.reverse(); } - const referenceLineElements = yConfigByValue.flatMap((yConfig) => { - const axisGroup = getAxisGroupForReferenceLine(yAxesConfiguration, yConfig); + const referenceLineElements = decorationConfigsByValue.flatMap((decorationConfig) => { + const axisGroup = getAxisGroupForReferenceLine(yAxesConfiguration, decorationConfig); const formatter = axisGroup?.formatter || xAxisFormatter; - const name = columnToLabelMap[yConfig.forAccessor] ?? titles?.yTitles?.[yConfig.forAccessor]; - const value = row[yConfig.forAccessor]; - const yConfigsWithSameDirection = groupedByDirection[yConfig.fill!]; - const indexFromSameType = yConfigsWithSameDirection.findIndex( - ({ forAccessor }) => forAccessor === yConfig.forAccessor + const name = + columnToLabelMap[decorationConfig.forAccessor] ?? + titles?.yTitles?.[decorationConfig.forAccessor]; + const value = row[decorationConfig.forAccessor]; + const yDecorationsWithSameDirection = groupedByDirection[decorationConfig.fill!]; + const indexFromSameType = yDecorationsWithSameDirection.findIndex( + ({ forAccessor }) => forAccessor === decorationConfig.forAccessor ); - const shouldCheckNextReferenceLine = indexFromSameType < yConfigsWithSameDirection.length - 1; + const shouldCheckNextReferenceLine = indexFromSameType < yDecorationsWithSameDirection.length - 1; const nextValue = shouldCheckNextReferenceLine - ? row[yConfigsWithSameDirection[indexFromSameType + 1].forAccessor] + ? row[yDecorationsWithSameDirection[indexFromSameType + 1].forAccessor] : undefined; - const { forAccessor, type, ...restAnnotationConfig } = yConfig; - const id = `${layer.layerId}-${yConfig.forAccessor}`; + const { forAccessor, type, ...restAnnotationConfig } = decorationConfig; + const id = `${layer.layerId}-${decorationConfig.forAccessor}`; const axesMap = { left: yAxesConfiguration.some((axes) => axes.position === 'left'), diff --git a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_lines.test.tsx b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_lines.test.tsx index ab6d863ee15ce..d48d5720f1b15 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_lines.test.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_lines.test.tsx @@ -15,7 +15,7 @@ import { LayerTypes } from '../../../common/constants'; import { ReferenceLineLayerArgs, ReferenceLineLayerConfig, - ExtendedYConfig, + ExtendedReferenceLineDecorationConfig, ReferenceLineArgs, ReferenceLineConfig, } from '../../../common/types'; @@ -46,12 +46,14 @@ const data: Datatable = { })), }; -function createLayers(yConfigs: ReferenceLineLayerArgs['yConfig']): ReferenceLineLayerConfig[] { +function createLayers( + decorations: ReferenceLineLayerArgs['decorations'] +): ReferenceLineLayerConfig[] { return [ { layerId: 'first', - accessors: (yConfigs || []).map(({ forAccessor }) => forAccessor), - yConfig: yConfigs, + accessors: (decorations || []).map(({ forAccessor }) => forAccessor), + decorations, type: 'referenceLineLayer', layerType: LayerTypes.REFERENCELINE, table: data, @@ -69,7 +71,7 @@ function createReferenceLine( type: 'referenceLine', layerType: 'referenceLine', lineLength, - yConfig: [{ type: 'referenceLineYConfig', ...args }], + decorations: [{ type: 'extendedReferenceLineDecorationConfig', ...args }], }; } @@ -82,7 +84,7 @@ interface XCoords { x1: number | undefined; } -function getAxisFromId(layerPrefix: string): ExtendedYConfig['position'] { +function getAxisFromId(layerPrefix: string): ExtendedReferenceLineDecorationConfig['position'] { return /left/i.test(layerPrefix) ? 'left' : /right/i.test(layerPrefix) ? 'right' : 'bottom'; } @@ -122,7 +124,7 @@ describe('ReferenceLines', () => { ['yAccessorLeft', 'below'], ['yAccessorRight', 'above'], ['yAccessorRight', 'below'], - ] as Array<[string, Exclude]>)( + ] as Array<[string, Exclude]>)( 'should render a RectAnnotation for a reference line with fill set: %s %s', (layerPrefix, fill) => { const position = getAxisFromId(layerPrefix); @@ -135,7 +137,7 @@ describe('ReferenceLines', () => { position, lineStyle: 'solid', fill, - type: 'extendedYConfig', + type: 'referenceLineDecorationConfig', }, ])} /> @@ -163,7 +165,7 @@ describe('ReferenceLines', () => { it.each([ ['xAccessor', 'above'], ['xAccessor', 'below'], - ] as Array<[string, Exclude]>)( + ] as Array<[string, Exclude]>)( 'should render a RectAnnotation for a reference line with fill set: %s %s', (layerPrefix, fill) => { const wrapper = shallow( @@ -174,7 +176,7 @@ describe('ReferenceLines', () => { forAccessor: `${layerPrefix}FirstId`, position: 'bottom', lineStyle: 'solid', - type: 'extendedYConfig', + type: 'referenceLineDecorationConfig', fill, }, ])} @@ -205,7 +207,7 @@ describe('ReferenceLines', () => { ['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, Exclude, YCoords, YCoords]>)( + ] as Array<[string, Exclude, YCoords, YCoords]>)( 'should avoid overlap between two reference lines with fill in the same direction: 2 x %s %s', (layerPrefix, fill, coordsA, coordsB) => { const position = getAxisFromId(layerPrefix); @@ -217,14 +219,14 @@ describe('ReferenceLines', () => { forAccessor: `${layerPrefix}FirstId`, position, lineStyle: 'solid', - type: 'extendedYConfig', + type: 'referenceLineDecorationConfig', fill, }, { forAccessor: `${layerPrefix}SecondId`, position, lineStyle: 'solid', - type: 'extendedYConfig', + type: 'referenceLineDecorationConfig', fill, }, ])} @@ -261,7 +263,7 @@ describe('ReferenceLines', () => { 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, Exclude, XCoords, XCoords]>)( + ] as Array<[string, Exclude, 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( @@ -272,14 +274,14 @@ describe('ReferenceLines', () => { forAccessor: `${layerPrefix}FirstId`, position: 'bottom', lineStyle: 'solid', - type: 'extendedYConfig', + type: 'referenceLineDecorationConfig', fill, }, { forAccessor: `${layerPrefix}SecondId`, position: 'bottom', lineStyle: 'solid', - type: 'extendedYConfig', + type: 'referenceLineDecorationConfig', fill, }, ])} @@ -327,14 +329,14 @@ describe('ReferenceLines', () => { position, lineStyle: 'solid', fill: 'above', - type: 'extendedYConfig', + type: 'referenceLineDecorationConfig', }, { forAccessor: `${layerPrefix}SecondId`, position, lineStyle: 'solid', fill: 'below', - type: 'extendedYConfig', + type: 'referenceLineDecorationConfig', }, ])} /> @@ -370,7 +372,7 @@ describe('ReferenceLines', () => { it.each([ ['above', { y0: 5, y1: 10 }, { y0: 10, y1: undefined }], ['below', { y0: undefined, y1: 5 }, { y0: 5, y1: 10 }], - ] as Array<[Exclude, YCoords, YCoords]>)( + ] as Array<[Exclude, 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( @@ -382,14 +384,14 @@ describe('ReferenceLines', () => { position: 'left', lineStyle: 'solid', fill, - type: 'extendedYConfig', + type: 'referenceLineDecorationConfig', }, { forAccessor: `yAccessorRightSecondId`, position: 'right', lineStyle: 'solid', fill, - type: 'extendedYConfig', + type: 'referenceLineDecorationConfig', }, ])} /> @@ -456,7 +458,7 @@ describe('ReferenceLines', () => { ['yAccessorLeft', 'below'], ['yAccessorRight', 'above'], ['yAccessorRight', 'below'], - ] as Array<[string, Exclude]>)( + ] as Array<[string, Exclude]>)( 'should render a RectAnnotation for a reference line with fill set: %s %s', (layerPrefix, fill) => { const position = getAxisFromId(layerPrefix); @@ -497,7 +499,7 @@ describe('ReferenceLines', () => { it.each([ ['xAccessor', 'above'], ['xAccessor', 'below'], - ] as Array<[string, Exclude]>)( + ] as Array<[string, Exclude]>)( 'should render a RectAnnotation for a reference line with fill set: %s %s', (layerPrefix, fill) => { const value = 1; @@ -537,7 +539,7 @@ describe('ReferenceLines', () => { it.each([ ['yAccessorLeft', 'above', { y0: 10, y1: undefined }, { y0: 10, y1: undefined }], ['yAccessorLeft', 'below', { y0: undefined, y1: 5 }, { y0: undefined, y1: 5 }], - ] as Array<[string, Exclude, YCoords, YCoords]>)( + ] as Array<[string, Exclude, YCoords, YCoords]>)( 'should avoid overlap between two reference lines with fill in the same direction: 2 x %s %s', (layerPrefix, fill, coordsA, coordsB) => { const position = getAxisFromId(layerPrefix); @@ -588,7 +590,7 @@ describe('ReferenceLines', () => { it.each([ ['xAccessor', 'above', { x0: 1, x1: undefined }, { x0: 1, x1: undefined }], ['xAccessor', 'below', { x0: undefined, x1: 1 }, { x0: undefined, x1: 1 }], - ] as Array<[string, Exclude, XCoords, XCoords]>)( + ] as Array<[string, Exclude, XCoords, XCoords]>)( 'should avoid overlap between two reference lines with fill in the same direction: 2 x %s %s', (layerPrefix, fill, coordsA, coordsB) => { const value = coordsA.x0 ?? coordsA.x1!; diff --git a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_lines.tsx b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_lines.tsx index dce4eb9733e3e..e9579f8d9e861 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_lines.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_lines.tsx @@ -36,13 +36,13 @@ export const ReferenceLines = ({ layers, titles = {}, ...rest }: ReferenceLinesP return ( <> {layers.flatMap((layer) => { - if (!layer.yConfig) { + if (!layer.decorations) { return null; } const key = `referenceLine-${layer.layerId}`; if (isReferenceLine(layer)) { - const nextValue = referenceLinesNextValues[layer.yConfig[0].fill][layer.layerId]; + const nextValue = referenceLinesNextValues[layer.decorations[0].fill][layer.layerId]; return ; } diff --git a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/utils.tsx b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/utils.tsx index d36297d7b9e44..fb9a902193e9c 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/utils.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/utils.tsx @@ -15,8 +15,8 @@ import { IconPosition, ReferenceLineConfig, FillStyle, - ReferenceLineYConfig, - ExtendedYConfigResult, + ExtendedReferenceLineDecorationConfig, + ReferenceLineDecorationConfig, } from '../../../common/types'; import { FillStyles } from '../../../common/constants'; import { @@ -150,13 +150,16 @@ export const getHorizontalRect = ( const sortReferenceLinesByGroup = (referenceLines: ReferenceLineConfig[], group: FillStyle) => { if (group === FillStyles.ABOVE || group === FillStyles.BELOW) { const order = group === FillStyles.ABOVE ? 'asc' : 'desc'; - return orderBy(referenceLines, ({ yConfig: [{ value }] }) => value, [order]); + return orderBy(referenceLines, ({ decorations: [{ value }] }) => value, [order]); } return referenceLines; }; export const getNextValuesForReferenceLines = (referenceLines: ReferenceLineConfig[]) => { - const grouppedReferenceLines = groupBy(referenceLines, ({ yConfig: [yConfig] }) => yConfig.fill); + const grouppedReferenceLines = groupBy( + referenceLines, + ({ decorations: [decorationConfig] }) => decorationConfig.fill + ); const groups = Object.keys(grouppedReferenceLines) as FillStyle[]; return groups.reduce>>( @@ -167,8 +170,8 @@ export const getNextValuesForReferenceLines = (referenceLines: ReferenceLineConf (nextValues, referenceLine, index, lines) => { let nextValue: number | undefined; if (index < lines.length - 1) { - const [yConfig] = lines[index + 1].yConfig; - nextValue = yConfig.value; + const [decorationConfig] = lines[index + 1].decorations; + nextValue = decorationConfig.value; } return { ...nextValues, [referenceLine.layerId]: nextValue }; @@ -218,11 +221,11 @@ export const computeChartMargins = ( export function getAxisGroupForReferenceLine( yAxesConfiguration: GroupsConfiguration, - yConfig: ReferenceLineYConfig | ExtendedYConfigResult + decorationConfig: ReferenceLineDecorationConfig | ExtendedReferenceLineDecorationConfig ) { return yAxesConfiguration.find( (axis) => - (yConfig.axisId && axis.groupId.includes(yConfig.axisId)) || - yConfig.position === axis.position + (decorationConfig.axisId && axis.groupId.includes(decorationConfig.axisId)) || + decorationConfig.position === axis.position ); } 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 597c2e514e8ba..c8832b3654dbe 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 @@ -1646,14 +1646,14 @@ describe('XYChart component', () => { { ...layer, accessors: ['a', 'b'], - yConfig: [ + decorations: [ { - type: 'yConfig', + type: 'dataDecorationConfig', forAccessor: 'a', axisId: '1', }, { - type: 'yConfig', + type: 'dataDecorationConfig', forAccessor: 'b', axisId: '2', }, @@ -1712,14 +1712,14 @@ describe('XYChart component', () => { { ...layer, accessors: ['c', 'd'], - yConfig: [ + decorations: [ { - type: 'yConfig', + type: 'dataDecorationConfig', forAccessor: 'c', axisId: '1', }, { - type: 'yConfig', + type: 'dataDecorationConfig', forAccessor: 'd', axisId: '1', }, @@ -1755,14 +1755,14 @@ describe('XYChart component', () => { type: 'extendedDataLayer', accessors: ['a', 'b'], splitAccessor: undefined, - yConfig: [ + decorations: [ { - type: 'yConfig', + type: 'dataDecorationConfig', forAccessor: 'a', color: '#550000', }, { - type: 'yConfig', + type: 'dataDecorationConfig', forAccessor: 'b', color: '#FFFF00', }, @@ -1774,9 +1774,9 @@ describe('XYChart component', () => { type: 'extendedDataLayer', accessors: ['c'], splitAccessor: undefined, - yConfig: [ + decorations: [ { - type: 'yConfig', + type: 'dataDecorationConfig', forAccessor: 'c', color: '#FEECDF', }, @@ -1807,16 +1807,16 @@ describe('XYChart component', () => { }) ).toEqual('#FEECDF'); }); - test('color is not applied to chart when splitAccessor is defined or when yConfig is not configured', () => { + test('color is not applied to chart when splitAccessor is defined or when decorations is not configured', () => { const newArgs: XYProps = { ...args, layers: [ { ...layer, accessors: ['a'], - yConfig: [ + decorations: [ { - type: 'yConfig', + type: 'dataDecorationConfig', forAccessor: 'a', color: '#550000', }, 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 ab3b9f0efa433..222fb1ab47a09 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 @@ -47,8 +47,8 @@ import type { FilterEvent, BrushEvent, FormatFactory } from '../types'; import { isTimeChart } from '../../common/helpers'; import type { CommonXYDataLayerConfig, - ExtendedYConfigResult, - ReferenceLineYConfig, + ReferenceLineDecorationConfig, + ExtendedReferenceLineDecorationConfig, XYChartProps, } from '../../common/types'; import { @@ -60,7 +60,7 @@ import { getFormattedTablesByLayers, getLayersFormats, getLayersTitles, - isReferenceLineYConfig, + isReferenceLineDecorationConfig, getFilteredLayers, getReferenceLayers, isDataLayer, @@ -311,7 +311,9 @@ export function XYChart({ const visualConfigs = [ ...referenceLineLayers - .flatMap(({ yConfig }) => yConfig) + .flatMap( + ({ decorations }) => decorations + ) .map((config) => ({ ...config, position: config @@ -388,19 +390,21 @@ export function XYChart({ padding, includeDataFromIds: referenceLineLayers .flatMap((l) => - l.yConfig ? l.yConfig.map((yConfig) => ({ layerId: l.layerId, yConfig })) : [] + l.decorations + ? l.decorations.map((decoration) => ({ layerId: l.layerId, decoration })) + : [] ) - .filter(({ yConfig }) => { - if (yConfig.axisId) { - return axis.groupId.includes(yConfig.axisId); + .filter(({ decoration }) => { + if (decoration.axisId) { + return axis.groupId.includes(decoration.axisId); } - return axis.position === yConfig.position; + return axis.position === decoration.position; }) - .map(({ layerId, yConfig }) => - isReferenceLineYConfig(yConfig) - ? `${layerId}-${yConfig.value}-${yConfig.fill !== 'none' ? 'rect' : 'line'}` - : `${layerId}-${yConfig.forAccessor}-${yConfig.fill !== 'none' ? 'rect' : 'line'}` + .map(({ layerId, decoration }) => + isReferenceLineDecorationConfig(decoration) + ? `${layerId}-${decoration.value}-${decoration.fill !== 'none' ? 'rect' : 'line'}` + : `${layerId}-${decoration.forAccessor}-${decoration.fill !== 'none' ? 'rect' : 'line'}` ), }; }; 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 4175d5b923d1e..d6dc9c0ce5f67 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/annotations.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/annotations.tsx @@ -9,26 +9,33 @@ import React from 'react'; import { Position } from '@elastic/charts'; import { EuiFlexGroup, EuiIcon, EuiIconProps, EuiText } from '@elastic/eui'; import classnames from 'classnames'; -import type { IconPosition, ExtendedYConfig, CollectiveConfig } from '../../common/types'; +import type { + IconPosition, + ReferenceLineDecorationConfig, + CollectiveConfig, +} from '../../common/types'; import { getBaseIconPlacement } from '../components'; import { hasIcon, iconSet } from './icon'; export const LINES_MARKER_SIZE = 20; -type PartialExtendedYConfig = Pick & { +type PartialReferenceLineDecorationConfig = Pick< + ReferenceLineDecorationConfig, + 'icon' | 'iconPosition' | 'textVisibility' +> & { position?: Position; }; type PartialCollectiveConfig = Pick; -const isExtendedYConfig = ( - config: PartialExtendedYConfig | PartialCollectiveConfig | undefined -): config is PartialExtendedYConfig => - (config as PartialExtendedYConfig)?.iconPosition ? true : false; +const isExtendedDecorationConfig = ( + config: PartialReferenceLineDecorationConfig | PartialCollectiveConfig | undefined +): config is PartialReferenceLineDecorationConfig => + (config as PartialReferenceLineDecorationConfig)?.iconPosition ? true : false; // Note: it does not take into consideration whether the reference line is in view or not export const getLinesCausedPaddings = ( - visualConfigs: Array, + visualConfigs: Array, axesMap: Record<'left' | 'right', unknown> ) => { // collect all paddings for the 4 axis: if any text is detected double it. @@ -39,7 +46,7 @@ export const getLinesCausedPaddings = ( return; } const { position, icon, textVisibility } = config; - const iconPosition = isExtendedYConfig(config) ? config.iconPosition : undefined; + const iconPosition = isExtendedDecorationConfig(config) ? config.iconPosition : undefined; if (position && (hasIcon(icon) || textVisibility)) { const placement = getBaseIconPlacement(iconPosition, axesMap, position); 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 26810055fb992..60ae601545e88 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 @@ -302,7 +302,7 @@ describe('axes_configuration', () => { [ { ...sampleLayer, - yConfig: [{ type: 'yConfig', forAccessor: 'yAccessorId', axisId: '1' }], + decorations: [{ type: 'dataDecorationConfig', forAccessor: 'yAccessorId', axisId: '1' }], }, ], false, @@ -323,7 +323,7 @@ describe('axes_configuration', () => { { ...sampleLayer, accessors: ['yAccessorId', 'yAccessorId3', 'yAccessorId4'], - yConfig: [{ type: 'yConfig', forAccessor: 'yAccessorId', axisId: '1' }], + decorations: [{ type: 'dataDecorationConfig', forAccessor: 'yAccessorId', axisId: '1' }], }, ], false, @@ -348,7 +348,7 @@ describe('axes_configuration', () => { { ...sampleLayer, accessors: ['yAccessorId', 'yAccessorId3', 'yAccessorId4'], - yConfig: [{ type: 'yConfig', forAccessor: 'yAccessorId', axisId: '1' }], + decorations: [{ type: 'dataDecorationConfig', forAccessor: 'yAccessorId', axisId: '1' }], }, ], false, 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 95aaa81ddaafe..9db6976bb37ea 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 @@ -10,17 +10,17 @@ import { Position } from '@elastic/charts'; import type { IFieldFormat, SerializedFieldFormat } from '@kbn/field-formats-plugin/common'; import { getAccessorByDimension } from '@kbn/visualizations-plugin/common/utils'; import { FormatFactory } from '../types'; -import type { ReferenceLineYConfig } from '../../common/types'; +import type { ExtendedReferenceLineDecorationConfig } from '../../common/types'; import { AxisExtentConfig, CommonXYDataLayerConfig, - ExtendedYConfig, - YConfig, + DataDecorationConfig, YAxisConfig, - ExtendedYConfigResult, + ReferenceLineDecorationConfig, + ReferenceLineDecorationConfigResult, } from '../../common'; import { LayersFieldFormats } from './layers'; -import { isReferenceLineYConfig } from './visualization'; +import { isReferenceLineDecorationConfig } from './visualization'; export interface Series { layer: string; @@ -66,11 +66,16 @@ export function groupAxesByType( layers.forEach((layer) => { const { layerId, table } = layer; layer.accessors.forEach((accessor) => { - const yConfig: Array | undefined = layer.yConfig; + const dataDecorations: + | Array + | undefined = layer.decorations; const yAccessor = getAccessorByDimension(accessor, table.columns); - const yConfigByAccessor = yConfig?.find((config) => config.forAccessor === yAccessor); + const decorationByAccessor = dataDecorations?.find( + (decorationConfig) => decorationConfig.forAccessor === yAccessor + ); const axisConfigById = yAxisConfigs?.find( - (axis) => yConfigByAccessor?.axisId && axis.id && axis.id === yConfigByAccessor?.axisId + (axis) => + decorationByAccessor?.axisId && axis.id && axis.id === decorationByAccessor?.axisId ); const key = axisConfigById?.id || 'auto'; const fieldFormat = fieldFormats[layerId].yAccessors[yAccessor]!; @@ -213,15 +218,15 @@ export function validateExtent(hasBarOrArea: boolean, extent?: AxisExtentConfig) export const getAxisGroupConfig = ( axesGroup?: GroupsConfiguration, - yConfig?: ExtendedYConfigResult | ReferenceLineYConfig + decoration?: ReferenceLineDecorationConfigResult | ExtendedReferenceLineDecorationConfig ) => { return axesGroup?.find((axis) => { - if (yConfig?.axisId) { - return axis.groupId.includes(yConfig.axisId); + if (decoration?.axisId) { + return axis.groupId.includes(decoration.axisId); } - return yConfig && isReferenceLineYConfig(yConfig) - ? yConfig.position === axis.position - : axis.series.some(({ accessor }) => accessor === yConfig?.forAccessor); + return decoration && isReferenceLineDecorationConfig(decoration) + ? decoration.position === axis.position + : axis.series.some(({ accessor }) => accessor === decoration?.forAccessor); }); }; 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 c3f555d51d57e..a705dab47dc43 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,11 @@ * Side Public License, v 1. */ -import type { CommonXYLayerConfig, ExtendedYConfig, YConfig } from '../../common'; +import type { + CommonXYLayerConfig, + ReferenceLineDecorationConfig, + DataDecorationConfig, +} from '../../common'; import { getDataLayers, isAnnotationsLayer, isDataLayer, isReferenceLine } from './visualization'; export function isHorizontalChart(layers: CommonXYLayerConfig[]) { @@ -21,6 +25,10 @@ export const getSeriesColor = (layer: CommonXYLayerConfig, accessor: string) => ) { return null; } - const yConfig: Array | undefined = layer?.yConfig; - return yConfig?.find((yConf) => yConf.forAccessor === accessor)?.color || null; + const decorations: Array | undefined = + layer?.decorations; + return ( + decorations?.find((decorationConfig) => decorationConfig.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 480fa5374238e..87d27d30badb2 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/visualization.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/visualization.ts @@ -10,7 +10,7 @@ import { LayerTypes, REFERENCE_LINE, REFERENCE_LINE_LAYER, - REFERENCE_LINE_Y_CONFIG, + EXTENDED_REFERENCE_LINE_DECORATION_CONFIG, } from '../../common/constants'; import { CommonXYLayerConfig, @@ -19,8 +19,8 @@ import { CommonXYAnnotationLayerConfig, ReferenceLineLayerConfig, ReferenceLineConfig, - ExtendedYConfigResult, - ReferenceLineYConfig, + ReferenceLineDecorationConfigResult, + ExtendedReferenceLineDecorationConfig, } from '../../common/types'; export const isDataLayer = (layer: CommonXYLayerConfig): layer is CommonXYDataLayerConfig => @@ -35,9 +35,10 @@ export const isReferenceLayer = (layer: CommonXYLayerConfig): layer is Reference export const isReferenceLine = (layer: CommonXYLayerConfig): layer is ReferenceLineConfig => layer.type === REFERENCE_LINE; -export const isReferenceLineYConfig = ( - yConfig: ReferenceLineYConfig | ExtendedYConfigResult -): yConfig is ReferenceLineYConfig => yConfig.type === REFERENCE_LINE_Y_CONFIG; +export const isReferenceLineDecorationConfig = ( + decoration: ExtendedReferenceLineDecorationConfig | ReferenceLineDecorationConfigResult +): decoration is ExtendedReferenceLineDecorationConfig => + decoration.type === EXTENDED_REFERENCE_LINE_DECORATION_CONFIG; export const isReferenceLineOrLayer = ( layer: CommonXYLayerConfig diff --git a/src/plugins/chart_expressions/expression_xy/public/plugin.ts b/src/plugins/chart_expressions/expression_xy/public/plugin.ts index 1caf2bf1bbbdb..f0cc90baab1e4 100755 --- a/src/plugins/chart_expressions/expression_xy/public/plugin.ts +++ b/src/plugins/chart_expressions/expression_xy/public/plugin.ts @@ -18,16 +18,16 @@ import { xyVisFunction, layeredXyVisFunction, extendedDataLayerFunction, - yConfigFunction, + dataDecorationConfigFunction, xAxisConfigFunction, yAxisConfigFunction, - extendedYConfigFunction, legendConfigFunction, axisExtentConfigFunction, referenceLineFunction, referenceLineLayerFunction, annotationLayerFunction, extendedAnnotationLayerFunction, + referenceLineDecorationConfigFunction, } from '../common/expression_functions'; import { GetStartDepsFn, getXyChartRenderer } from './expression_renderers'; @@ -53,8 +53,8 @@ export class ExpressionXyPlugin { { expressions, charts }: SetupDeps ): ExpressionXyPluginSetup { expressions.registerFunction(yAxisConfigFunction); - expressions.registerFunction(yConfigFunction); - expressions.registerFunction(extendedYConfigFunction); + expressions.registerFunction(dataDecorationConfigFunction); + expressions.registerFunction(referenceLineDecorationConfigFunction); expressions.registerFunction(legendConfigFunction); expressions.registerFunction(extendedDataLayerFunction); expressions.registerFunction(axisExtentConfigFunction); diff --git a/src/plugins/chart_expressions/expression_xy/server/plugin.ts b/src/plugins/chart_expressions/expression_xy/server/plugin.ts index 058ab8cfd10b6..f0e0fc141302a 100755 --- a/src/plugins/chart_expressions/expression_xy/server/plugin.ts +++ b/src/plugins/chart_expressions/expression_xy/server/plugin.ts @@ -12,10 +12,10 @@ import { ExpressionXyPluginSetup, ExpressionXyPluginStart } from './types'; import { xyVisFunction, legendConfigFunction, - yConfigFunction, + dataDecorationConfigFunction, xAxisConfigFunction, yAxisConfigFunction, - extendedYConfigFunction, + referenceLineDecorationConfigFunction, axisExtentConfigFunction, annotationLayerFunction, referenceLineFunction, @@ -31,9 +31,9 @@ export class ExpressionXyPlugin { public setup(core: CoreSetup, { expressions }: SetupDeps) { expressions.registerFunction(yAxisConfigFunction); - expressions.registerFunction(yConfigFunction); + expressions.registerFunction(dataDecorationConfigFunction); expressions.registerFunction(xAxisConfigFunction); - expressions.registerFunction(extendedYConfigFunction); + expressions.registerFunction(referenceLineDecorationConfigFunction); expressions.registerFunction(legendConfigFunction); expressions.registerFunction(extendedDataLayerFunction); expressions.registerFunction(axisExtentConfigFunction); 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 c0b84eb09bbea..aac91b9682e42 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 @@ -35,6 +35,7 @@ Object { "columnToLabel": Array [ "{\\"b\\":\\"col_b\\",\\"c\\":\\"col_c\\",\\"d\\":\\"col_d\\"}", ], + "decorations": Array [], "hide": Array [ false, ], @@ -81,7 +82,6 @@ Object { "xScaleType": Array [ "linear", ], - "yConfig": Array [], }, "function": "extendedDataLayer", "type": "function", @@ -181,7 +181,9 @@ Object { "showTitle": Array [ true, ], - "title": Array [], + "title": Array [ + "", + ], }, "function": "yAxisConfig", "type": "function", @@ -231,7 +233,9 @@ Object { "showTitle": Array [ true, ], - "title": Array [], + "title": Array [ + "", + ], }, "function": "yAxisConfig", "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 6376c5f30eaf2..9a2d83809b1ca 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 @@ -436,7 +436,7 @@ describe('#toExpression', () => { ) as Ast; function getYConfigColorForLayer(ast: Ast, index: number) { - return ((ast.chain[0].arguments.layers[index] as Ast).chain[0].arguments.yConfig[0] as Ast) + return ((ast.chain[0].arguments.layers[index] as Ast).chain[0].arguments.decorations[0] as Ast) .chain[0].arguments.color; } expect(getYConfigColorForLayer(expression, 0)).toEqual([]); 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 534a0d3c76ea5..8628e036095b4 100644 --- a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts +++ b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts @@ -9,7 +9,7 @@ import { Ast, AstFunction } from '@kbn/interpreter'; import { ScaleType } from '@elastic/charts'; import type { PaletteRegistry } from '@kbn/coloring'; import { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public'; -import type { YAxisConfig, AxisExtentConfig } from '@kbn/expression-xy-plugin/common'; +import type { AxisExtentConfig } from '@kbn/expression-xy-plugin/common'; import { LegendSize } from '@kbn/visualizations-plugin/common/constants'; import { State, @@ -17,6 +17,7 @@ import { XYDataLayerConfig, XYReferenceLineLayerConfig, XYAnnotationLayerConfig, + AxisConfig, } from './types'; import type { ValidXYDataLayerConfig } from './types'; import { OperationMetadata, DatasourcePublicAPI, DatasourceLayers } from '../types'; @@ -190,19 +191,17 @@ export const buildExpression = ( return null; } - const validLayersWithYConfig = [...validDataLayers, ...validReferenceLayers]; - - const isLeftAxis = validLayersWithYConfig.some(({ yConfig }) => + const isLeftAxis = validDataLayers.some(({ yConfig }) => yConfig?.some((config) => config.axisMode === 'left') ); - const isRightAxis = validLayersWithYConfig.some(({ yConfig }) => + const isRightAxis = validDataLayers.some(({ yConfig }) => yConfig?.some((config) => config.axisMode === 'right') ); - const yAxisConfigs: YAxisConfig[] = [ + const yAxisConfigs: AxisConfig[] = [ { position: 'left', - extent: state?.yLeftExtent ? { ...state?.yLeftExtent, type: 'axisExtentConfig' } : undefined, + extent: state?.yLeftExtent, showTitle: state?.axisTitlesVisibilitySettings?.yLeft ?? true, title: state.yTitle || '', showLabels: state?.tickLabelsVisibilitySettings?.yLeft ?? true, @@ -212,9 +211,7 @@ export const buildExpression = ( }, { position: 'right', - extent: state?.yRightExtent - ? { ...state?.yRightExtent, type: 'axisExtentConfig' } - : undefined, + extent: state?.yRightExtent, showTitle: state?.axisTitlesVisibilitySettings?.yRight ?? true, title: state.yRightTitle || '', showLabels: state?.tickLabelsVisibilitySettings?.yRight ?? true, @@ -228,6 +225,8 @@ export const buildExpression = ( yAxisConfigs.push({ id: 'left', position: 'left', + // we need also settings from global config here so that default's doesn't override it + ...yAxisConfigs[0], }); } @@ -235,6 +234,8 @@ export const buildExpression = ( yAxisConfigs.push({ id: 'right', position: 'right', + // we need also settings from global config here so that default's doesn't override it + ...yAxisConfigs[1], }); } @@ -331,7 +332,6 @@ export const buildExpression = ( ...validReferenceLayers.map((layer) => referenceLineLayerToExpression( layer, - yAxisConfigs, datasourceLayers[(layer as XYReferenceLineLayerConfig).layerId], datasourceExpressionsByLayers[layer.layerId] ) @@ -347,7 +347,7 @@ export const buildExpression = ( }; const yAxisConfigsToExpression = ( - yAxisConfigs: YAxisConfig[], + yAxisConfigs: AxisConfig[], validDataLayers: ValidXYDataLayerConfig[] ): Ast[] => { return yAxisConfigs.map((axis) => ({ @@ -360,10 +360,10 @@ const yAxisConfigsToExpression = ( id: axis.id ? [axis.id] : [], position: axis.position ? [axis.position] : [], extent: axis.extent ? [axisExtentConfigToExpression(axis.extent, validDataLayers)] : [], - showTitle: axis.showTitle ? [axis.showTitle] : [], - title: axis.title ? [axis.title] : [], - showLabels: axis.showLabels ? [axis.showLabels] : [], - showGridLines: axis.showGridLines ? [axis.showGridLines] : [], + showTitle: [axis.showTitle ?? true], + title: axis.title !== undefined ? [axis.title] : [], + showLabels: [axis.showLabels ?? true], + showGridLines: [axis.showGridLines ?? true], labelsOrientation: axis.labelsOrientation !== undefined ? [axis.labelsOrientation] : [], }, }, @@ -373,7 +373,6 @@ const yAxisConfigsToExpression = ( const referenceLineLayerToExpression = ( layer: XYReferenceLineLayerConfig, - yAxisConfigs: YAxisConfig[], datasourceLayer: DatasourcePublicAPI, datasourceExpression: Ast ): Ast => { @@ -385,9 +384,9 @@ const referenceLineLayerToExpression = ( function: 'referenceLineLayer', arguments: { layerId: [layer.layerId], - yConfig: layer.yConfig + decorations: layer.yConfig ? layer.yConfig.map((yConfig) => - extendedYConfigToExpression(yConfig, yAxisConfigs, defaultReferenceLineColor) + extendedYConfigToRLDecorationConfigExpression(yConfig, defaultReferenceLineColor) ) : [], accessors: layer.accessors, @@ -423,7 +422,7 @@ const annotationLayerToExpression = ( const dataLayerToExpression = ( layer: ValidXYDataLayerConfig, - yAxisConfigs: YAxisConfig[], + yAxisConfigs: AxisConfig[], datasourceLayer: DatasourcePublicAPI, metadata: Record>, paletteService: PaletteRegistry, @@ -445,6 +444,7 @@ const dataLayerToExpression = ( const isPercentage = dataFromType.includes('percentage'); const isStacked = dataFromType.includes('stacked'); const isHorizontal = dataFromType.includes('horizontal'); + debugger; return { type: 'expression', @@ -462,8 +462,10 @@ const dataLayerToExpression = ( isStacked: isStacked ? [isStacked] : [], isHorizontal: isHorizontal ? [isHorizontal] : [], splitAccessor: layer.collapseFn || !layer.splitAccessor ? [] : [layer.splitAccessor], - yConfig: layer.yConfig - ? layer.yConfig.map((yConfig) => yConfigToExpression(yConfig, yAxisConfigs)) + decorations: layer.yConfig + ? layer.yConfig.map((yConfig) => + yConfigToDataDecorationConfigExpression(yConfig, yAxisConfigs) + ) : [], seriesType: [seriesType], accessors: layer.accessors, @@ -524,9 +526,9 @@ const dataLayerToExpression = ( }; }; -const yConfigToExpression = ( +const yConfigToDataDecorationConfigExpression = ( yConfig: YConfig, - yAxisConfigs: YAxisConfig[], + yAxisConfigs: AxisConfig[], defaultColor?: string ): Ast => { const axisId = yAxisConfigs.find((axis) => axis.id && axis.position === yConfig.axisMode)?.id; @@ -535,7 +537,7 @@ const yConfigToExpression = ( chain: [ { type: 'function', - function: 'yConfig', + function: 'dataDecorationConfig', arguments: { axisId: axisId ? [axisId] : [], forAccessor: [yConfig.forAccessor], @@ -546,21 +548,19 @@ const yConfigToExpression = ( }; }; -const extendedYConfigToExpression = ( +const extendedYConfigToRLDecorationConfigExpression = ( yConfig: YConfig, - yAxisConfigs: YAxisConfig[], defaultColor?: string ): Ast => { - const axisId = yAxisConfigs.find((axis) => axis.id && axis.position === yConfig.axisMode)?.id; return { type: 'expression', chain: [ { type: 'function', - function: 'extendedYConfig', + function: 'referenceLineDecorationConfig', arguments: { forAccessor: [yConfig.forAccessor], - axisId: axisId ? [axisId] : [], + position: yConfig.axisMode ? [yConfig.axisMode] : [], color: yConfig.color ? [yConfig.color] : defaultColor ? [defaultColor] : [], lineStyle: [yConfig.lineStyle || 'solid'], lineWidth: [yConfig.lineWidth || 1], diff --git a/x-pack/plugins/lens/public/xy_visualization/types.ts b/x-pack/plugins/lens/public/xy_visualization/types.ts index 0cbb86462dab9..b55c7642ddebf 100644 --- a/x-pack/plugins/lens/public/xy_visualization/types.ts +++ b/x-pack/plugins/lens/public/xy_visualization/types.ts @@ -19,6 +19,7 @@ import type { LineStyle, IconPosition, FillStyle, + YAxisConfig, } from '@kbn/expression-xy-plugin/common'; import { EventAnnotationConfig } from '@kbn/event-annotation-plugin/common'; import { LensIconChartArea } from '../assets/chart_area'; @@ -63,6 +64,10 @@ export interface AxesSettingsConfig { yLeft: boolean; } +export interface AxisConfig extends Omit { + extent?: AxisExtentConfig; +} + export interface LabelsOrientationConfig { x: number; yLeft: number; From 505646af9fa4f76f995513119802e50372dc54a4 Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Wed, 8 Jun 2022 17:10:49 +0300 Subject: [PATCH 183/213] Fix types and i18n --- x-pack/plugins/lens/public/index.ts | 3 +-- .../components/shared/exploratory_view/types.ts | 4 ++-- x-pack/plugins/translations/translations/fr-FR.json | 11 ----------- x-pack/plugins/translations/translations/ja-JP.json | 11 ----------- x-pack/plugins/translations/translations/zh-CN.json | 11 ----------- 5 files changed, 3 insertions(+), 37 deletions(-) diff --git a/x-pack/plugins/lens/public/index.ts b/x-pack/plugins/lens/public/index.ts index df0d1e4e6ed6d..715df94d82ac4 100644 --- a/x-pack/plugins/lens/public/index.ts +++ b/x-pack/plugins/lens/public/index.ts @@ -20,6 +20,7 @@ export type { XYAnnotationLayerConfig, YAxisMode, SeriesType, + YConfig, } from './xy_visualization/types'; export type { DatasourcePublicAPI, @@ -76,7 +77,6 @@ export type { } from './indexpattern_datasource/types'; export type { XYArgs, - ExtendedYConfig, XYRender, LayerType, LineStyle, @@ -88,7 +88,6 @@ export type { XYChartProps, LegendConfig, IconPosition, - ExtendedYConfigResult, DataLayerArgs, ValueLabelMode, AxisExtentMode, 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 74d0578d459f9..85fbf2e073ecb 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 type { FieldBasedIndexPatternColumn, SeriesType, OperationType, - ExtendedYConfig, + YConfig, } from '@kbn/lens-plugin/public'; import type { PersistableFilter } from '@kbn/lens-plugin/common'; @@ -84,7 +84,7 @@ export interface SeriesConfig { hasOperationType: boolean; palette?: PaletteOutput; yTitle?: string; - yConfig?: ExtendedYConfig[]; + yConfig?: YConfig[]; query?: { query: string; language: 'kuery' }; } diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 0e933e3106c68..267d27227ba94 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -3451,7 +3451,6 @@ "expressionXY.dataLayer.splitAccessor.help": "Colonne selon laquelle effectuer la division", "expressionXY.dataLayer.xAccessor.help": "Axe X", "expressionXY.dataLayer.xScaleType.help": "Type d’échelle de l’axe x", - "expressionXY.dataLayer.yConfig.help": "Configuration supplémentaire pour les axes y", "expressionXY.legend.filterForValueButtonAriaLabel": "Filtrer sur la valeur", "expressionXY.legend.filterOptionsLegend": "{legendDataLabel}, options de filtre", "expressionXY.legend.filterOutValueButtonAriaLabel": "Exclure la valeur", @@ -3468,7 +3467,6 @@ "expressionXY.legendConfig.verticalAlignment.help": "Spécifie l'alignement vertical de la légende lorsqu'elle est affichée à l'intérieur du graphique.", "expressionXY.referenceLineLayer.accessors.help": "Les colonnes à afficher sur l’axe y.", "expressionXY.referenceLineLayer.help": "Configurer une ligne de référence dans le graphique xy", - "expressionXY.referenceLineLayer.yConfig.help": "Configuration supplémentaire pour les axes y", "expressionXY.xyChart.emptyXLabel": "(vide)", "expressionXY.xyChart.iconSelect.alertIconLabel": "Alerte", "expressionXY.xyChart.iconSelect.asteriskIconLabel": "Astérisque", @@ -3499,15 +3497,6 @@ "expressionXY.xyVis.renderer.help": "Outil de rendu de graphique X/Y", "expressionXY.xyVis.valueLabels.help": "Mode des étiquettes de valeur", "expressionXY.xyVis.valuesInLegend.help": "Afficher les valeurs dans la légende", - "expressionXY.yConfig.color.help": "La couleur des séries", - "expressionXY.yConfig.fill.help": "Remplir", - "expressionXY.yConfig.forAccessor.help": "L’accesseur auquel cette configuration s’applique", - "expressionXY.yConfig.help": "Configurer le comportement de l’indicateur d’axe y d’un graphique xy", - "expressionXY.yConfig.icon.help": "Icône facultative utilisée pour les lignes de référence", - "expressionXY.yConfig.iconPosition.help": "Le placement de l’icône pour la ligne de référence", - "expressionXY.yConfig.lineStyle.help": "Le style de la ligne de référence", - "expressionXY.yConfig.lineWidth.help": "La largeur de la ligne de référence", - "expressionXY.yConfig.textVisibility.help": "Visibilité de l’étiquette sur la ligne de référence", "fieldFormats.advancedSettings.format.bytesFormat.numeralFormatLinkText": "Format numérique", "fieldFormats.advancedSettings.format.bytesFormatText": "{numeralFormatLink} par défaut pour le format \"octets\"", "fieldFormats.advancedSettings.format.bytesFormatTitle": "Format octets", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 72d4ce07d5c14..5da40092526c8 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -3545,7 +3545,6 @@ "expressionXY.dataLayer.splitAccessor.help": "分割の基準となる列", "expressionXY.dataLayer.xAccessor.help": "X 軸", "expressionXY.dataLayer.xScaleType.help": "x軸の目盛タイプ", - "expressionXY.dataLayer.yConfig.help": "y軸の詳細構成", "expressionXY.legend.filterForValueButtonAriaLabel": "値でフィルター", "expressionXY.legend.filterOptionsLegend": "{legendDataLabel}、フィルターオプション", "expressionXY.legend.filterOutValueButtonAriaLabel": "値を除外", @@ -3562,7 +3561,6 @@ "expressionXY.legendConfig.verticalAlignment.help": "凡例がグラフ内に表示されるときに凡例の縦の配置を指定します。", "expressionXY.referenceLineLayer.accessors.help": "y軸に表示する列。", "expressionXY.referenceLineLayer.help": "xyグラフで基準線を構成", - "expressionXY.referenceLineLayer.yConfig.help": "y軸の詳細構成", "expressionXY.xyChart.emptyXLabel": "(空)", "expressionXY.xyChart.iconSelect.alertIconLabel": "アラート", "expressionXY.xyChart.iconSelect.asteriskIconLabel": "アスタリスク", @@ -3593,15 +3591,6 @@ "expressionXY.xyVis.renderer.help": "X/Y チャートを再レンダリング", "expressionXY.xyVis.valueLabels.help": "値ラベルモード", "expressionXY.xyVis.valuesInLegend.help": "凡例に値を表示", - "expressionXY.yConfig.color.help": "系列の色", - "expressionXY.yConfig.fill.help": "塗りつぶし", - "expressionXY.yConfig.forAccessor.help": "この構成のアクセサー", - "expressionXY.yConfig.help": "xyグラフのy軸メトリックの動作を構成", - "expressionXY.yConfig.icon.help": "基準線で使用される任意のアイコン", - "expressionXY.yConfig.iconPosition.help": "基準線のアイコンの配置", - "expressionXY.yConfig.lineStyle.help": "基準線のスタイル", - "expressionXY.yConfig.lineWidth.help": "基準線の幅", - "expressionXY.yConfig.textVisibility.help": "基準線のラベルの表示", "fieldFormats.advancedSettings.format.bytesFormat.numeralFormatLinkText": "数字フォーマット", "fieldFormats.advancedSettings.format.bytesFormatText": "「バイト」フォーマットのデフォルト{numeralFormatLink}です", "fieldFormats.advancedSettings.format.bytesFormatTitle": "バイトフォーマット", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 7b71dbb76caa8..615f949ac6454 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -3555,7 +3555,6 @@ "expressionXY.dataLayer.splitAccessor.help": "拆分要依据的列", "expressionXY.dataLayer.xAccessor.help": "X 轴", "expressionXY.dataLayer.xScaleType.help": "x 轴的缩放类型", - "expressionXY.dataLayer.yConfig.help": "y 轴的其他配置", "expressionXY.legend.filterForValueButtonAriaLabel": "筛留值", "expressionXY.legend.filterOptionsLegend": "{legendDataLabel}, 筛选选项", "expressionXY.legend.filterOutValueButtonAriaLabel": "筛除值", @@ -3572,7 +3571,6 @@ "expressionXY.legendConfig.verticalAlignment.help": "指定图例显示在图表内时垂直对齐。", "expressionXY.referenceLineLayer.accessors.help": "要在 y 轴上显示的列。", "expressionXY.referenceLineLayer.help": "配置 xy 图表中的参考线", - "expressionXY.referenceLineLayer.yConfig.help": "y 轴的其他配置", "expressionXY.xyChart.emptyXLabel": "(空)", "expressionXY.xyChart.iconSelect.alertIconLabel": "告警", "expressionXY.xyChart.iconSelect.asteriskIconLabel": "星号", @@ -3603,15 +3601,6 @@ "expressionXY.xyVis.renderer.help": "X/Y 图表呈现器", "expressionXY.xyVis.valueLabels.help": "值标签模式", "expressionXY.xyVis.valuesInLegend.help": "在图例中显示值", - "expressionXY.yConfig.color.help": "序列的颜色", - "expressionXY.yConfig.fill.help": "填充", - "expressionXY.yConfig.forAccessor.help": "此配置针对的访问器", - "expressionXY.yConfig.help": "配置 xy 图表的 y 轴指标的行为", - "expressionXY.yConfig.icon.help": "用于参考线的可选图标", - "expressionXY.yConfig.iconPosition.help": "参考线图标的位置", - "expressionXY.yConfig.lineStyle.help": "参考线的样式", - "expressionXY.yConfig.lineWidth.help": "参考线的宽度", - "expressionXY.yConfig.textVisibility.help": "参考线上标签的可见性", "fieldFormats.advancedSettings.format.bytesFormat.numeralFormatLinkText": "数值格式", "fieldFormats.advancedSettings.format.bytesFormatText": "“字节”格式的默认{numeralFormatLink}", "fieldFormats.advancedSettings.format.bytesFormatTitle": "字节格式", From 22fdd8283ea9c0ee23b572940b60e156e9c9615d Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Wed, 8 Jun 2022 15:10:13 +0000 Subject: [PATCH 184/213] [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' --- .../components/reference_lines/reference_line_layer.tsx | 3 ++- .../lens/public/xy_visualization/to_expression.test.ts | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_line_layer.tsx b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_line_layer.tsx index c7d4eb7204de0..21cb546654a7d 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_line_layer.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_line_layer.tsx @@ -63,7 +63,8 @@ export const ReferenceLineLayer: FC = ({ ({ forAccessor }) => forAccessor === decorationConfig.forAccessor ); - const shouldCheckNextReferenceLine = indexFromSameType < yDecorationsWithSameDirection.length - 1; + const shouldCheckNextReferenceLine = + indexFromSameType < yDecorationsWithSameDirection.length - 1; const nextValue = shouldCheckNextReferenceLine ? row[yDecorationsWithSameDirection[indexFromSameType + 1].forAccessor] 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 1a5e9d22f4104..c22638f54dd77 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 @@ -494,8 +494,9 @@ describe('#toExpression', () => { ) as Ast; function getYConfigColorForLayer(ast: Ast, index: number) { - return ((ast.chain[0].arguments.layers[index] as Ast).chain[0].arguments.decorations[0] as Ast) - .chain[0].arguments.color; + return ( + (ast.chain[0].arguments.layers[index] as Ast).chain[0].arguments.decorations[0] as Ast + ).chain[0].arguments.color; } expect(getYConfigColorForLayer(expression, 0)).toEqual([]); expect(getYConfigColorForLayer(expression, 1)).toEqual([defaultReferenceLineColor]); From c0492a6362b9ed62af0eaa6365c0da9b9f5a24cb Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Thu, 9 Jun 2022 10:55:36 +0300 Subject: [PATCH 185/213] Fix lint --- x-pack/plugins/lens/public/xy_visualization/to_expression.ts | 1 - 1 file changed, 1 deletion(-) 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 ada1277548d0f..7430476f6eb4a 100644 --- a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts +++ b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts @@ -442,7 +442,6 @@ const dataLayerToExpression = ( const isPercentage = dataFromType.includes('percentage'); const isStacked = dataFromType.includes('stacked'); const isHorizontal = dataFromType.includes('horizontal'); - debugger; return { type: 'expression', From 98d383cb0c03c135607d1757152a5a0abb39cd75 Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Fri, 10 Jun 2022 13:06:43 +0300 Subject: [PATCH 186/213] Fix some nits --- .../public/components/data_layers.tsx | 7 ++--- .../public/helpers/axes_configuration.ts | 31 +++++++++++-------- 2 files changed, 21 insertions(+), 17 deletions(-) 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 03a5d007b12aa..919e336ebbc94 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 @@ -94,10 +94,9 @@ export const DataLayers: FC = ({ axisConfiguration.series.find((currentSeries) => currentSeries.accessor === yColumnId) ); - let isPercentage = layer.isPercentage; - if (yAxis?.mode) { - isPercentage = yAxis?.mode === AxisModes.PERCENTAGE; - } + const isPercentage = yAxis?.mode + ? yAxis?.mode === AxisModes.PERCENTAGE + : layer.isPercentage; const seriesProps = getSeriesProps({ layer, 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 9db6976bb37ea..2b75cf36f6d0e 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 @@ -37,8 +37,11 @@ interface AxesSeries { } export interface AxisConfiguration extends Omit { + /** + * Axis group identificator. Format: `axis-${axis.id}` or just `left`/`right`. + */ groupId: string; - position: 'left' | 'right' | 'bottom' | 'top'; + position: Position; formatter?: IFieldFormat; series: Series[]; } @@ -77,7 +80,7 @@ export function groupAxesByType( (axis) => decorationByAccessor?.axisId && axis.id && axis.id === decorationByAccessor?.axisId ); - const key = axisConfigById?.id || 'auto'; + const key = `axis-${axisConfigById?.id}` || 'auto'; const fieldFormat = fieldFormats[layerId].yAccessors[yAccessor]!; if (!series[key]) { series[key] = []; @@ -89,7 +92,9 @@ export function groupAxesByType( const tablesExist = layers.filter(({ table }) => Boolean(table)).length > 0; series.auto.forEach((currentSeries) => { - let key = Object.keys(series).find( + // if we already have the specified axis with compatible formatters, we should use it + // if not, use logic below this `find` with auto-assignment series to left or right global axis + let axisGroupId = Object.keys(series).find( (seriesKey) => seriesKey.includes('axis') && series[seriesKey].length > 0 && @@ -97,7 +102,7 @@ export function groupAxesByType( isFormatterCompatible(axisSeries.fieldFormat, currentSeries.fieldFormat) ) ); - if (!key) { + if (!axisGroupId) { if ( series.left.length === 0 || (tablesExist && @@ -105,7 +110,7 @@ export function groupAxesByType( isFormatterCompatible(leftSeries.fieldFormat, currentSeries.fieldFormat) )) ) { - key = 'left'; + axisGroupId = 'left'; } else if ( series.right.length === 0 || (tablesExist && @@ -113,15 +118,15 @@ export function groupAxesByType( isFormatterCompatible(leftSeries.fieldFormat, currentSeries.fieldFormat) )) ) { - key = 'right'; + axisGroupId = 'right'; } else if (series.right.length >= series.left.length) { - key = 'left'; + axisGroupId = 'left'; } else { - key = 'right'; + axisGroupId = 'right'; } } - series[key].push(currentSeries); + series[axisGroupId].push(currentSeries); }); return series; } @@ -178,9 +183,9 @@ export function getAxesConfiguration( }); if (series.left.length > 0) { - position = shouldRotate ? 'bottom' : 'left'; + position = shouldRotate ? Position.Bottom : Position.Left; axisGroups.push({ - groupId: 'left', + groupId: Position.Left, formatter: formatFactory?.(series.left[0].fieldFormat), series: series.left.map(({ fieldFormat, ...currentSeries }) => currentSeries), ...axisGlobalConfig(position, yAxisConfigs), @@ -189,9 +194,9 @@ export function getAxesConfiguration( } if (series.right.length > 0) { - position = shouldRotate ? 'top' : 'right'; + position = shouldRotate ? Position.Top : Position.Right; axisGroups.push({ - groupId: 'right', + groupId: Position.Right, formatter: formatFactory?.(series.right[0].fieldFormat), series: series.right.map(({ fieldFormat, ...currentSeries }) => currentSeries), ...axisGlobalConfig(position, yAxisConfigs), From abeae020d276fb573ef9faa245313ef9ed02c024 Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Fri, 10 Jun 2022 14:44:01 +0300 Subject: [PATCH 187/213] Fix CI --- .../expression_xy/public/helpers/axes_configuration.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) 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 2b75cf36f6d0e..b23c684a22903 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 @@ -169,12 +169,13 @@ export function getAxesConfiguration( let position: Position; yAxisConfigs?.forEach((axis) => { - if (axis.id && series[axis.id] && series[axis.id].length > 0) { + const groupId = axis.id ? `axis-${axis.id}` : undefined; + if (groupId && series[groupId] && series[groupId].length > 0) { position = getAxisPosition(axis.position || Position.Left, shouldRotate); axisGroups.push({ - groupId: `axis-${axis.id}`, - formatter: formatFactory?.(series[axis.id][0].fieldFormat), - series: series[axis.id].map(({ fieldFormat, ...currentSeries }) => currentSeries), + groupId, + formatter: formatFactory?.(series[groupId][0].fieldFormat), + series: series[groupId].map(({ fieldFormat, ...currentSeries }) => currentSeries), ...axisGlobalConfig(position, yAxisConfigs), ...axis, position, From 7cc36d117dcb68e3e1895b578c73d70033fba4fc Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Fri, 10 Jun 2022 15:39:46 +0300 Subject: [PATCH 188/213] Fix CI --- .../expression_xy/public/helpers/axes_configuration.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 b23c684a22903..924c481857c3d 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 @@ -80,7 +80,7 @@ export function groupAxesByType( (axis) => decorationByAccessor?.axisId && axis.id && axis.id === decorationByAccessor?.axisId ); - const key = `axis-${axisConfigById?.id}` || 'auto'; + const key = axisConfigById?.id ? `axis-${axisConfigById?.id}` : 'auto'; const fieldFormat = fieldFormats[layerId].yAccessors[yAccessor]!; if (!series[key]) { series[key] = []; From 71ce9c494aafd897d014b94cc191a8a85880ffaa Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Mon, 13 Jun 2022 11:11:44 +0300 Subject: [PATCH 189/213] Fix CI --- .../public/helpers/axes_configuration.ts | 53 ++++++++----------- 1 file changed, 21 insertions(+), 32 deletions(-) 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 924c481857c3d..392438d652baf 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 @@ -92,38 +92,27 @@ export function groupAxesByType( const tablesExist = layers.filter(({ table }) => Boolean(table)).length > 0; series.auto.forEach((currentSeries) => { - // if we already have the specified axis with compatible formatters, we should use it - // if not, use logic below this `find` with auto-assignment series to left or right global axis - let axisGroupId = Object.keys(series).find( - (seriesKey) => - seriesKey.includes('axis') && - series[seriesKey].length > 0 && - series[seriesKey].every((axisSeries) => - isFormatterCompatible(axisSeries.fieldFormat, currentSeries.fieldFormat) - ) - ); - if (!axisGroupId) { - if ( - series.left.length === 0 || - (tablesExist && - series.left.every((leftSeries) => - isFormatterCompatible(leftSeries.fieldFormat, currentSeries.fieldFormat) - )) - ) { - axisGroupId = 'left'; - } else if ( - series.right.length === 0 || - (tablesExist && - series.left.every((leftSeries) => - isFormatterCompatible(leftSeries.fieldFormat, currentSeries.fieldFormat) - )) - ) { - axisGroupId = 'right'; - } else if (series.right.length >= series.left.length) { - axisGroupId = 'left'; - } else { - axisGroupId = 'right'; - } + let axisGroupId; + if ( + series.left.length === 0 || + (tablesExist && + series.left.every((leftSeries) => + isFormatterCompatible(leftSeries.fieldFormat, currentSeries.fieldFormat) + )) + ) { + axisGroupId = 'left'; + } else if ( + series.right.length === 0 || + (tablesExist && + series.left.every((leftSeries) => + isFormatterCompatible(leftSeries.fieldFormat, currentSeries.fieldFormat) + )) + ) { + axisGroupId = 'right'; + } else if (series.right.length >= series.left.length) { + axisGroupId = 'left'; + } else { + axisGroupId = 'right'; } series[axisGroupId].push(currentSeries); From 8bbf1015de755ac0c79ee66a5e41c40eb3c967db Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Mon, 13 Jun 2022 15:48:18 +0300 Subject: [PATCH 190/213] Refactoring auto assignment logic --- .../public/helpers/axes_configuration.ts | 73 +++++++++++++------ 1 file changed, 49 insertions(+), 24 deletions(-) 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 392438d652baf..35ea1a7d85db3 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 @@ -55,6 +55,18 @@ export function isFormatterCompatible( return formatter1?.id === formatter2?.id; } +const LEFT_GLOBAL_AXIS_ID = 'left'; +const RIGHT_GLOBAL_AXIS_ID = 'right'; + +function isAxisSeriesAppliedForFormatter( + series: FormattedMetric[], + currentSeries: FormattedMetric +) { + return series.every((leftSeries) => + isFormatterCompatible(leftSeries.fieldFormat, currentSeries.fieldFormat) + ); +} + export function groupAxesByType( layers: CommonXYDataLayerConfig[], fieldFormats: LayersFieldFormats, @@ -66,6 +78,9 @@ export function groupAxesByType( right: [], }; + const leftSeriesKeys: string[] = []; + const rightSeriesKeys: string[] = []; + layers.forEach((layer) => { const { layerId, table } = layer; layer.accessors.forEach((accessor) => { @@ -86,33 +101,43 @@ export function groupAxesByType( series[key] = []; } series[key].push({ layer: layer.layerId, accessor: yAccessor, fieldFormat }); + + if (axisConfigById?.position === Position.Left) { + leftSeriesKeys.push(key); + } else if (axisConfigById?.position === Position.Right) { + rightSeriesKeys.push(key); + } }); }); const tablesExist = layers.filter(({ table }) => Boolean(table)).length > 0; + leftSeriesKeys.push(LEFT_GLOBAL_AXIS_ID); + rightSeriesKeys.push(RIGHT_GLOBAL_AXIS_ID); + series.auto.forEach((currentSeries) => { - let axisGroupId; - if ( - series.left.length === 0 || - (tablesExist && - series.left.every((leftSeries) => - isFormatterCompatible(leftSeries.fieldFormat, currentSeries.fieldFormat) - )) - ) { - axisGroupId = 'left'; - } else if ( - series.right.length === 0 || - (tablesExist && - series.left.every((leftSeries) => - isFormatterCompatible(leftSeries.fieldFormat, currentSeries.fieldFormat) - )) - ) { - axisGroupId = 'right'; - } else if (series.right.length >= series.left.length) { - axisGroupId = 'left'; + const leftAxisGroupId = tablesExist + ? leftSeriesKeys.find((leftSeriesKey) => + isAxisSeriesAppliedForFormatter(series[leftSeriesKey], currentSeries) + ) + : undefined; + + const rightAxisGroupId = tablesExist + ? rightSeriesKeys.find((rightSeriesKey) => + isAxisSeriesAppliedForFormatter(series[rightSeriesKey], currentSeries) + ) + : undefined; + + let axisGroupId = LEFT_GLOBAL_AXIS_ID; + + if (series[LEFT_GLOBAL_AXIS_ID].length === 0 || leftAxisGroupId) { + axisGroupId = leftAxisGroupId || LEFT_GLOBAL_AXIS_ID; + } else if (series[RIGHT_GLOBAL_AXIS_ID].length === 0 || rightAxisGroupId) { + axisGroupId = rightAxisGroupId || RIGHT_GLOBAL_AXIS_ID; + } else if (series[RIGHT_GLOBAL_AXIS_ID].length >= series[LEFT_GLOBAL_AXIS_ID].length) { + axisGroupId = LEFT_GLOBAL_AXIS_ID; } else { - axisGroupId = 'right'; + axisGroupId = RIGHT_GLOBAL_AXIS_ID; } series[axisGroupId].push(currentSeries); @@ -172,10 +197,10 @@ export function getAxesConfiguration( } }); - if (series.left.length > 0) { + if (series[LEFT_GLOBAL_AXIS_ID].length > 0) { position = shouldRotate ? Position.Bottom : Position.Left; axisGroups.push({ - groupId: Position.Left, + groupId: LEFT_GLOBAL_AXIS_ID, formatter: formatFactory?.(series.left[0].fieldFormat), series: series.left.map(({ fieldFormat, ...currentSeries }) => currentSeries), ...axisGlobalConfig(position, yAxisConfigs), @@ -183,10 +208,10 @@ export function getAxesConfiguration( }); } - if (series.right.length > 0) { + if (series[RIGHT_GLOBAL_AXIS_ID].length > 0) { position = shouldRotate ? Position.Top : Position.Right; axisGroups.push({ - groupId: Position.Right, + groupId: RIGHT_GLOBAL_AXIS_ID, formatter: formatFactory?.(series.right[0].fieldFormat), series: series.right.map(({ fieldFormat, ...currentSeries }) => currentSeries), ...axisGlobalConfig(position, yAxisConfigs), From 83765fffc5333ca25df652bcff11c4e15ec9194c Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Wed, 15 Jun 2022 16:45:40 +0300 Subject: [PATCH 191/213] Add possibility to use multiple split accessors --- .../expression_xy/common/__mocks__/index.ts | 4 +- .../extended_data_layer.test.ts | 2 +- .../extended_data_layer.ts | 3 +- .../extended_data_layer_fn.ts | 2 +- .../common/expression_functions/xy_vis.ts | 3 +- .../common/expression_functions/xy_vis_fn.ts | 4 +- .../expression_xy/common/helpers/layers.ts | 16 +- .../common/types/expression_functions.ts | 4 +- .../expression_xy/public/__mocks__/index.tsx | 2 +- .../__snapshots__/xy_chart.test.tsx.snap | 40 +++- .../public/components/data_layers.tsx | 2 +- .../public/components/legend_action.test.tsx | 23 +- .../public/components/legend_action.tsx | 59 ++--- .../components/tooltip/tooltip.test.tsx | 47 ++-- .../public/components/xy_chart.test.tsx | 120 +++++++--- .../public/components/xy_chart.tsx | 52 ++--- .../public/helpers/axes_configuration.test.ts | 2 +- .../public/helpers/color_assignment.test.ts | 140 ++++++++---- .../public/helpers/color_assignment.ts | 110 ++++++--- .../public/helpers/data_layers.tsx | 211 +++++++++--------- .../expression_xy/public/helpers/layers.ts | 39 +++- .../expression_xy/public/helpers/state.ts | 2 +- .../public/xy_visualization/to_expression.ts | 2 +- 23 files changed, 551 insertions(+), 338 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 1bde83c1822b7..300bb7f5de30e 100644 --- a/src/plugins/chart_expressions/expression_xy/common/__mocks__/index.ts +++ b/src/plugins/chart_expressions/expression_xy/common/__mocks__/index.ts @@ -54,7 +54,7 @@ export const sampleLayer: DataLayerConfig = { seriesType: 'line', xAccessor: 'c', accessors: ['a', 'b'], - splitAccessor: 'd', + splitAccessors: ['d'], columnToLabel: '{"a": "Label A", "b": "Label B", "d": "Label D"}', xScaleType: 'ordinal', isHistogram: false, @@ -72,7 +72,7 @@ export const sampleExtendedLayer: ExtendedDataLayerConfig = { seriesType: 'line', xAccessor: 'c', accessors: ['a', 'b'], - splitAccessor: 'd', + splitAccessors: ['d'], columnToLabel: '{"a": "Label A", "b": "Label B", "d": "Label D"}', xScaleType: 'ordinal', isHistogram: false, diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_data_layer.test.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_data_layer.test.ts index 5ec11188058f5..0041218fe919d 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_data_layer.test.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_data_layer.test.ts @@ -17,7 +17,7 @@ describe('extendedDataLayerConfig', () => { seriesType: 'line', xAccessor: 'c', accessors: ['a', 'b'], - splitAccessor: 'd', + splitAccessors: ['d'], xScaleType: 'linear', isHistogram: false, isHorizontal: false, 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 58da88a8d4b25..17c6485c711da 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,9 +23,10 @@ export const extendedDataLayerFunction: ExtendedDataLayerFn = { types: ['string'], help: strings.getXAccessorHelp(), }, - splitAccessor: { + splitAccessors: { types: ['string'], help: strings.getSplitAccessorHelp(), + multi: true, }, accessors: { types: ['string'], diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_data_layer_fn.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_data_layer_fn.ts index 70a5bc2cf9e24..16905f96f9c2f 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_data_layer_fn.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/extended_data_layer_fn.ts @@ -23,7 +23,7 @@ export const extendedDataLayerFn: ExtendedDataLayerFn['fn'] = async (data, args, const accessors = getAccessors(args, table); validateAccessor(accessors.xAccessor, table.columns); - validateAccessor(accessors.splitAccessor, table.columns); + accessors.splitAccessors?.forEach((accessor) => validateAccessor(accessor, table.columns)); accessors.accessors.forEach((accessor) => validateAccessor(accessor, table.columns)); validateMarkSizeForChartType(args.markSizeAccessor, args.seriesType); validateAccessor(args.markSizeAccessor, table.columns); 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 7d2783cf6f1cd..9db238a117b75 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 @@ -24,9 +24,10 @@ export const xyVisFunction: XyVisFn = { types: ['string', 'vis_dimension'], help: strings.getXAccessorHelp(), }, - splitAccessor: { + splitAccessors: { types: ['string', 'vis_dimension'], help: strings.getSplitAccessorHelp(), + multi: true, }, accessors: { types: ['string', 'vis_dimension'], 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 5d56da5e92bf0..90f6a4d8f8c25 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 @@ -73,7 +73,7 @@ export const xyVisFn: XyVisFn['fn'] = async (data, args, handlers) => { accessors, xAccessor, hide, - splitAccessor, + splitAccessors, columnToLabel, xScaleType, isHistogram, @@ -96,7 +96,7 @@ export const xyVisFn: XyVisFn['fn'] = async (data, args, handlers) => { const dataLayers: DataLayerConfigResult[] = [createDataLayer({ ...args, showLines }, data)]; validateAccessor(dataLayers[0].xAccessor, data.columns); - validateAccessor(dataLayers[0].splitAccessor, data.columns); + dataLayers[0].splitAccessors?.forEach((accessor) => validateAccessor(accessor, data.columns)); dataLayers[0].accessors.forEach((accessor) => validateAccessor(accessor, data.columns)); validateMarkSizeForChartType(dataLayers[0].markSizeAccessor, args.seriesType); diff --git a/src/plugins/chart_expressions/expression_xy/common/helpers/layers.ts b/src/plugins/chart_expressions/expression_xy/common/helpers/layers.ts index 679fbfcc1a339..35036110eebaf 100644 --- a/src/plugins/chart_expressions/expression_xy/common/helpers/layers.ts +++ b/src/plugins/chart_expressions/expression_xy/common/helpers/layers.ts @@ -48,22 +48,28 @@ export function getDataLayers(layers: XYExtendedLayerConfigResult[]) { export function getAccessors< T, - U extends { splitAccessor?: T; xAccessor?: T; accessors: T[]; markSizeAccessor?: T } + U extends { splitAccessors?: T[]; xAccessor?: T; accessors: T[]; markSizeAccessor?: T } >(args: U, table: Datatable) { - let splitAccessor: T | string | undefined = args.splitAccessor; + let splitAccessors: Array | undefined = args.splitAccessors; let xAccessor: T | string | undefined = args.xAccessor; let accessors: Array = args.accessors ?? []; let markSizeAccessor: T | string | undefined = args.markSizeAccessor; - if (!splitAccessor && !xAccessor && !(accessors && accessors.length) && !markSizeAccessor) { + if ( + !(splitAccessors && splitAccessors.length) && + !xAccessor && + !(accessors && accessors.length) && + !markSizeAccessor + ) { const y = table.columns.find((column) => column.id === PointSeriesColumnNames.Y)?.id; + const splitColumnId = table.columns.find((column) => column.id === PointSeriesColumnNames.COLOR)?.id; xAccessor = table.columns.find((column) => column.id === PointSeriesColumnNames.X)?.id; - splitAccessor = table.columns.find((column) => column.id === PointSeriesColumnNames.COLOR)?.id; + splitAccessors = splitColumnId ? [splitColumnId] : []; accessors = y ? [y] : []; markSizeAccessor = table.columns.find( (column) => column.id === PointSeriesColumnNames.SIZE )?.id; } - return { splitAccessor, xAccessor, accessors, markSizeAccessor }; + return { splitAccessors, xAccessor, accessors, markSizeAccessor }; } 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 2528eaa8a9c03..6a12f41672fc0 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 @@ -116,7 +116,7 @@ export interface DataLayerArgs { seriesType: SeriesType; xAccessor?: string | ExpressionValueVisDimension; hide?: boolean; - splitAccessor?: string | ExpressionValueVisDimension; + splitAccessors?: Array; markSizeAccessor?: string | ExpressionValueVisDimension; lineWidth?: number; showPoints?: boolean; @@ -142,7 +142,7 @@ export interface ExtendedDataLayerArgs { seriesType: SeriesType; xAccessor?: string; hide?: boolean; - splitAccessor?: string; + splitAccessors?: string[]; markSizeAccessor?: string; lineWidth?: number; showPoints?: boolean; 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 a7c6494a33542..d4781db0ff915 100644 --- a/src/plugins/chart_expressions/expression_xy/public/__mocks__/index.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/__mocks__/index.tsx @@ -171,7 +171,7 @@ export const dateHistogramLayer: DataLayerConfig = { isStacked: true, isPercentage: false, isHorizontal: false, - splitAccessor: 'splitAccessorId', + splitAccessors: ['splitAccessorId'], seriesType: 'bar', accessors: ['yAccessorId'], palette: mockPaletteOutput, 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 a9fcc8484f814..614ab2b126a03 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 @@ -708,7 +708,9 @@ exports[`XYChart component it renders area 1`] = ` }, "seriesType": "area", "showLines": true, - "splitAccessor": "d", + "splitAccessors": Array [ + "d", + ], "table": Object { "columns": Array [ Object { @@ -1294,7 +1296,9 @@ exports[`XYChart component it renders bar 1`] = ` }, "seriesType": "bar", "showLines": true, - "splitAccessor": "d", + "splitAccessors": Array [ + "d", + ], "table": Object { "columns": Array [ Object { @@ -1880,7 +1884,9 @@ exports[`XYChart component it renders horizontal bar 1`] = ` }, "seriesType": "bar", "showLines": true, - "splitAccessor": "d", + "splitAccessors": Array [ + "d", + ], "table": Object { "columns": Array [ Object { @@ -2456,7 +2462,9 @@ exports[`XYChart component it renders line 1`] = ` }, "seriesType": "line", "showLines": true, - "splitAccessor": "d", + "splitAccessors": Array [ + "d", + ], "table": Object { "columns": Array [ Object { @@ -3042,7 +3050,9 @@ exports[`XYChart component it renders stacked area 1`] = ` }, "seriesType": "area", "showLines": true, - "splitAccessor": "d", + "splitAccessors": Array [ + "d", + ], "table": Object { "columns": Array [ Object { @@ -3628,7 +3638,9 @@ exports[`XYChart component it renders stacked bar 1`] = ` }, "seriesType": "bar", "showLines": true, - "splitAccessor": "d", + "splitAccessors": Array [ + "d", + ], "table": Object { "columns": Array [ Object { @@ -4214,7 +4226,9 @@ exports[`XYChart component it renders stacked horizontal bar 1`] = ` }, "seriesType": "bar", "showLines": true, - "splitAccessor": "d", + "splitAccessors": Array [ + "d", + ], "table": Object { "columns": Array [ Object { @@ -5058,7 +5072,9 @@ exports[`XYChart component split chart should render split chart if both, splitR }, "seriesType": "bar", "showLines": true, - "splitAccessor": "d", + "splitAccessors": Array [ + "d", + ], "table": Object { "columns": Array [ Object { @@ -5912,7 +5928,9 @@ exports[`XYChart component split chart should render split chart if splitColumnA }, "seriesType": "bar", "showLines": true, - "splitAccessor": "d", + "splitAccessors": Array [ + "d", + ], "table": Object { "columns": Array [ Object { @@ -6764,7 +6782,9 @@ exports[`XYChart component split chart should render split chart if splitRowAcce }, "seriesType": "bar", "showLines": true, - "splitAccessor": "d", + "splitAccessors": Array [ + "d", + ], "table": Object { "columns": Array [ Object { 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 919e336ebbc94..e05f2dc5d0113 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 @@ -74,7 +74,7 @@ export const DataLayers: FC = ({ chartHasMoreThanOneBarSeries, defaultXScaleType, }) => { - const colorAssignments = getColorAssignments(layers, formatFactory); + const colorAssignments = getColorAssignments(layers, formatFactory, titles); return ( <> {layers.flatMap((layer) => 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 147338853a808..033659f05335e 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 @@ -17,6 +17,7 @@ import { LayerTypes } from '../../common/constants'; import { getLegendAction } from './legend_action'; import { LegendActionPopover } from './legend_action_popover'; import { mockPaletteOutput } from '../../common/__mocks__'; +import { FormatFactory } from '../types'; const table: Datatable = { type: 'datatable', @@ -163,7 +164,7 @@ const sampleLayer: DataLayerConfig = { showLines: true, xAccessor: 'c', accessors: ['a', 'b'], - splitAccessor: 'splitAccessorId', + splitAccessors: ['splitAccessorId'], columnToLabel: '{"a": "Label A", "b": "Label B", "d": "Label D"}', xScaleType: 'ordinal', isHistogram: false, @@ -171,12 +172,25 @@ const sampleLayer: DataLayerConfig = { table, }; +const formatFactory = (() => + ({ + convert(x: unknown) { + return x; + }, + } as unknown)) as FormatFactory; + describe('getLegendAction', function () { let wrapperProps: LegendActionProps; const Component: ComponentType = getLegendAction( [sampleLayer], jest.fn(), - jest.fn(), + formatFactory, + { + first: { + table, + formattedColumns: {}, + }, + }, {} ); let wrapper: ReactWrapper; @@ -188,6 +202,7 @@ describe('getLegendAction', function () { series: [ { seriesKeys: ["Women's Accessories", 'test'], + splitAccessors: new Map().set('splitAccessorId', "Women's Accessories"), }, ] as unknown as SeriesIdentifier[], }; @@ -205,6 +220,7 @@ describe('getLegendAction', function () { series: [ { seriesKeys: ['test', 'b'], + splitAccessors: new Map().set('splitAccessorId', 'test'), }, ] as unknown as SeriesIdentifier[], }; @@ -219,12 +235,13 @@ describe('getLegendAction', function () { series: [ { seriesKeys: ["Women's Accessories", 'b'], + splitAccessors: new Map().set('splitAccessorId', "Women's Accessories"), }, ] as unknown as SeriesIdentifier[], }; wrapper = mountWithIntl(); expect(wrapper.find(EuiPopover).length).toBe(1); - expect(wrapper.find(EuiPopover).prop('title')).toEqual("Women's Accessories, filter options"); + expect(wrapper.find(EuiPopover).prop('title')).toEqual("Women's Accessories - Label B, filter options"); expect(wrapper.find(LegendActionPopover).prop('context')).toEqual({ data: [ { 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 68e5b89559933..2dcc0deb91e4f 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 @@ -13,13 +13,14 @@ import type { FilterEvent } from '../types'; import type { CommonXYDataLayerConfig } from '../../common'; import type { FormatFactory } from '../types'; import { LegendActionPopover } from './legend_action_popover'; -import { DatatablesWithFormatInfo, getFormat } from '../helpers'; +import { DatatablesWithFormatInfo, getSeriesName, LayersAccessorsTitles } from '../helpers'; export const getLegendAction = ( dataLayers: CommonXYDataLayerConfig[], onFilter: (data: FilterEvent['data']) => void, formatFactory: FormatFactory, - formattedDatatables: DatatablesWithFormatInfo + formattedDatatables: DatatablesWithFormatInfo, + titles: LayersAccessorsTitles ): LegendAction => React.memo(({ series: [xySeries] }) => { const series = xySeries as XYChartSeriesIdentifier; @@ -36,36 +37,31 @@ export const getLegendAction = ( } const layer = dataLayers[layerIndex]; - if (!layer || !layer.splitAccessor) { + if (!layer || !layer.splitAccessors || !layer.splitAccessors.length) { return null; } - const splitLabel = series.seriesKeys[0] as string; - const { table } = layer; - const accessor = getAccessorByDimension(layer.splitAccessor, table.columns); - const formatter = formatFactory( - accessor ? getFormat(table.columns, layer.splitAccessor) : undefined - ); - const rowIndex = table.rows.findIndex((row) => { - if (formattedDatatables[layer.layerId]?.formattedColumns[accessor]) { - // stringify the value to compare with the chart value - return formatter.convert(row[accessor]) === splitLabel; + const data: FilterEvent['data']['data'] = []; + + series.splitAccessors.forEach((value, key) => { + const rowIndex = table.rows.findIndex((row) => { + return row[key] === value; + }); + if (rowIndex !== -1) { + data.push({ + row: rowIndex, + column: table.columns.findIndex((column) => column.id === key), + value: table.rows[rowIndex][key], + table, + }); } - return row[accessor] === splitLabel; }); - if (rowIndex < 0) return null; - - const data = [ - { - row: rowIndex, - column: table.columns.findIndex((col) => col.id === accessor), - value: accessor ? table.rows[rowIndex][accessor] : splitLabel, - table, - }, - ]; + if (data.length === 0) { + return null; + } const context: FilterEvent['data'] = { data, @@ -74,9 +70,18 @@ export const getLegendAction = ( return ( ; + seriesSplitAccessors: Map; }): XYChartSeriesIdentifier => ({ - specId: generateSeriesId({ layerId, xAccessor, splitAccessor }, yAccessor), + specId: generateSeriesId({ layerId, xAccessor }, splitAccessors, yAccessor), yAccessor: yAccessor ?? 'a', - splitAccessors, + splitAccessors: seriesSplitAccessors, seriesKeys: [], key: '1', smVerticalAccessorValue: splitColumnAccessor, @@ -42,9 +42,11 @@ const getSeriesIdentifier = ({ describe('Tooltip', () => { const { data } = sampleArgs(); - const { layerId, xAccessor, splitAccessor, accessors } = sampleLayer; - const splitAccessors = new Map(); - splitAccessors.set(splitAccessor, '10'); + const { layerId, xAccessor, splitAccessors = [], accessors } = sampleLayer; + const seriesSplitAccessors = new Map(); + splitAccessors.forEach((splitAccessor) => { + seriesSplitAccessors.set(splitAccessor, '10'); + }); const accessor = accessors[0] as string; const splitRowAccessor = 'd'; @@ -54,8 +56,8 @@ describe('Tooltip', () => { layerId, yAccessor: accessor, xAccessor: xAccessor as string, - splitAccessor: splitAccessor as string, - splitAccessors, + splitAccessors: splitAccessors as string[], + seriesSplitAccessors, splitRowAccessor, splitColumnAccessor, }); @@ -74,7 +76,7 @@ describe('Tooltip', () => { [layerId]: { xTitles: { [xAccessor as string]: 'x-title' }, yTitles: { [accessor]: 'y-title' }, - splitSeriesTitles: { [splitAccessor as string]: 'split-series-title' }, + splitSeriesTitles: { [splitAccessors[0] as string]: 'split-series-title' }, splitRowTitles: { [splitRowAccessor]: 'split-row-title' }, splitColumnTitles: { [splitColumnAccessor]: 'split-column-title' }, }, @@ -84,7 +86,7 @@ describe('Tooltip', () => { [layerId]: { xAccessors: { [xAccessor as string]: { id: 'number' } }, yAccessors: { [accessor]: { id: 'string' } }, - splitSeriesAccessors: { [splitAccessor as string]: { id: 'date' } }, + splitSeriesAccessors: { [splitAccessors[0] as string]: { id: 'date' } }, splitRowAccessors: { [splitRowAccessor]: { id: 'number' } }, splitColumnAccessors: { [splitColumnAccessor]: { id: 'number' } }, }, @@ -188,8 +190,8 @@ describe('Tooltip', () => { const seriesIdentifierWithoutX = getSeriesIdentifier({ layerId, yAccessor: accessor, - splitAccessor: splitAccessor as string, - splitAccessors, + splitAccessors: splitAccessors as string[], + seriesSplitAccessors, splitRowAccessor, splitColumnAccessor, }); @@ -215,8 +217,8 @@ describe('Tooltip', () => { const seriesIdentifierWithoutY = getSeriesIdentifier({ layerId, xAccessor: xAccessor as string, - splitAccessor: splitAccessor as string, - splitAccessors, + splitAccessors: splitAccessors as string[], + seriesSplitAccessors, splitRowAccessor, splitColumnAccessor, }); @@ -243,7 +245,8 @@ describe('Tooltip', () => { layerId, xAccessor: xAccessor as string, yAccessor: accessor, - splitAccessors: new Map(), + splitAccessors: splitAccessors as string[], + seriesSplitAccessors: new Map(), splitRowAccessor, splitColumnAccessor, }); @@ -270,8 +273,8 @@ describe('Tooltip', () => { layerId, xAccessor: xAccessor as string, yAccessor: accessor, - splitAccessor: splitAccessor as string, - splitAccessors, + splitAccessors: splitAccessors as string[], + seriesSplitAccessors, splitColumnAccessor, }); @@ -297,8 +300,8 @@ describe('Tooltip', () => { layerId, xAccessor: xAccessor as string, yAccessor: accessor, - splitAccessor: splitAccessor as string, - splitAccessors, + splitAccessors: splitAccessors as string[], + seriesSplitAccessors, splitRowAccessor, }); 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 c8832b3654dbe..7fd010d696c88 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 @@ -159,7 +159,7 @@ describe('XYChart component', () => { xAccessor: 'c', accessors: ['a', 'b'], showLines: true, - splitAccessor: 'd', + splitAccessors: ['d'], columnToLabel: '{"a": "Label A", "b": "Label B", "d": "Label D"}', xScaleType: 'time', isHistogram: false, @@ -261,7 +261,7 @@ describe('XYChart component', () => { isPercentage: false, xAccessor: 'c', accessors: ['a', 'b'], - splitAccessor: 'd', + splitAccessors: ['d'], columnToLabel: '{"a": "Label A", "b": "Label B", "d": "Label D"}', xScaleType: 'time', isHistogram: true, @@ -405,7 +405,7 @@ describe('XYChart component', () => { seriesType: 'line', xScaleType: 'time', isHistogram: true, - splitAccessor: undefined, + splitAccessors: undefined, table: newData, } as DataLayerConfig, ], @@ -1068,7 +1068,7 @@ describe('XYChart component', () => { key: 'spec{d}yAccessor{d}splitAccessors{b-2}', specId: 'd', yAccessor: 'd', - splitAccessors: {}, + splitAccessors: new Map().set('b', 2), seriesKeys: [2, 'd'], }; @@ -1092,7 +1092,7 @@ describe('XYChart component', () => { showLines: true, xAccessor: 'b', xScaleType: 'time', - splitAccessor: 'b', + splitAccessors: ['b'], accessors: ['d'], columnToLabel: '{"a": "Label A", "b": "Label B", "d": "Label D"}', palette: mockPaletteOutput, @@ -1500,7 +1500,7 @@ describe('XYChart component', () => { { ...(args.layers[0] as DataLayerConfig), xAccessor: undefined, - splitAccessor: 'e', + splitAccessors: ['e'], seriesType: 'bar', isStacked: true, }, @@ -1530,7 +1530,7 @@ describe('XYChart component', () => { seriesType: 'bar', isHistogram: true, } as DataLayerConfig; - delete firstLayer.splitAccessor; + delete firstLayer.splitAccessors; const component = shallow( ); @@ -1546,7 +1546,7 @@ describe('XYChart component', () => { seriesType: 'bar', isHistogram: true, } as DataLayerConfig; - delete firstLayer.splitAccessor; + delete firstLayer.splitAccessors; const component = shallow( ); @@ -1563,13 +1563,13 @@ describe('XYChart component', () => { seriesType: 'line', isHistogram: true, } as DataLayerConfig; - delete firstLayer.splitAccessor; + delete firstLayer.splitAccessors; const secondLayer: DataLayerConfig = { ...args.layers[0], seriesType: 'line', isHistogram: true, } as DataLayerConfig; - delete secondLayer.splitAccessor; + delete secondLayer.splitAccessors; const component = shallow( ); @@ -1754,7 +1754,7 @@ describe('XYChart component', () => { ...layer, type: 'extendedDataLayer', accessors: ['a', 'b'], - splitAccessor: undefined, + splitAccessors: undefined, decorations: [ { type: 'dataDecorationConfig', @@ -1773,7 +1773,7 @@ describe('XYChart component', () => { ...layer, type: 'extendedDataLayer', accessors: ['c'], - splitAccessor: undefined, + splitAccessors: undefined, decorations: [ { type: 'dataDecorationConfig', @@ -1838,12 +1838,14 @@ describe('XYChart component', () => { (lineSeries.at(0).prop('color') as Function)!({ yAccessor: 'a', seriesKeys: ['a'], + splitAccessors: new Map(), }) ).toEqual('blue'); expect( (lineSeries.at(1).prop('color') as Function)!({ yAccessor: 'c', seriesKeys: ['c'], + splitAccessors: new Map(), }) ).toEqual('blue'); }); @@ -1972,7 +1974,7 @@ describe('XYChart component', () => { { ...args.layers[0], accessors: ['a'], - splitAccessor: 'd', + splitAccessors: ['d'], columnToLabel: '{"a": "Label A"}', table: dataWithoutFormats, }, @@ -1986,7 +1988,16 @@ describe('XYChart component', () => { .find(LineSeries) .prop('name') as SeriesNameFn; - expect(nameFn({ ...nameFnArgs, seriesKeys: ['split1', 'a'] }, false)).toEqual('split1'); + expect( + nameFn( + { + ...nameFnArgs, + seriesKeys: ['split1', 'a'], + splitAccessors: nameFnArgs.splitAccessors.set('d', 'split1'), + }, + false + ) + ).toEqual('split1'); }); test('split series with formatting and single y accessor', () => { @@ -1997,7 +2008,7 @@ describe('XYChart component', () => { { ...args.layers[0], accessors: ['a'], - splitAccessor: 'd', + splitAccessors: ['d'], columnToLabel: '{"a": "Label A"}', table: dataWithFormats, }, @@ -2012,7 +2023,16 @@ describe('XYChart component', () => { .prop('name') as SeriesNameFn; convertSpy.mockReturnValueOnce('formatted'); - expect(nameFn({ ...nameFnArgs, seriesKeys: ['split1', 'a'] }, false)).toEqual('formatted'); + expect( + nameFn( + { + ...nameFnArgs, + seriesKeys: ['split1', 'a'], + splitAccessors: nameFnArgs.splitAccessors.set('d', 'split1'), + }, + false + ) + ).toEqual('formatted'); expect(getFormatSpy).toHaveBeenCalledWith({ id: 'custom' }); }); @@ -2024,7 +2044,7 @@ describe('XYChart component', () => { { ...args.layers[0], accessors: ['a', 'b'], - splitAccessor: 'd', + splitAccessors: ['d'], columnToLabel: '{"a": "Label A","b": "Label B"}', table: dataWithoutFormats, }, @@ -2037,12 +2057,26 @@ describe('XYChart component', () => { 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' - ); - expect(nameFn2({ ...nameFnArgs, seriesKeys: ['split1', 'b'] }, false)).toEqual( - 'split1 - Label B' - ); + expect( + nameFn1( + { + ...nameFnArgs, + seriesKeys: ['split1', 'a'], + splitAccessors: nameFnArgs.splitAccessors.set('d', 'split1'), + }, + false + ) + ).toEqual('split1 - Label A'); + expect( + nameFn2( + { + ...nameFnArgs, + seriesKeys: ['split1', 'b'], + splitAccessors: nameFnArgs.splitAccessors.set('d', 'split1'), + }, + false + ) + ).toEqual('split1 - Label B'); }); test('split series with formatting with multiple y accessors', () => { @@ -2053,7 +2087,7 @@ describe('XYChart component', () => { { ...args.layers[0], accessors: ['a', 'b'], - splitAccessor: 'd', + splitAccessors: ['d'], columnToLabel: '{"a": "Label A","b": "Label B"}', table: dataWithFormats, }, @@ -2067,12 +2101,26 @@ describe('XYChart component', () => { const nameFn2 = 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' - ); + expect( + nameFn1( + { + ...nameFnArgs, + seriesKeys: ['split1', 'a'], + splitAccessors: nameFnArgs.splitAccessors.set('d', 'split1'), + }, + false + ) + ).toEqual('formatted1 - Label A'); + expect( + nameFn2( + { + ...nameFnArgs, + seriesKeys: ['split1', 'b'], + splitAccessors: nameFnArgs.splitAccessors.set('d', 'split1'), + }, + false + ) + ).toEqual('formatted2 - Label B'); }); }); @@ -2431,7 +2479,7 @@ describe('XYChart component', () => { showLines: true, xAccessor: 'a', accessors: ['c'], - splitAccessor: 'b', + splitAccessors: ['b'], columnToLabel: '', xScaleType: 'ordinal', isHistogram: false, @@ -2447,7 +2495,7 @@ describe('XYChart component', () => { showLines: true, xAccessor: 'a', accessors: ['c'], - splitAccessor: 'b', + splitAccessors: ['b'], columnToLabel: '', xScaleType: 'ordinal', isHistogram: false, @@ -2534,7 +2582,7 @@ describe('XYChart component', () => { seriesType: 'line', xAccessor: 'a', accessors: ['c'], - splitAccessor: 'b', + splitAccessors: ['b'], columnToLabel: '', xScaleType: 'ordinal', isHistogram: false, @@ -2619,7 +2667,7 @@ describe('XYChart component', () => { showLines: true, xAccessor: 'a', accessors: ['c'], - splitAccessor: 'b', + splitAccessors: ['b'], columnToLabel: '', xScaleType: 'ordinal', isHistogram: false, @@ -2649,7 +2697,7 @@ describe('XYChart component', () => { { ...(args.layers[0] as DataLayerConfig), accessors: ['a'], - splitAccessor: undefined, + splitAccessors: undefined, }, ], legend: { ...args.legend, isVisible: true, showSingleSeries: true }, @@ -2672,7 +2720,7 @@ describe('XYChart component', () => { { ...(args.layers[0] as DataLayerConfig), accessors: ['a'], - splitAccessor: undefined, + splitAccessors: undefined, }, ], legend: { ...args.legend, isVisible: true, isInside: true }, 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 47d78b474efcd..e1439f4226592 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 @@ -232,7 +232,9 @@ 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) => isDataLayer(layer) && layer.splitAccessors && layer.splitAccessors.length + ); const shouldRotate = isHorizontalChart(dataLayers); const yAxesConfiguration = getAxesConfiguration( @@ -261,7 +263,9 @@ export function XYChart({ const chartHasMoreThanOneBarSeries = filteredBarLayers.length > 1 || filteredBarLayers.some((layer) => layer.accessors.length > 1) || - filteredBarLayers.some((layer) => isDataLayer(layer) && layer.splitAccessor); + filteredBarLayers.some( + (layer) => isDataLayer(layer) && layer.splitAccessors && layer.splitAccessors.length + ); const isTimeViz = isTimeChart(dataLayers); @@ -316,7 +320,7 @@ export function XYChart({ .map((config) => ({ ...config, position: config - ? getAxisGroupForReferenceLine(yAxesConfiguration, config)?.position + ? getAxisGroupForReferenceLine(yAxesConfiguration, config)?.position ?? Position.Bottom : Position.Bottom, })), ...groupedLineAnnotations, @@ -468,27 +472,15 @@ export function XYChart({ ]; if (xySeries.seriesKeys.length > 1) { - const pointValue = xySeries.seriesKeys[0]; - const splitAccessor = layer.splitAccessor - ? getAccessorByDimension(layer.splitAccessor, table.columns) - : undefined; - - const splitFormat = splitAccessor - ? fieldFormats[layer.layerId].splitSeriesAccessors[splitAccessor] - : undefined; - const splitFormatter = formatFactory(splitFormat); - - points.push({ - row: table.rows.findIndex((row) => { - if (splitAccessor) { - if (formattedDatatables[layer.layerId]?.formattedColumns[splitAccessor]) { - return splitFormatter.convert(row[splitAccessor]) === pointValue; - } - return row[splitAccessor] === pointValue; - } - }), - column: table.columns.findIndex((col) => col.id === splitAccessor), - value: pointValue, + xySeries.splitAccessors.forEach((value, key) => { + const rowIndex = table.rows.findIndex((row) => { + return row[key] === value; + }); + points.push({ + row: rowIndex, + column: table.columns.findIndex((column) => column.id === key), + value: table.rows[rowIndex][key], + }); }); } const context: FilterEvent['data'] = { @@ -665,7 +657,7 @@ export function XYChart({ onElementClick={interactive ? clickHandler : undefined} legendAction={ interactive - ? getLegendAction(dataLayers, onClickValue, formatFactory, formattedDatatables) + ? getLegendAction(dataLayers, onClickValue, formatFactory, formattedDatatables, titles) : undefined } showLegendExtra={isHistogramViz && valuesInLegend} @@ -696,9 +688,9 @@ export function XYChart({ gridLine={gridLineStyle} hide={xAxisConfig?.hide || dataLayers[0]?.hide || !dataLayers[0]?.xAccessor} tickFormat={(d) => { - let value = safeXAccessorLabelRenderer(d) || ''; + const value = safeXAccessorLabelRenderer(d) || ''; if (xAxisConfig?.truncate && value.length > xAxisConfig.truncate) { - value = `${value.slice(0, xAxisConfig.truncate)}...`; + return `${value.slice(0, xAxisConfig.truncate)}...`; } return value; }} @@ -729,9 +721,9 @@ export function XYChart({ }} hide={axis.hide || dataLayers[0]?.hide} tickFormat={(d) => { - let value = axis.formatter?.convert(d) || ''; + const value = axis.formatter?.convert(d) || ''; if (axis.truncate && value.length > axis.truncate) { - value = `${value.slice(0, axis.truncate)}...`; + return `${value.slice(0, axis.truncate)}...`; } return value; }} @@ -752,7 +744,7 @@ export function XYChart({ histogramMode={dataLayers.every( (layer) => layer.isHistogram && - (layer.isStacked || !layer.splitAccessor) && + (layer.isStacked || !layer.splitAccessors || !layer.splitAccessors.length) && (layer.isStacked || layer.seriesType !== SeriesTypes.BAR || !chartHasMoreThanOneBarSeries) 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 60ae601545e88..b3557c1cc97c8 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 @@ -236,7 +236,7 @@ describe('axes_configuration', () => { seriesType: 'line', xAccessor: 'c', accessors: ['yAccessorId'], - splitAccessor: 'd', + splitAccessors: ['d'], columnToLabel: '{"a": "Label A", "b": "Label B", "d": "Label D"}', xScaleType: 'ordinal', isHistogram: 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 c1405a2639f5e..bbc9fe5c33c12 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 @@ -61,7 +61,7 @@ describe('color_assignment', () => { isHorizontal: false, palette: { type: 'palette', name: 'palette1' }, layerType: LayerTypes.DATA, - splitAccessor: 'split1', + splitAccessors: ['split1'], accessors: ['y1', 'y2'], table: tables['1'], }, @@ -77,7 +77,7 @@ describe('color_assignment', () => { isHorizontal: false, palette: { type: 'palette', name: 'palette2' }, layerType: LayerTypes.DATA, - splitAccessor: 'split2', + splitAccessors: ['split2'], accessors: ['y3', 'y4'], table: tables['2'], }, @@ -92,7 +92,14 @@ describe('color_assignment', () => { describe('totalSeriesCount', () => { it('should calculate total number of series per palette', () => { - const assignments = getColorAssignments(layers, formatFactory); + const assignments = getColorAssignments(layers, formatFactory, { + yTitles: { + y1: 'test1', + y2: 'test2', + y3: 'test3', + y4: 'test4', + }, + }); // two y accessors, with 3 splitted series expect(assignments.palette1.totalSeriesCount).toEqual(2 * 3); expect(assignments.palette2.totalSeriesCount).toEqual(2 * 3); @@ -101,7 +108,15 @@ describe('color_assignment', () => { it('should calculate total number of series spanning multible layers', () => { const assignments = getColorAssignments( [layers[0], { ...layers[1], palette: layers[0].palette }], - formatFactory + formatFactory, + { + yTitles: { + y1: 'test1', + y2: 'test2', + y3: 'test3', + y4: 'test4', + }, + } ); // two y accessors, with 3 splitted series, two times expect(assignments.palette1.totalSeriesCount).toEqual(2 * 3 + 2 * 3); @@ -110,8 +125,16 @@ 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 }], - formatFactory + [layers[0], { ...layers[1], palette: layers[0].palette, splitAccessors: undefined }], + formatFactory, + { + yTitles: { + y1: 'test1', + y2: 'test2', + y3: 'test3', + y4: 'test4', + }, + } ); // 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); @@ -120,7 +143,7 @@ describe('color_assignment', () => { it('should format non-primitive values and count them correctly', () => { const complexObject = { aProp: 123 }; - const formatMock = jest.fn((x) => 'formatted'); + const formatMock = jest.fn((value) => (typeof value === 'object' ? 'formatted' : value)); const newLayers = [ { ...layers[0], @@ -134,25 +157,31 @@ describe('color_assignment', () => { (() => ({ convert: formatMock, - } as unknown)) as FormatFactory + } as unknown)) as FormatFactory, + { + yTitles: { + y1: 'test1', + y2: 'test2', + y3: 'test3', + y4: 'test4', + }, + } ); 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.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 newLayers = [{ ...layers[0], table: { ...tables['1'], columns: [] } }, layers[1]]; - const assignments = getColorAssignments(newLayers, formatFactory); + const assignments = getColorAssignments(newLayers, formatFactory, { + yTitles: { + y1: 'test1', + y2: 'test2', + y3: 'test3', + y4: 'test4', + }, + }); // if the split column is missing, just assume a single split expect(assignments.palette1.totalSeriesCount).toEqual(2); @@ -161,32 +190,53 @@ describe('color_assignment', () => { describe('getRank', () => { it('should return the correct rank for a series key', () => { - const assignments = getColorAssignments(layers, formatFactory); + const assignments = getColorAssignments(layers, formatFactory, { + yTitles: { + y1: 'test1', + y2: 'test2', + y3: 'test3', + y4: 'test4', + }, + }); // 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], '2 - test2')).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 - test4')).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); + const assignments = getColorAssignments(newLayers, formatFactory, { + yTitles: { + y1: 'test1', + y2: 'test2', + y3: 'test3', + y4: 'test4', + }, + }); // 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], '2 - test2')).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], '2 - test3')).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 }, + { ...layers[1], palette: layers[0].palette, splitAccessors: undefined }, ]; - const assignments = getColorAssignments(newLayers, formatFactory); + const assignments = getColorAssignments(newLayers, formatFactory, { + yTitles: { + y1: 'test1', + y2: 'test2', + y3: 'test3', + y4: 'test4', + }, + }); // 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], '2 - test2')).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], 'test4')).toEqual(7); }); it('should return the correct rank for a series with a non-primitive value', () => { @@ -202,29 +252,35 @@ describe('color_assignment', () => { newLayers, (() => ({ - convert: () => 'formatted', - } as unknown)) as FormatFactory + convert: (value: unknown) => (typeof value === 'object' ? 'formatted' : value), + } as unknown)) as FormatFactory, + { + yTitles: { + y1: 'test1', + y2: 'test2', + y3: 'test3', + y4: 'test4', + }, + } ); // 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.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], 'formatted - test1')).toEqual(2); }); it('should handle missing columns', () => { const newLayers = [{ ...layers[0], table: { ...tables['1'], columns: [] } }, layers[1]]; - const assignments = getColorAssignments(newLayers, formatFactory); + const assignments = getColorAssignments(newLayers, formatFactory, { + yTitles: { + y1: 'test1', + y2: 'test2', + y3: 'test3', + y4: 'test4', + }, + }); // 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], 'test2')).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 0b7f8d8b08f22..150448cfca853 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 @@ -6,15 +6,18 @@ * Side Public License, v 1. */ -import { uniq, mapValues } from 'lodash'; +import { mapValues } from 'lodash'; +import { Datatable } from '@kbn/expressions-plugin'; import { euiLightVars } from '@kbn/ui-theme'; -import { getAccessorByDimension } from '@kbn/visualizations-plugin/common/utils'; +import { + getAccessorByDimension, + getColumnByAccessor, +} from '@kbn/visualizations-plugin/common/utils'; import { FormatFactory } from '../types'; import { isDataLayer } from './visualization'; import { CommonXYDataLayerConfig, CommonXYLayerConfig } from '../../common'; import { getFormat } from './format'; - -const isPrimitive = (value: unknown): boolean => value != null && typeof value !== 'object'; +import { LayerAccessorsTitles } from './layers'; export const defaultReferenceLineColor = euiLightVars.euiColorDarkShade; @@ -22,13 +25,64 @@ export type ColorAssignments = Record< string, { totalSeriesCount: number; - getRank(sortedLayer: CommonXYDataLayerConfig, seriesKey: string, yAccessor: string): number; + getRank(sortedLayer: CommonXYDataLayerConfig, seriesName: string): number; } >; +export const getAllSeries = ( + table: Datatable, + splitAccessors: CommonXYDataLayerConfig['splitAccessors'] = [], + accessors: CommonXYDataLayerConfig['accessors'], + columnToLabel: CommonXYDataLayerConfig['columnToLabel'], + titles: LayerAccessorsTitles, + formatFactory: FormatFactory +) => { + const allSeries: string[] = []; + if (!table) { + return []; + } + + const columnToLabelMap = columnToLabel ? JSON.parse(columnToLabel) : {}; + + table.rows.forEach((row) => { + let seriesName = ''; + splitAccessors.forEach((accessor) => { + const splitColumn = getColumnByAccessor(accessor, table.columns); + if (!splitColumn) return; + const splitAccessor = getAccessorByDimension(accessor, table.columns); + const columnFormatter = + splitAccessor && formatFactory(getFormat(table.columns, splitAccessor)); + const name = columnFormatter + ? columnFormatter.convert(row[splitAccessor]) + : row[splitAccessor]; + if (seriesName) { + seriesName += ` - ${name}`; + } else { + seriesName = name; + } + }); + + accessors.forEach((accessor) => { + const yAccessor = getAccessorByDimension(accessor, table.columns); + const yTitle = columnToLabelMap[yAccessor] ?? titles?.yTitles?.[yAccessor] ?? null; + let name; + if (seriesName) { + name = accessors.length > 1 ? `${seriesName} - ${yTitle}` : seriesName; + } else { + name = yTitle; + } + if (!allSeries.includes(name)) { + allSeries.push(name); + } + }); + }); + return allSeries; +}; + export function getColorAssignments( layers: CommonXYLayerConfig[], - formatFactory: FormatFactory + formatFactory: FormatFactory, + titles: LayerAccessorsTitles ): ColorAssignments { const layersPerPalette: Record = {}; @@ -46,28 +100,17 @@ export function getColorAssignments( return mapValues(layersPerPalette, (paletteLayers) => { const seriesPerLayer = paletteLayers.map((layer) => { - if (!layer.splitAccessor) { - return { numberOfSeries: layer.accessors.length, splits: [] }; - } - const splitAccessor = getAccessorByDimension(layer.splitAccessor, layer.table.columns); - const column = layer.table.columns?.find(({ id }) => id === splitAccessor); - const columnFormatter = - column && formatFactory(getFormat(layer.table.columns, layer.splitAccessor)); - const splits = - !column || !layer.table - ? [] - : uniq( - layer.table.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 allSeries = + getAllSeries( + layer.table, + layer.splitAccessors, + layer.accessors, + layer.columnToLabel, + titles, + formatFactory + ) || []; + + return { numberOfSeries: allSeries.length, allSeries }; }); const totalSeriesCount = seriesPerLayer.reduce( (sum, perLayer) => sum + perLayer.numberOfSeries, @@ -75,24 +118,19 @@ export function getColorAssignments( ); return { totalSeriesCount, - getRank(sortedLayer: CommonXYDataLayerConfig, seriesKey: string, yAccessor: string) { + getRank(sortedLayer: CommonXYDataLayerConfig, seriesName: string) { const layerIndex = paletteLayers.findIndex( (layer) => sortedLayer.layerId === layer.layerId ); const currentSeriesPerLayer = seriesPerLayer[layerIndex]; - const splitRank = currentSeriesPerLayer.splits.indexOf(seriesKey); + const rank = currentSeriesPerLayer.allSeries.indexOf(seriesName); 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.findIndex( - (accessor) => getAccessorByDimension(accessor, sortedLayer.table.columns) === yAccessor - ) + (rank !== -1 ? rank : 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 bdb3a812c72bb..9087d943c3476 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 @@ -18,12 +18,9 @@ import { XYChartSeriesIdentifier, } from '@elastic/charts'; import { i18n } from '@kbn/i18n'; -import { FieldFormat, IFieldFormat, SerializedFieldFormat } from '@kbn/field-formats-plugin/common'; +import { IFieldFormat } from '@kbn/field-formats-plugin/common'; import { Datatable } from '@kbn/expressions-plugin'; -import { - getFormatByAccessor, - getAccessorByDimension, -} from '@kbn/visualizations-plugin/common/utils'; +import { getAccessorByDimension } from '@kbn/visualizations-plugin/common/utils'; import type { ExpressionValueVisDimension } from '@kbn/visualizations-plugin/common/expression_functions'; import { PaletteRegistry, SeriesLayer } from '@kbn/coloring'; import { CommonXYDataLayerConfig, XScaleType } from '../../common'; @@ -58,10 +55,10 @@ type GetSeriesPropsFn = (config: { type GetSeriesNameFn = ( data: XYChartSeriesIdentifier, config: { - splitColumnId?: string; + splitAccessors: Array; accessorsCount: number; - splitHint: SerializedFieldFormat | undefined; - splitFormatter: FieldFormat; + columns: Datatable['columns']; + formatFactory: FormatFactory; alreadyFormattedColumns: Record; columnToLabelMap: Record; }, @@ -74,11 +71,10 @@ type GetColorFn = ( layer: CommonXYDataLayerConfig; accessor: string; colorAssignments: ColorAssignments; - columnToLabelMap: Record; paletteService: PaletteRegistry; + getSeriesNameFn: (d: XYChartSeriesIdentifier) => SeriesName; syncColors?: boolean; - }, - titles: LayerAccessorsTitles + } ) => string | null; type GetPointConfigFn = (config: { @@ -185,13 +181,16 @@ export const getFormattedTablesByLayers = ( formatFactory: FormatFactory ): DatatablesWithFormatInfo => layers.reduce( - (formattedDatatables, { layerId, table, xAccessor, splitAccessor, accessors, xScaleType }) => ({ + ( + formattedDatatables, + { layerId, table, xAccessor, splitAccessors = [], accessors, xScaleType } + ) => ({ ...formattedDatatables, [layerId]: getFormattedTable( table, formatFactory, xAccessor, - [xAccessor, splitAccessor, ...accessors].filter( + [xAccessor, ...splitAccessors, ...accessors].filter( (a): a is string | ExpressionValueVisDimension => a !== undefined ), xScaleType @@ -200,13 +199,41 @@ export const getFormattedTablesByLayers = ( {} ); -const getSeriesName: GetSeriesNameFn = ( +function getSplitValues( + splitAccessorsMap: XYChartSeriesIdentifier['splitAccessors'], + splitAccessors: Array, + alreadyFormattedColumns: Record, + columns: Datatable['columns'], + formatFactory: FormatFactory +) { + if (splitAccessorsMap.size < 0) { + return []; + } + + const splitValues: Array = []; + splitAccessorsMap.forEach((value, key) => { + const split = splitAccessors.find( + (accessor) => getAccessorByDimension(accessor, columns) === key + ); + if (split) { + const splitColumnId = getAccessorByDimension(split, columns); + const splitFormatByAccessor = getFormat(columns, split); + const splitFormatter = formatFactory(splitFormatByAccessor); + splitValues.push( + alreadyFormattedColumns[splitColumnId] ? value : splitFormatter.convert(value) + ); + } + }); + return splitValues; +} + +export const getSeriesName: GetSeriesNameFn = ( data, { - splitColumnId, + splitAccessors, accessorsCount, - splitHint, - splitFormatter, + columns, + formatFactory, alreadyFormattedColumns, columnToLabelMap, }, @@ -215,40 +242,26 @@ const getSeriesName: GetSeriesNameFn = ( // For multiple y series, the name of the operation is used on each, either: // * Key - Y name // * Formatted value - Y name - if (splitColumnId && accessorsCount > 1) { - const formatted = alreadyFormattedColumns[splitColumnId]; - const result = data.seriesKeys - .map((key: string | number, i) => { - if (i === 0 && splitHint && splitColumnId && !formatted) { - return splitFormatter.convert(key); - } - return splitColumnId && i === 0 - ? key - : columnToLabelMap[key] ?? - titles?.yTitles?.[key] ?? - titles?.splitSeriesTitles?.[key] ?? - null; - }) - .join(' - '); - return result; - } + const splitValues = getSplitValues( + data.splitAccessors, + splitAccessors, + alreadyFormattedColumns, + columns, + formatFactory + ); + + const key = data.seriesKeys[data.seriesKeys.length - 1]; + const yAccessorTitle = columnToLabelMap[key] ?? titles?.yTitles?.[key] ?? null; - // For formatted split series, format the key - // This handles splitting by dates, for example - if (splitHint) { - if (splitColumnId && alreadyFormattedColumns[splitColumnId]) { - return data.seriesKeys[0]; + if (accessorsCount > 1) { + if (splitValues.length === 0) { + return yAccessorTitle; } - return splitFormatter.convert(data.seriesKeys[0]); + return `${splitValues.join(' - ')}${yAccessorTitle ? ' - ' + yAccessorTitle : ''}`; } - // 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 splitColumnId - ? data.seriesKeys[0] - : columnToLabelMap[data.seriesKeys[0]] ?? titles?.yTitles?.[data.seriesKeys[0]] ?? null; + return splitValues.length > 0 ? splitValues.join(' - ') : yAccessorTitle; }; const getPointConfig: GetPointConfigFn = ({ @@ -276,9 +289,8 @@ const getLineConfig: GetLineConfigFn = ({ showLines, lineWidth }) => ({ }); const getColor: GetColorFn = ( - { yAccessor, seriesKeys }, - { layer, accessor, colorAssignments, columnToLabelMap, paletteService, syncColors }, - titles + series, + { layer, accessor, colorAssignments, paletteService, syncColors, getSeriesNameFn } ) => { const overwriteColor = getSeriesColor(layer, accessor); if (overwriteColor !== null) { @@ -286,13 +298,13 @@ const getColor: GetColorFn = ( } const colorAssignment = colorAssignments[layer.palette.name]; + const name = getSeriesNameFn(series)?.toString() || ''; + const seriesLayers: SeriesLayer[] = [ { - name: layer.splitAccessor - ? String(seriesKeys[0]) - : columnToLabelMap[seriesKeys[0]] ?? titles?.yTitles?.[seriesKeys[0]] ?? null, + name, totalSeriesAtDepth: colorAssignment.totalSeriesCount, - rankAtDepth: colorAssignment.getRank(layer, String(seriesKeys[0]), String(yAccessor)), + rankAtDepth: colorAssignment.getRank(layer, name), }, ]; return paletteService.get(layer.palette.name).getCategoricalColor( @@ -311,27 +323,21 @@ const EMPTY_ACCESSOR = '-'; const SPLIT_CHAR = '.'; export const generateSeriesId = ( - { - layerId, - xAccessor, - splitAccessor, - }: Pick, + { layerId, xAccessor }: Pick, + splitColumnIds: string[], accessor?: string ) => - [ - layerId, - xAccessor ?? EMPTY_ACCESSOR, - accessor ?? EMPTY_ACCESSOR, - splitAccessor ?? EMPTY_ACCESSOR, - ].join(SPLIT_CHAR); + [layerId, xAccessor ?? EMPTY_ACCESSOR, accessor ?? EMPTY_ACCESSOR, ...splitColumnIds].join( + SPLIT_CHAR + ); export const getMetaFromSeriesId = (seriesId: string) => { - const [layerId, xAccessor, yAccessor, splitAccessor] = seriesId.split(SPLIT_CHAR); + const [layerId, xAccessor, yAccessor, ...splitAccessors] = seriesId.split(SPLIT_CHAR); return { layerId, xAccessor: xAccessor === EMPTY_ACCESSOR ? undefined : xAccessor, yAccessor, - splitAccessor: splitAccessor === EMPTY_ACCESSOR ? undefined : splitAccessor, + splitAccessor: splitAccessors[0] === EMPTY_ACCESSOR ? undefined : splitAccessors, }; }; @@ -361,18 +367,17 @@ export const getSeriesProps: GetSeriesPropsFn = ({ const scaleType = yAxis?.scaleType || ScaleType.Linear; const isBarChart = layer.seriesType === SeriesTypes.BAR; const xColumnId = layer.xAccessor && getAccessorByDimension(layer.xAccessor, table.columns); - const splitColumnId = - layer.splitAccessor && getAccessorByDimension(layer.splitAccessor, table.columns); + const splitColumnIds = layer.splitAccessors + ? layer.splitAccessors.map((splitAccessor) => { + return getAccessorByDimension(splitAccessor, table.columns); + }) + : []; const enableHistogramMode = layer.isHistogram && - (isStacked || !layer.splitAccessor) && + (isStacked || !splitColumnIds.length) && (isStacked || !isBarChart || !chartHasMoreThanOneBarSeries); const formatter = table?.columns.find((column) => column.id === accessor)?.meta?.params; - const splitHint = layer.splitAccessor - ? getFormatByAccessor(layer.splitAccessor, table.columns) - : undefined; - const splitFormatter = formatFactory(splitHint); const markSizeColumnId = markSizeAccessor ? getAccessorByDimension(markSizeAccessor, table.columns) @@ -393,8 +398,8 @@ export const getSeriesProps: GetSeriesPropsFn = ({ (row) => !(xColumnId && typeof row[xColumnId] === 'undefined') && !( - splitColumnId && - typeof row[splitColumnId] === 'undefined' && + splitColumnIds.length && + splitColumnIds.some((splitColumnId) => typeof row[splitColumnId] === 'undefined') && typeof row[accessor] === 'undefined' ) ); @@ -408,10 +413,29 @@ export const getSeriesProps: GetSeriesPropsFn = ({ })); } + const getSeriesNameFn = (d: XYChartSeriesIdentifier) => { + return getSeriesName( + d, + { + splitAccessors: layer.splitAccessors || [], + accessorsCount: layer.accessors.length, + alreadyFormattedColumns: formattedColumns, + columns: formattedTable.columns, + formatFactory, + columnToLabelMap, + }, + titles + ); + }; + return { - splitSeriesAccessors: splitColumnId ? [splitColumnId] : [], + splitSeriesAccessors: splitColumnIds.length ? splitColumnIds : [], stackAccessors: isStacked ? [layer.xAccessor as string] : [], - id: generateSeriesId(layer, accessor), + id: generateSeriesId( + layer, + splitColumnIds.length ? splitColumnIds : [EMPTY_ACCESSOR], + accessor + ), xAccessor: xColumnId || 'unifiedX', yAccessors: [accessor], markSizeAccessor: markSizeColumnId, @@ -423,18 +447,14 @@ export const getSeriesProps: GetSeriesPropsFn = ({ ? ScaleType.LinearBinary : scaleType, color: (series) => - getColor( - series, - { - layer, - accessor, - colorAssignments, - columnToLabelMap, - paletteService, - syncColors, - }, - titles - ), + getColor(series, { + layer, + accessor, + colorAssignments, + paletteService, + getSeriesNameFn, + syncColors, + }), groupId: yAxis?.groupId, enableHistogramMode, stackMode, @@ -468,18 +488,7 @@ export const getSeriesProps: GetSeriesPropsFn = ({ line: getLineConfig({ lineWidth: layer.lineWidth, showLines: layer.showLines }), }, name(d) { - return getSeriesName( - d, - { - splitColumnId, - accessorsCount: layer.accessors.length, - splitHint, - splitFormatter, - alreadyFormattedColumns: formattedColumns, - columnToLabelMap, - }, - titles - ); + return getSeriesNameFn(d); }, }; }; 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 eba7cf7c8a9f3..9cdf76382de15 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/layers.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/layers.ts @@ -63,7 +63,7 @@ export function getFilteredLayers(layers: CommonXYLayerConfig[]) { let table: Datatable | undefined; let accessors: Array = []; let xAccessor: undefined | string | number; - let splitAccessor: undefined | string | number; + let splitAccessors: string[] = []; if (isDataLayer(layer) || isReferenceLayer(layer)) { table = layer.table; @@ -73,10 +73,11 @@ export function getFilteredLayers(layers: CommonXYLayerConfig[]) { if (isDataLayer(layer)) { xAccessor = layer.xAccessor && table && getAccessorByDimension(layer.xAccessor, table.columns); - splitAccessor = - layer.splitAccessor && - table && - getAccessorByDimension(layer.splitAccessor, table.columns); + splitAccessors = table + ? layer.splitAccessors?.map((splitAccessor) => + getAccessorByDimension(splitAccessor, table!.columns) + ) || [] + : []; } return !( @@ -87,8 +88,10 @@ export function getFilteredLayers(layers: CommonXYLayerConfig[]) { table.rows.every((row) => xAccessor && 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) => splitAccessor && typeof row[splitAccessor] === 'undefined')) + splitAccessors.length && + table.rows.every((row) => + splitAccessors.every((splitAccessor) => typeof row[splitAccessor] === 'undefined') + )) ); } ); @@ -125,10 +128,11 @@ const getYAccessorWithFieldFormat = ( }; export const getLayerFormats = ( - { xAccessor, accessors, splitAccessor, table, isPercentage }: CommonXYDataLayerConfig, + { xAccessor, accessors, splitAccessors = [], table, isPercentage }: CommonXYDataLayerConfig, { splitColumnAccessor, splitRowAccessor }: SplitAccessors ): LayerFieldFormats => { const yAccessors: Array = accessors; + const splitColumnAccessors: Array = splitAccessors; return { xAccessors: getAccessorWithFieldFormat(xAccessor, table.columns), yAccessors: yAccessors.reduce( @@ -138,7 +142,13 @@ export const getLayerFormats = ( }), {} ), - splitSeriesAccessors: getAccessorWithFieldFormat(splitAccessor, table.columns), + splitSeriesAccessors: splitColumnAccessors?.reduce( + (formatters, splitAccessor) => ({ + ...formatters, + ...getAccessorWithFieldFormat(splitAccessor, table.columns), + }), + {} + ), splitColumnAccessors: getAccessorWithFieldFormat(splitColumnAccessor, table.columns), splitRowAccessors: getAccessorWithFieldFormat(splitRowAccessor, table.columns), }; @@ -171,7 +181,7 @@ const getTitleForYAccessor = ( }; export const getLayerTitles = ( - { xAccessor, accessors, splitAccessor, table, layerId }: CommonXYDataLayerConfig, + { xAccessor, accessors, splitAccessors = [], table, layerId }: CommonXYDataLayerConfig, { splitColumnAccessor, splitRowAccessor }: SplitAccessors, { xTitle }: CustomTitles, groups: GroupsConfiguration @@ -191,6 +201,7 @@ export const getLayerTitles = ( const xColumnId = xAccessor && getAccessorByDimension(xAccessor, table.columns); const yColumnIds = accessors.map((a) => a && getAccessorByDimension(a, table.columns)); + const splitColumnAccessors: Array = splitAccessors; return { xTitles: xTitle && xColumnId ? { [xColumnId]: xTitle } : mapTitle(xColumnId), @@ -198,7 +209,13 @@ export const getLayerTitles = ( (titles, yAccessor) => ({ ...titles, ...(yAccessor ? getYTitle(yAccessor) : {}) }), {} ), - splitSeriesTitles: mapTitle(splitAccessor), + splitSeriesTitles: splitColumnAccessors.reduce( + (titles, splitAccessor) => ({ + ...titles, + ...(splitAccessor ? mapTitle(splitAccessor) : {}), + }), + {} + ), splitColumnTitles: mapTitle(splitColumnAccessor), splitRowTitles: mapTitle(splitRowAccessor), }; 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 a705dab47dc43..f1b8295ff005c 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/state.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/state.ts @@ -19,7 +19,7 @@ export function isHorizontalChart(layers: CommonXYLayerConfig[]) { export const getSeriesColor = (layer: CommonXYLayerConfig, accessor: string) => { if ( - (isDataLayer(layer) && layer.splitAccessor) || + (isDataLayer(layer) && layer.splitAccessors) || isAnnotationsLayer(layer) || isReferenceLine(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 a8fc1bec4b756..485f0fc6e66e6 100644 --- a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts +++ b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts @@ -463,7 +463,7 @@ const dataLayerToExpression = ( isPercentage: isPercentage ? [isPercentage] : [], isStacked: isStacked ? [isStacked] : [], isHorizontal: isHorizontal ? [isHorizontal] : [], - splitAccessor: layer.collapseFn || !layer.splitAccessor ? [] : [layer.splitAccessor], + splitAccessors: layer.collapseFn || !layer.splitAccessor ? [] : [layer.splitAccessor], decorations: layer.yConfig ? layer.yConfig.map((yConfig) => yConfigToDataDecorationConfigExpression(yConfig, yAxisConfigs) From 3ab85d86aea28975e9482954a24fc6f93a0830fe Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Wed, 15 Jun 2022 21:03:08 +0300 Subject: [PATCH 192/213] Some fixes after resolving merge conflicts --- .../expression_functions/common_axis_args.ts | 8 +++++++- .../expression_functions/y_axis_config.ts | 5 ----- .../common/types/expression_functions.ts | 2 +- .../public/components/xy_chart.test.tsx | 19 +++++++++++-------- .../public/components/xy_chart.tsx | 2 +- .../__snapshots__/to_expression.test.ts.snap | 1 + .../public/xy_visualization/to_expression.ts | 7 +------ 7 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_axis_args.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_axis_args.ts index 292279af5fa3f..942d83c27ea75 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_axis_args.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_axis_args.ts @@ -8,12 +8,13 @@ import { strings } from '../i18n'; import { XAxisConfigFn, YAxisConfigFn } from '../types'; +import { AXIS_EXTENT_CONFIG } from '../constants'; type CommonAxisConfigFn = XAxisConfigFn | YAxisConfigFn; export const commonAxisConfigArgs: Omit< CommonAxisConfigFn['args'], - 'scaleType' | 'mode' | 'extent' | 'boundsMargin' + 'scaleType' | 'mode' | 'boundsMargin' > = { title: { types: ['string'], @@ -63,4 +64,9 @@ export const commonAxisConfigArgs: Omit< types: ['number'], help: strings.getAxisTruncateHelp(), }, + extent: { + types: [AXIS_EXTENT_CONFIG], + help: strings.getAxisExtentHelp(), + default: `{${AXIS_EXTENT_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 6088ed4e3c86e..ec10fc8f6e0b9 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 @@ -29,11 +29,6 @@ export const yAxisConfigFunction: YAxisConfigFn = { types: ['number'], help: strings.getAxisBoundsMarginHelp(), }, - extent: { - types: [AXIS_EXTENT_CONFIG], - help: strings.getAxisExtentHelp(), - default: `{${AXIS_EXTENT_CONFIG}}`, - }, scaleType: { options: [...Object.values(YScaleTypes)], help: strings.getAxisScaleTypeHelp(), 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 2528eaa8a9c03..b27863efa4fe8 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 @@ -86,12 +86,12 @@ export interface AxisConfig { showLabels?: boolean; showTitle?: boolean; showGridLines?: boolean; + extent?: AxisExtentConfigResult; } export interface YAxisConfig extends AxisConfig { mode?: AxisMode; boundsMargin?: number; - extent?: AxisExtentConfigResult; scaleType?: YScaleType; } 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 0ceb1b196f888..6ca70bcad89c9 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 @@ -519,11 +519,14 @@ describe('XYChart component', () => { isHistogram: true, }, ], - xExtent: { - type: 'axisExtentConfig', - mode: 'custom', - lowerBound: 123, - upperBound: 456, + xAxisConfig: { + type: 'xAxisConfig', + extent: { + type: 'axisExtentConfig', + mode: 'custom', + lowerBound: 123, + upperBound: 456, + }, }, }} /> @@ -2455,7 +2458,7 @@ describe('XYChart component', () => { extent: { mode: 'dataBounds', type: 'axisExtentConfig', - } + }, }, markSizeRatio: 1, layers: [ @@ -2563,7 +2566,7 @@ describe('XYChart component', () => { extent: { mode: 'dataBounds', type: 'axisExtentConfig', - } + }, }, showTooltip: true, markSizeRatio: 1, @@ -2653,7 +2656,7 @@ describe('XYChart component', () => { extent: { mode: 'dataBounds', type: 'axisExtentConfig', - } + }, }, markSizeRatio: 1, layers: [ 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 2185c700c125b..6ae9d0396db06 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 @@ -274,7 +274,7 @@ export function XYChart({ minInterval, isTimeViz, isHistogramViz, - xAxisConfig.extent + xAxisConfig?.extent ); const axisTitlesVisibilitySettings = { 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 d5fb0cb0b672d..9e5ff4f39252a 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 @@ -130,6 +130,7 @@ Object { "chain": Array [ Object { "arguments": Object { + "extent": Array [], "id": Array [ "x", ], 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 0ac9680d3b06b..ee48b0cf3d295 100644 --- a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts +++ b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts @@ -9,11 +9,6 @@ import { Ast, AstFunction } from '@kbn/interpreter'; import { Position, ScaleType } from '@elastic/charts'; import type { PaletteRegistry } from '@kbn/coloring'; import { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public'; -<<<<<<< HEAD -import type { AxisExtentConfig } from '@kbn/expression-xy-plugin/common'; -======= -import type { YConfig, ExtendedYConfig } from '@kbn/expression-xy-plugin/common'; ->>>>>>> upstream/main import { LegendSize } from '@kbn/visualizations-plugin/public'; import { State, @@ -368,7 +363,7 @@ const yAxisConfigsToExpression = ( arguments: { id: axis.id ? [axis.id] : [], position: axis.position ? [axis.position] : [], - extent: axis.extent ? [axisExtentConfigToExpression(axis.extent, validDataLayers)] : [], + extent: axis.extent ? [axisExtentConfigToExpression(axis.extent)] : [], showTitle: [axis.showTitle ?? true], title: axis.title !== undefined ? [axis.title] : [], showLabels: [axis.showLabels ?? true], From e613c827baac497e08a8f6774b1dfde8f3ae86c4 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Wed, 15 Jun 2022 18:09:03 +0000 Subject: [PATCH 193/213] [CI] Auto-commit changed files from 'node scripts/precommit_hook.js --ref HEAD~1..HEAD --fix' --- .../expression_xy/common/expression_functions/y_axis_config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 ec10fc8f6e0b9..64f58dc5acb98 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 { Position } from '@elastic/charts'; import { strings } from '../i18n'; -import { Y_AXIS_CONFIG, AxisModes, AXIS_EXTENT_CONFIG, YScaleTypes } from '../constants'; +import { Y_AXIS_CONFIG, AxisModes, YScaleTypes } from '../constants'; import { YAxisConfigFn } from '../types'; import { commonAxisConfigArgs } from './common_axis_args'; From 483d704c28817111fd1dc4c3b0fdb06512d718cc Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Wed, 15 Jun 2022 23:14:32 +0300 Subject: [PATCH 194/213] Fixed types --- x-pack/plugins/lens/public/xy_visualization/to_expression.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 ee48b0cf3d295..c9e53b75b1955 100644 --- a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts +++ b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts @@ -316,7 +316,7 @@ export const buildExpression = ( showLabels: [state?.tickLabelsVisibilitySettings?.x ?? true], showGridLines: [state?.gridlinesVisibilitySettings?.x ?? true], labelsOrientation: [state?.labelsOrientation?.x ?? 0], - extent: state.xExtent ? axisExtentConfigToExpression(state.xExtent) : [], + extent: state.xExtent ? [axisExtentConfigToExpression(state.xExtent)] : [], }, }, ], From 4541f1b894554cf72ba296b018879ce31c054f8a Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Thu, 16 Jun 2022 16:02:18 +0300 Subject: [PATCH 195/213] Fix log datatable --- .../expression_xy/common/utils/log_datatables.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 44026b30ed493..f2a85241d1aec 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 @@ -34,10 +34,10 @@ export const getLayerDimensions = ( layer: CommonXYDataLayerConfig | ReferenceLineLayerConfig ): Dimension[] => { let xAccessor; - let splitAccessor; + let splitAccessors; if (layer.layerType === LayerTypes.DATA) { xAccessor = layer.xAccessor; - splitAccessor = layer.splitAccessor; + splitAccessors = layer.splitAccessors; } const { accessors, layerType } = layer; @@ -47,6 +47,6 @@ export const getLayerDimensions = ( layerType === LayerTypes.DATA ? strings.getMetricHelp() : strings.getReferenceLineHelp(), ], [xAccessor ? [xAccessor] : undefined, strings.getXAxisHelp()], - [splitAccessor ? [splitAccessor] : undefined, strings.getBreakdownHelp()], + [splitAccessors ? splitAccessors : undefined, strings.getBreakdownHelp()], ]; }; From c1fcbe9fb8f6521ea9a34b828aada5957ed99121 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 16 Jun 2022 13:53:21 +0000 Subject: [PATCH 196/213] [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' --- .../chart_expressions/expression_xy/common/helpers/layers.ts | 4 +++- .../expression_xy/public/components/legend_action.test.tsx | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/common/helpers/layers.ts b/src/plugins/chart_expressions/expression_xy/common/helpers/layers.ts index 35036110eebaf..3090ffe08cccd 100644 --- a/src/plugins/chart_expressions/expression_xy/common/helpers/layers.ts +++ b/src/plugins/chart_expressions/expression_xy/common/helpers/layers.ts @@ -62,7 +62,9 @@ export function getAccessors< !markSizeAccessor ) { const y = table.columns.find((column) => column.id === PointSeriesColumnNames.Y)?.id; - const splitColumnId = table.columns.find((column) => column.id === PointSeriesColumnNames.COLOR)?.id; + const splitColumnId = table.columns.find( + (column) => column.id === PointSeriesColumnNames.COLOR + )?.id; xAccessor = table.columns.find((column) => column.id === PointSeriesColumnNames.X)?.id; splitAccessors = splitColumnId ? [splitColumnId] : []; accessors = y ? [y] : []; 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 033659f05335e..9dd6ccdcbc9a0 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 @@ -241,7 +241,9 @@ describe('getLegendAction', function () { }; wrapper = mountWithIntl(); expect(wrapper.find(EuiPopover).length).toBe(1); - expect(wrapper.find(EuiPopover).prop('title')).toEqual("Women's Accessories - Label B, filter options"); + expect(wrapper.find(EuiPopover).prop('title')).toEqual( + "Women's Accessories - Label B, filter options" + ); expect(wrapper.find(LegendActionPopover).prop('context')).toEqual({ data: [ { From c10bb49898e144522fd6c6f497ef2e831f703798 Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Thu, 16 Jun 2022 16:54:52 +0300 Subject: [PATCH 197/213] Fixed snapshots --- .../xy_visualization/__snapshots__/to_expression.test.ts.snap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 9e5ff4f39252a..997ba54a85e2e 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 @@ -67,7 +67,7 @@ Object { "seriesType": Array [ "area", ], - "splitAccessor": Array [ + "splitAccessors": Array [ "d", ], "table": Array [ From a323101bd1b823fa746506a6e7434f1a9557d088 Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Thu, 16 Jun 2022 17:58:07 +0300 Subject: [PATCH 198/213] Fixed lint --- .../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 58e4555e1ac52..e64f27988d612 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 @@ -454,7 +454,7 @@ export function XYChart({ ? formatFactory(xFormat) : xAxisFormatter; - const rowIndex = table.rows.findIndex((row) => { + let rowIndex = table.rows.findIndex((row) => { if (xAccessor) { if (formattedDatatables[layer.layerId]?.formattedColumns[xAccessor]) { // stringify the value to compare with the chart value @@ -474,7 +474,7 @@ export function XYChart({ if (xySeries.seriesKeys.length > 1) { xySeries.splitAccessors.forEach((value, key) => { - const rowIndex = table.rows.findIndex((row) => { + rowIndex = table.rows.findIndex((row) => { return row[key] === value; }); points.push({ From 7c6cdd6230c823a804a86176d864cbd094b81b99 Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Fri, 17 Jun 2022 13:24:11 +0300 Subject: [PATCH 199/213] Fixed comments --- .../expression_functions/reference_line.ts | 2 +- .../common/expression_functions/validate.ts | 16 ++++++------- .../reference_lines/reference_line.tsx | 6 ++++- .../reference_lines/reference_line_layer.tsx | 10 +++++--- .../components/reference_lines/utils.tsx | 6 +++-- .../public/components/xy_chart.tsx | 22 ++++++++++-------- .../public/helpers/axes_configuration.ts | 15 ------------ .../public/xy_visualization/to_expression.ts | 23 ++++++++----------- 8 files changed, 47 insertions(+), 53 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line.ts index d7efd8d58cdc8..0f3a3a4f6b1fb 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line.ts @@ -50,7 +50,7 @@ export const referenceLineFunction: ReferenceLineFn = { types: ['string'], help: i18n.translate('expressionXY.referenceLine.axisId.help', { defaultMessage: - 'Id of axis to which the reference line belongs. Have more priority than "position"', + 'Id of axis to which the reference line belongs. It has higher priority than "position"', }), }, color: { diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/validate.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/validate.ts index c8f3e9051a581..a2ccbdda40c4e 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/validate.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/validate.ts @@ -113,16 +113,14 @@ export const errors = { }; export const hasBarLayer = (layers: Array) => - layers.filter(({ seriesType }) => seriesType === SeriesTypes.BAR).length > 0; + layers.some(({ seriesType }) => seriesType === SeriesTypes.BAR); export const hasAreaLayer = (layers: Array) => - layers.filter(({ seriesType }) => seriesType === SeriesTypes.AREA).length > 0; + layers.some(({ seriesType }) => seriesType === SeriesTypes.AREA); export const hasHistogramBarLayer = ( layers: Array -) => - layers.filter(({ seriesType, isHistogram }) => seriesType === SeriesTypes.BAR && isHistogram) - .length > 0; +) => layers.some(({ seriesType, isHistogram }) => seriesType === SeriesTypes.BAR && isHistogram); export const isValidExtentWithCustomMode = (extent: AxisExtentConfigResult) => { const isValidLowerBound = @@ -137,8 +135,8 @@ export const validateExtentForDataBounds = ( extent: AxisExtentConfigResult, layers: Array ) => { - const lineSeries = layers.filter(({ seriesType }) => seriesType === SeriesTypes.LINE); - if (!lineSeries.length && extent.mode === AxisExtentModes.DATA_BOUNDS) { + const hasLineSeries = layers.some(({ seriesType }) => seriesType === SeriesTypes.LINE); + if (!hasLineSeries && extent.mode === AxisExtentModes.DATA_BOUNDS) { throw new Error(errors.dataBoundsForNotLineChartError()); } }; @@ -214,7 +212,7 @@ export const validateMarkSizeForChartType = ( markSizeAccessor: ExpressionValueVisDimension | string | undefined, seriesType: SeriesType ) => { - if (markSizeAccessor && seriesType !== SeriesTypes.LINE && seriesType !== SeriesTypes.AREA) { + if (markSizeAccessor && !isAreaOrLineChart(seriesType)) { throw new Error(errors.markSizeAccessorForNonLineOrAreaChartsError()); } }; @@ -256,7 +254,7 @@ export const validateLinesVisibilityForChartType = ( showLines: boolean | undefined, seriesType: SeriesType ) => { - if (showLines && !(seriesType === SeriesTypes.LINE || seriesType === SeriesTypes.AREA)) { + if (showLines && !isAreaOrLineChart(seriesType)) { throw new Error(errors.linesVisibilityForNonLineChartError()); } }; diff --git a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_line.tsx b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_line.tsx index 6611f775ef48e..b46a8bf00da16 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_line.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_line.tsx @@ -41,7 +41,11 @@ export const ReferenceLine: FC = ({ const { value } = decorationConfig; - const axisGroup = getAxisGroupForReferenceLine(yAxesConfiguration, decorationConfig); + const axisGroup = getAxisGroupForReferenceLine( + yAxesConfiguration, + decorationConfig, + isHorizontal + ); const formatter = axisGroup?.formatter || xAxisFormatter; const id = `${layer.layerId}-${value}`; diff --git a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_line_layer.tsx b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_line_layer.tsx index 21cb546654a7d..021395ee620f0 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_line_layer.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_line_layer.tsx @@ -51,7 +51,11 @@ export const ReferenceLineLayer: FC = ({ } const referenceLineElements = decorationConfigsByValue.flatMap((decorationConfig) => { - const axisGroup = getAxisGroupForReferenceLine(yAxesConfiguration, decorationConfig); + const axisGroup = getAxisGroupForReferenceLine( + yAxesConfiguration, + decorationConfig, + isHorizontal + ); const formatter = axisGroup?.formatter || xAxisFormatter; const name = @@ -74,8 +78,8 @@ export const ReferenceLineLayer: FC = ({ const id = `${layer.layerId}-${decorationConfig.forAccessor}`; const axesMap = { - left: yAxesConfiguration.some((axes) => axes.position === 'left'), - right: yAxesConfiguration.some((axes) => axes.position === 'right'), + left: yAxesConfiguration.some((axes) => axes.position === Position.Left), + right: yAxesConfiguration.some((axes) => axes.position === Position.Right), }; return ( diff --git a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/utils.tsx b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/utils.tsx index fb9a902193e9c..51fc5111ae882 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/utils.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/utils.tsx @@ -25,6 +25,7 @@ import { mapVerticalToHorizontalPlacement, Marker, MarkerBody, + getAxisPosition, } from '../../helpers'; import { ReferenceLineAnnotationConfig } from './reference_line_annotations'; @@ -221,11 +222,12 @@ export const computeChartMargins = ( export function getAxisGroupForReferenceLine( yAxesConfiguration: GroupsConfiguration, - decorationConfig: ReferenceLineDecorationConfig | ExtendedReferenceLineDecorationConfig + decorationConfig: ReferenceLineDecorationConfig | ExtendedReferenceLineDecorationConfig, + shouldRotate: boolean ) { return yAxesConfiguration.find( (axis) => (decorationConfig.axisId && axis.groupId.includes(decorationConfig.axisId)) || - decorationConfig.position === axis.position + getAxisPosition(decorationConfig.position ?? Position.Left, shouldRotate) === axis.position ); } 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 6ae9d0396db06..fd39755a75509 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 @@ -49,6 +49,8 @@ import type { ReferenceLineDecorationConfig, ExtendedReferenceLineDecorationConfig, XYChartProps, + AxisExtentConfig, + AxisExtentConfigResult, } from '../../common/types'; import { isHorizontalChart, @@ -245,8 +247,8 @@ export function XYChart({ const xTitle = xAxisConfig?.title || (xAxisColumn && xAxisColumn.name); const yAxesMap = { - left: yAxesConfiguration.find(({ position }) => position === 'left'), - right: yAxesConfiguration.find(({ position }) => position === 'right'), + left: yAxesConfiguration.find(({ position }) => position === Position.Left), + right: yAxesConfiguration.find(({ position }) => position === Position.Right), }; const titles = getLayersTitles( @@ -317,7 +319,8 @@ export function XYChart({ .map((config) => ({ ...config, position: config - ? getAxisGroupForReferenceLine(yAxesConfiguration, config)?.position + ? getAxisGroupForReferenceLine(yAxesConfiguration, config, shouldRotate)?.position ?? + Position.Left : Position.Bottom, })), ...groupedLineAnnotations, @@ -356,7 +359,8 @@ export function XYChart({ }; const getYAxisDomain = (axis: GroupsConfiguration[number]) => { - const extent = axis.extent || { + const extent: AxisExtentConfigResult = axis.extent || { + type: 'axisExtentConfig', mode: 'full', }; const hasBarOrArea = Boolean( @@ -389,17 +393,17 @@ export function XYChart({ max, padding, includeDataFromIds: referenceLineLayers - .flatMap((l) => - l.decorations - ? l.decorations.map((decoration) => ({ layerId: l.layerId, decoration })) - : [] + .flatMap( + (l) => l.decorations?.map((decoration) => ({ layerId: l.layerId, decoration })) || [] ) .filter(({ decoration }) => { if (decoration.axisId) { return axis.groupId.includes(decoration.axisId); } - return axis.position === decoration.position; + return ( + axis.position === getAxisPosition(decoration.position ?? Position.Left, shouldRotate) + ); }) .map(({ layerId, decoration }) => isReferenceLineDecorationConfig(decoration) 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 35ea1a7d85db3..b751ec8ad7eb1 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 @@ -235,18 +235,3 @@ export function validateExtent(hasBarOrArea: boolean, extent?: AxisExtentConfig) extent.upperBound <= extent.lowerBound; return { inclusiveZeroError, boundaryError }; } - -export const getAxisGroupConfig = ( - axesGroup?: GroupsConfiguration, - decoration?: ReferenceLineDecorationConfigResult | ExtendedReferenceLineDecorationConfig -) => { - return axesGroup?.find((axis) => { - if (decoration?.axisId) { - return axis.groupId.includes(decoration.axisId); - } - - return decoration && isReferenceLineDecorationConfig(decoration) - ? decoration.position === axis.position - : axis.series.some(({ accessor }) => accessor === decoration?.forAccessor); - }); -}; 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 c9e53b75b1955..ae55603debaa9 100644 --- a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts +++ b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts @@ -192,15 +192,15 @@ export const buildExpression = ( } const isLeftAxis = validDataLayers.some(({ yConfig }) => - yConfig?.some((config) => config.axisMode === 'left') + yConfig?.some((config) => config.axisMode === Position.Left) ); const isRightAxis = validDataLayers.some(({ yConfig }) => - yConfig?.some((config) => config.axisMode === 'right') + yConfig?.some((config) => config.axisMode === Position.Right) ); const yAxisConfigs: AxisConfig[] = [ { - position: 'left', + position: Position.Left, extent: state?.yLeftExtent, showTitle: state?.axisTitlesVisibilitySettings?.yLeft ?? true, title: state.yTitle || '', @@ -210,7 +210,7 @@ export const buildExpression = ( scaleType: state.yLeftScale || 'linear', }, { - position: 'right', + position: Position.Right, extent: state?.yRightExtent, showTitle: state?.axisTitlesVisibilitySettings?.yRight ?? true, title: state.yRightTitle || '', @@ -223,8 +223,8 @@ export const buildExpression = ( if (isLeftAxis) { yAxisConfigs.push({ - id: 'left', - position: 'left', + id: Position.Left, + position: Position.Left, // we need also settings from global config here so that default's doesn't override it ...yAxisConfigs[0], }); @@ -232,8 +232,8 @@ export const buildExpression = ( if (isRightAxis) { yAxisConfigs.push({ - id: 'right', - position: 'right', + id: Position.Right, + position: Position.Right, // we need also settings from global config here so that default's doesn't override it ...yAxisConfigs[1], }); @@ -300,7 +300,7 @@ export const buildExpression = ( valueLabels: [state?.valueLabels || 'hide'], hideEndzones: [state?.hideEndzones || false], valuesInLegend: [state?.valuesInLegend || false], - yAxisConfigs: [...yAxisConfigsToExpression(yAxisConfigs, validDataLayers)], + yAxisConfigs: [...yAxisConfigsToExpression(yAxisConfigs)], xAxisConfig: [ { type: 'expression', @@ -350,10 +350,7 @@ export const buildExpression = ( }; }; -const yAxisConfigsToExpression = ( - yAxisConfigs: AxisConfig[], - validDataLayers: ValidXYDataLayerConfig[] -): Ast[] => { +const yAxisConfigsToExpression = (yAxisConfigs: AxisConfig[]): Ast[] => { return yAxisConfigs.map((axis) => ({ type: 'expression', chain: [ From c6fee746f2cff70d20f1ed97a60d24be6a428cfa Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Fri, 17 Jun 2022 13:30:57 +0300 Subject: [PATCH 200/213] Some fixes after resolving merge conflicts --- .../expression_xy/common/expression_functions/validate.ts | 6 +++++- .../expression_xy/common/expression_functions/xy_vis_fn.ts | 7 +------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/validate.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/validate.ts index d5ba433b1faf0..9b8183abfa205 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/validate.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/validate.ts @@ -19,6 +19,7 @@ import { CommonXYDataLayerConfig, YAxisConfigResult, ExtendedDataLayerConfigResult, + XAxisConfigResult, } from '../types'; import { isTimeChart } from '../helpers'; @@ -166,7 +167,8 @@ export const validateXExtent = ( export const validateExtents = ( dataLayers: Array, hasBarOrArea: boolean, - yAxisConfigs?: YAxisConfigResult[] + yAxisConfigs?: YAxisConfigResult[], + xAxisConfig?: XAxisConfigResult ) => { yAxisConfigs?.forEach((axis) => { if (!axis.extent) { @@ -182,6 +184,8 @@ export const validateExtents = ( validateExtentForDataBounds(axis.extent, dataLayers); }); + + validateXExtent(xAxisConfig?.extent, dataLayers); }; export const validateAxes = ( 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 447940a0d6873..00c29436926f4 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 @@ -33,11 +33,7 @@ import { validateLineWidthForChartType, validatePointsRadiusForChartType, validateLinesVisibilityForChartType, -<<<<<<< HEAD validateAxes, -======= - validateXExtent, ->>>>>>> upstream/main } from './validate'; const createDataLayer = (args: XYArgs, table: Datatable): DataLayerConfigResult => { @@ -131,8 +127,7 @@ export const xyVisFn: XyVisFn['fn'] = async (data, args, handlers) => { const hasBar = hasBarLayer(dataLayers); const hasArea = hasAreaLayer(dataLayers); - validateExtents(dataLayers, hasBar || hasArea, args.yAxisConfigs); - validateXExtent(args.xAxisConfigs?.extent, dataLayers); + validateExtents(dataLayers, hasBar || hasArea, args.yAxisConfigs, args.xAxisConfig); validateFillOpacity(args.fillOpacity, hasArea); validateAddTimeMarker(dataLayers, args.addTimeMarker); validateMinTimeBarInterval(dataLayers, hasBar, args.minTimeBarInterval); From bed889db85fc7e24348569cc3369ffd9302af724 Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Fri, 17 Jun 2022 15:44:37 +0300 Subject: [PATCH 201/213] Fixed reference line position --- .../reference_lines/reference_line_annotations.tsx | 11 ++++++----- .../public/components/reference_lines/utils.tsx | 4 +--- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_line_annotations.tsx b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_line_annotations.tsx index 822fd830b415e..b1428da4fdf7b 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_line_annotations.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_line_annotations.tsx @@ -50,12 +50,13 @@ interface Props { const getRectDataValue = ( annotationConfig: ReferenceLineAnnotationConfig, - formatter: FieldFormat | undefined + formatter: FieldFormat | undefined, + groupId: string ) => { - const { name, value, nextValue, fill, axisGroup } = annotationConfig; + const { name, value, nextValue, fill } = annotationConfig; const isFillAbove = fill === 'above'; - if (axisGroup?.position === Position.Bottom) { + if (groupId === Position.Bottom) { return getBottomRect(name, isFillAbove, formatter, value, nextValue); } @@ -103,7 +104,7 @@ export const ReferenceLineAnnotations: FC = ({ key={`${id}-line`} dataValues={[dataValues]} domainType={ - axisGroup?.position === Position.Bottom + props.groupId === Position.Bottom ? AnnotationDomainType.XDomain : AnnotationDomainType.YDomain } @@ -113,7 +114,7 @@ export const ReferenceLineAnnotations: FC = ({ let rect; if (fill && fill !== 'none') { - const rectDataValues = getRectDataValue(config, formatter); + const rectDataValues = getRectDataValue(config, formatter, props.groupId); rect = ( ), // rotate the position if required - markerPosition: isHorizontal - ? mapVerticalToHorizontalPlacement(markerPositionVertical) - : markerPositionVertical, + markerPosition: markerPositionVertical, }; }; From 91454ca57dcc85347fbd6352fdd77fa1693a3ce8 Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Fri, 17 Jun 2022 15:53:15 +0300 Subject: [PATCH 202/213] Fix nit --- .../chart_expressions/expression_xy/common/helpers/layers.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/common/helpers/layers.ts b/src/plugins/chart_expressions/expression_xy/common/helpers/layers.ts index 679fbfcc1a339..83ea26eea262a 100644 --- a/src/plugins/chart_expressions/expression_xy/common/helpers/layers.ts +++ b/src/plugins/chart_expressions/expression_xy/common/helpers/layers.ts @@ -35,9 +35,7 @@ export function appendLayerIds( } export const getShowLines = (args: DataLayerArgs | ExtendedDataLayerArgs) => - args.seriesType === SeriesTypes.LINE || args.seriesType !== SeriesTypes.AREA - ? args.showLines ?? true - : args.showLines; + args.showLines ?? (args.seriesType === SeriesTypes.LINE || args.seriesType !== SeriesTypes.AREA); export function getDataLayers(layers: XYExtendedLayerConfigResult[]) { return layers.filter( From 3f94012d7ba31b4a3e7a7ef7b3945588636c3e66 Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Fri, 17 Jun 2022 16:21:28 +0300 Subject: [PATCH 203/213] Fixed CI --- .../expression_xy/common/__mocks__/index.ts | 4 -- .../expression_functions/xy_vis.test.ts | 52 ++++++++++++------- .../public/components/xy_chart.tsx | 1 - .../public/helpers/axes_configuration.ts | 3 -- 4 files changed, 32 insertions(+), 28 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 e56509e9118a5..1bde83c1822b7 100644 --- a/src/plugins/chart_expressions/expression_xy/common/__mocks__/index.ts +++ b/src/plugins/chart_expressions/expression_xy/common/__mocks__/index.ts @@ -102,10 +102,6 @@ export const createArgsWithLayers = ( showLabels: true, showTitle: true, title: '', - extent: { - mode: 'dataBounds', - type: 'axisExtentConfig', - }, }, yAxisConfigs: [ { 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 bbd906b724844..0b81682eb4381 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 @@ -233,7 +233,10 @@ describe('xyVis', () => { annotationLayers: [], isHistogram: true, xScaleType: 'time', - xExtent: { type: 'axisExtentConfig', mode: 'dataBounds' }, + xAxisConfig: { + type: 'xAxisConfig', + extent: { type: 'axisExtentConfig', mode: 'dataBounds' }, + }, }, createMockExecutionContext() ) @@ -255,11 +258,14 @@ describe('xyVis', () => { ...restLayerArgs, referenceLines: [], annotationLayers: [], - xExtent: { - type: 'axisExtentConfig', - mode: 'full', - lowerBound: undefined, - upperBound: undefined, + xAxisConfig: { + type: 'xAxisConfig', + extent: { + type: 'axisExtentConfig', + mode: 'full', + lowerBound: undefined, + upperBound: undefined, + }, }, }, createMockExecutionContext() @@ -282,9 +288,9 @@ describe('xyVis', () => { ...restLayerArgs, referenceLines: [], annotationLayers: [], - xExtent: { - type: 'axisExtentConfig', - mode: 'dataBounds', + xAxisConfig: { + type: 'xAxisConfig', + extent: { type: 'axisExtentConfig', mode: 'dataBounds' }, }, }, createMockExecutionContext() @@ -292,7 +298,7 @@ describe('xyVis', () => { ).rejects.toThrowErrorMatchingSnapshot(); }); - test('it renders with custom xExtent for a numeric histogram', async () => { + test('it renders with custom x-axis extent for a numeric histogram', async () => { const { data, args } = sampleArgs(); const { layers, ...rest } = args; const { layerId, layerType, table, type, ...restLayerArgs } = sampleLayer; @@ -304,11 +310,14 @@ describe('xyVis', () => { referenceLines: [], annotationLayers: [], isHistogram: true, - xExtent: { - type: 'axisExtentConfig', - mode: 'custom', - lowerBound: 0, - upperBound: 10, + xAxisConfig: { + type: 'xAxisConfig', + extent: { + type: 'axisExtentConfig', + mode: 'custom', + lowerBound: 0, + upperBound: 10, + }, }, }, createMockExecutionContext() @@ -320,11 +329,14 @@ describe('xyVis', () => { value: { args: { ...rest, - xExtent: { - type: 'axisExtentConfig', - mode: 'custom', - lowerBound: 0, - upperBound: 10, + xAxisConfig: { + type: 'xAxisConfig', + extent: { + type: 'axisExtentConfig', + mode: 'custom', + lowerBound: 0, + upperBound: 10, + }, }, layers: [ { 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 fd39755a75509..7115f4f9a9ce8 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 @@ -49,7 +49,6 @@ import type { ReferenceLineDecorationConfig, ExtendedReferenceLineDecorationConfig, XYChartProps, - AxisExtentConfig, AxisExtentConfigResult, } from '../../common/types'; import { 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 b751ec8ad7eb1..5d38bac4ddb1e 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 @@ -10,17 +10,14 @@ import { Position } from '@elastic/charts'; import type { IFieldFormat, SerializedFieldFormat } from '@kbn/field-formats-plugin/common'; import { getAccessorByDimension } from '@kbn/visualizations-plugin/common/utils'; import { FormatFactory } from '../types'; -import type { ExtendedReferenceLineDecorationConfig } from '../../common/types'; import { AxisExtentConfig, CommonXYDataLayerConfig, DataDecorationConfig, YAxisConfig, ReferenceLineDecorationConfig, - ReferenceLineDecorationConfigResult, } from '../../common'; import { LayersFieldFormats } from './layers'; -import { isReferenceLineDecorationConfig } from './visualization'; export interface Series { layer: string; From 25ab50929e1824e3f8781406f4322383c998b0df Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Fri, 17 Jun 2022 13:57:22 +0000 Subject: [PATCH 204/213] [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' --- .../expression_xy/public/components/xy_chart.tsx | 1 - .../expression_xy/public/helpers/axes_configuration.ts | 3 --- 2 files changed, 4 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 744dd304ac14f..4377742821711 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 @@ -49,7 +49,6 @@ import type { ReferenceLineDecorationConfig, ExtendedReferenceLineDecorationConfig, XYChartProps, - AxisExtentConfig, AxisExtentConfigResult, } from '../../common/types'; import { 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 b751ec8ad7eb1..5d38bac4ddb1e 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 @@ -10,17 +10,14 @@ import { Position } from '@elastic/charts'; import type { IFieldFormat, SerializedFieldFormat } from '@kbn/field-formats-plugin/common'; import { getAccessorByDimension } from '@kbn/visualizations-plugin/common/utils'; import { FormatFactory } from '../types'; -import type { ExtendedReferenceLineDecorationConfig } from '../../common/types'; import { AxisExtentConfig, CommonXYDataLayerConfig, DataDecorationConfig, YAxisConfig, ReferenceLineDecorationConfig, - ReferenceLineDecorationConfigResult, } from '../../common'; import { LayersFieldFormats } from './layers'; -import { isReferenceLineDecorationConfig } from './visualization'; export interface Series { layer: string; From 3e5090732064d655ca7fe57272ea7ff99b766081 Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Fri, 17 Jun 2022 18:37:04 +0300 Subject: [PATCH 205/213] Fixed comments --- .../__snapshots__/xy_chart.test.tsx.snap | 36 ++++++++++++++----- .../reference_lines/reference_line.tsx | 11 +++--- .../reference_line_annotations.tsx | 10 ++++-- .../reference_lines/reference_line_layer.tsx | 11 +++--- .../reference_lines/reference_lines.test.tsx | 24 +++++++++++++ .../reference_lines/reference_lines.tsx | 3 +- .../components/reference_lines/utils.tsx | 14 +++++--- .../public/components/xy_chart.tsx | 21 +++++++---- .../public/helpers/annotations.tsx | 10 ++++-- .../public/helpers/axes_configuration.ts | 29 +++++++++++++-- .../public/xy_visualization/to_expression.ts | 1 + 11 files changed, 127 insertions(+), 43 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 a9fcc8484f814..c36f145efa973 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 @@ -1552,7 +1552,7 @@ exports[`XYChart component it renders horizontal bar 1`] = ` } gridLine={ Object { - "visible": undefined, + "visible": false, } } groupId="left" @@ -1563,13 +1563,13 @@ exports[`XYChart component it renders horizontal bar 1`] = ` Object { "axisTitle": Object { "padding": undefined, - "visible": undefined, + "visible": true, }, "tickLabel": Object { "fill": undefined, "padding": undefined, - "rotation": undefined, - "visible": undefined, + "rotation": -90, + "visible": false, }, } } @@ -1987,6 +1987,10 @@ exports[`XYChart component it renders horizontal bar 1`] = ` yAxesConfiguration={ Array [ Object { + "extent": Object { + "mode": "full", + "type": "axisExtentConfig", + }, "formatter": Object { "convert": [MockFunction] { "calls": Array [ @@ -2010,6 +2014,7 @@ exports[`XYChart component it renders horizontal bar 1`] = ` }, }, "groupId": "left", + "labelsOrientation": -90, "position": "bottom", "series": Array [ Object { @@ -2021,6 +2026,11 @@ exports[`XYChart component it renders horizontal bar 1`] = ` "layer": "first", }, ], + "showGridLines": false, + "showLabels": false, + "showTitle": true, + "title": "", + "type": "yAxisConfig", }, ] } @@ -3886,7 +3896,7 @@ exports[`XYChart component it renders stacked horizontal bar 1`] = ` } gridLine={ Object { - "visible": undefined, + "visible": false, } } groupId="left" @@ -3897,13 +3907,13 @@ exports[`XYChart component it renders stacked horizontal bar 1`] = ` Object { "axisTitle": Object { "padding": undefined, - "visible": undefined, + "visible": true, }, "tickLabel": Object { "fill": undefined, "padding": undefined, - "rotation": undefined, - "visible": undefined, + "rotation": -90, + "visible": false, }, } } @@ -4321,6 +4331,10 @@ exports[`XYChart component it renders stacked horizontal bar 1`] = ` yAxesConfiguration={ Array [ Object { + "extent": Object { + "mode": "full", + "type": "axisExtentConfig", + }, "formatter": Object { "convert": [MockFunction] { "calls": Array [ @@ -4344,6 +4358,7 @@ exports[`XYChart component it renders stacked horizontal bar 1`] = ` }, }, "groupId": "left", + "labelsOrientation": -90, "position": "bottom", "series": Array [ Object { @@ -4355,6 +4370,11 @@ exports[`XYChart component it renders stacked horizontal bar 1`] = ` "layer": "first", }, ], + "showGridLines": false, + "showLabels": false, + "showTitle": true, + "title": "", + "type": "yAxisConfig", }, ] } diff --git a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_line.tsx b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_line.tsx index b46a8bf00da16..aaa4fdcd3e12c 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_line.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_line.tsx @@ -11,7 +11,7 @@ import { Position } from '@elastic/charts'; import { FieldFormat } from '@kbn/field-formats-plugin/common'; import { ReferenceLineConfig } from '../../../common/types'; import { ReferenceLineAnnotations } from './reference_line_annotations'; -import { GroupsConfiguration } from '../../helpers'; +import { AxesMap, GroupsConfiguration } from '../../helpers'; import { getAxisGroupForReferenceLine } from './utils'; interface ReferenceLineProps { @@ -21,6 +21,7 @@ interface ReferenceLineProps { yAxesConfiguration: GroupsConfiguration; isHorizontal: boolean; nextValue?: number; + yAxesMap: AxesMap; } export const ReferenceLine: FC = ({ @@ -30,6 +31,7 @@ export const ReferenceLine: FC = ({ paddingMap, isHorizontal, nextValue, + yAxesMap, }) => { const { decorations: [decorationConfig], @@ -50,16 +52,11 @@ export const ReferenceLine: FC = ({ const formatter = axisGroup?.formatter || xAxisFormatter; const id = `${layer.layerId}-${value}`; - const axesMap = { - left: yAxesConfiguration.some((axes) => axes.position === 'left'), - right: yAxesConfiguration.some((axes) => axes.position === 'right'), - }; - return ( diff --git a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_line_annotations.tsx b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_line_annotations.tsx index b1428da4fdf7b..7214094f0e42f 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_line_annotations.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_line_annotations.tsx @@ -10,7 +10,7 @@ import { AnnotationDomainType, LineAnnotation, Position, RectAnnotation } from ' import { euiLightVars } from '@kbn/ui-theme'; import React, { FC } from 'react'; import { FieldFormat } from '@kbn/field-formats-plugin/common'; -import { AxisConfiguration, LINES_MARKER_SIZE } from '../../helpers'; +import { AxesMap, AxisConfiguration, getOriginalAxisPosition, LINES_MARKER_SIZE } from '../../helpers'; import { AvailableReferenceLineIcon, FillStyle, @@ -44,7 +44,7 @@ interface Props { config: ReferenceLineAnnotationConfig; paddingMap: Partial>; formatter?: FieldFormat; - axesMap: Record<'left' | 'right', boolean>; + axesMap: AxesMap; isHorizontal: boolean; } @@ -74,7 +74,11 @@ export const ReferenceLineAnnotations: FC = ({ const defaultColor = euiLightVars.euiColorDarkShade; // get the position for vertical chart - const markerPositionVertical = getBaseIconPlacement(iconPosition, axesMap, axisGroup?.position); + const markerPositionVertical = getBaseIconPlacement( + iconPosition, + axesMap, + getOriginalAxisPosition(axisGroup?.position ?? Position.Bottom, isHorizontal) + ); // the padding map is built for vertical chart const hasReducedPadding = paddingMap[markerPositionVertical] === LINES_MARKER_SIZE; diff --git a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_line_layer.tsx b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_line_layer.tsx index 021395ee620f0..1525ccdea60b9 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_line_layer.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_line_layer.tsx @@ -12,7 +12,7 @@ import { groupBy } from 'lodash'; import { Position } from '@elastic/charts'; import { ReferenceLineLayerConfig } from '../../../common/types'; import { ReferenceLineAnnotations } from './reference_line_annotations'; -import { LayerAccessorsTitles, GroupsConfiguration } from '../../helpers'; +import { LayerAccessorsTitles, GroupsConfiguration, AxesMap } from '../../helpers'; import { getAxisGroupForReferenceLine } from './utils'; interface ReferenceLineLayerProps { @@ -22,6 +22,7 @@ interface ReferenceLineLayerProps { titles?: LayerAccessorsTitles; xAxisFormatter: FieldFormat; yAxesConfiguration: GroupsConfiguration; + yAxesMap: AxesMap; } export const ReferenceLineLayer: FC = ({ @@ -31,6 +32,7 @@ export const ReferenceLineLayer: FC = ({ paddingMap, isHorizontal, titles, + yAxesMap, }) => { if (!layer.decorations) { return null; @@ -77,11 +79,6 @@ export const ReferenceLineLayer: FC = ({ const { forAccessor, type, ...restAnnotationConfig } = decorationConfig; const id = `${layer.layerId}-${decorationConfig.forAccessor}`; - const axesMap = { - left: yAxesConfiguration.some((axes) => axes.position === Position.Left), - right: yAxesConfiguration.some((axes) => axes.position === Position.Right), - }; - return ( = ({ ...restAnnotationConfig, axisGroup, }} - axesMap={axesMap} + axesMap={yAxesMap} paddingMap={paddingMap} formatter={formatter} isHorizontal={isHorizontal} diff --git a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_lines.test.tsx b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_lines.test.tsx index d48d5720f1b15..9ffb6c4cac5ad 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_lines.test.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_lines.test.tsx @@ -116,6 +116,18 @@ describe('ReferenceLines', () => { }, ], paddingMap: {}, + yAxesMap: { + left: { + groupId: 'left', + position: 'left', + series: [], + }, + right: { + groupId: 'right', + position: 'right', + series: [], + }, + }, }; }); @@ -450,6 +462,18 @@ describe('ReferenceLines', () => { }, ], paddingMap: {}, + yAxesMap: { + left: { + groupId: 'left', + position: 'left', + series: [], + }, + right: { + groupId: 'right', + position: 'right', + series: [], + }, + }, }; }); diff --git a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_lines.tsx b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_lines.tsx index e9579f8d9e861..28d6bb7263e69 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_lines.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_lines.tsx @@ -12,7 +12,7 @@ import React from 'react'; import { Position } from '@elastic/charts'; import type { FieldFormat } from '@kbn/field-formats-plugin/common'; import type { CommonXYReferenceLineLayerConfig, ReferenceLineConfig } from '../../../common/types'; -import { GroupsConfiguration, isReferenceLine, LayersAccessorsTitles } from '../../helpers'; +import { AxesMap, GroupsConfiguration, isReferenceLine, LayersAccessorsTitles } from '../../helpers'; import { ReferenceLineLayer } from './reference_line_layer'; import { ReferenceLine } from './reference_line'; import { getNextValuesForReferenceLines } from './utils'; @@ -24,6 +24,7 @@ export interface ReferenceLinesProps { isHorizontal: boolean; paddingMap: Partial>; titles?: LayersAccessorsTitles; + yAxesMap: AxesMap; } export const ReferenceLines = ({ layers, titles = {}, ...rest }: ReferenceLinesProps) => { diff --git a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/utils.tsx b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/utils.tsx index 7037e4169338a..bafde69ce0f35 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/utils.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/utils.tsx @@ -26,6 +26,8 @@ import { Marker, MarkerBody, getAxisPosition, + getOriginalAxisPosition, + AxesMap, } from '../../helpers'; import { ReferenceLineAnnotationConfig } from './reference_line_annotations'; @@ -34,7 +36,7 @@ import { ReferenceLineAnnotationConfig } from './reference_line_annotations'; // this function assume the chart is vertical export function getBaseIconPlacement( iconPosition: IconPosition | undefined, - axesMap?: Record, + axesMap?: AxesMap, position?: Position ) { if (iconPosition === 'auto') { @@ -75,7 +77,7 @@ export const getSharedStyle = (config: ReferenceLineAnnotationConfig) => ({ export const getLineAnnotationProps = ( config: ReferenceLineAnnotationConfig, labels: { markerLabel?: string; markerBodyLabel?: string }, - axesMap: Record<'left' | 'right', boolean>, + axesMap: AxesMap, paddingMap: Partial>, isHorizontal: boolean ) => { @@ -83,7 +85,7 @@ export const getLineAnnotationProps = ( const markerPositionVertical = getBaseIconPlacement( config.iconPosition, axesMap, - config.axisGroup?.position + getOriginalAxisPosition(config.axisGroup?.position ?? Position.Bottom, isHorizontal) ); // the padding map is built for vertical chart const hasReducedPadding = paddingMap[markerPositionVertical] === LINES_MARKER_SIZE; @@ -108,7 +110,9 @@ export const getLineAnnotationProps = ( /> ), // rotate the position if required - markerPosition: markerPositionVertical, + markerPosition: isHorizontal + ? mapVerticalToHorizontalPlacement(markerPositionVertical) + : markerPositionVertical, }; }; @@ -188,7 +192,7 @@ export const computeChartMargins = ( referenceLinePaddings: Partial>, labelVisibility: Partial>, titleVisibility: Partial>, - axesMap: Record<'left' | 'right', unknown>, + axesMap: AxesMap, isHorizontal: boolean ) => { const result: Partial> = {}; 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 7115f4f9a9ce8..12e7dfd743942 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 @@ -69,6 +69,7 @@ import { getLinesCausedPaddings, validateExtent, Series, + getOriginalAxisPosition, } from '../helpers'; import { getXDomain, XyEndzones } from './x_domain'; import { getLegendAction } from './legend_action'; @@ -246,8 +247,12 @@ export function XYChart({ const xTitle = xAxisConfig?.title || (xAxisColumn && xAxisColumn.name); const yAxesMap = { - left: yAxesConfiguration.find(({ position }) => position === Position.Left), - right: yAxesConfiguration.find(({ position }) => position === Position.Right), + left: yAxesConfiguration.find( + ({ position }) => position === getAxisPosition(Position.Left, shouldRotate) + ), + right: yAxesConfiguration.find( + ({ position }) => position === getAxisPosition(Position.Right, shouldRotate) + ), }; const titles = getLayersTitles( @@ -326,10 +331,11 @@ export function XYChart({ ].filter(Boolean); const shouldHideDetails = annotationsLayers.length > 0 ? annotationsLayers[0].hide : false; - const linesPaddings = !shouldHideDetails ? getLinesCausedPaddings(visualConfigs, yAxesMap) : {}; + const linesPaddings = !shouldHideDetails ? getLinesCausedPaddings(visualConfigs, yAxesMap, shouldRotate) : {}; const getYAxesStyle = (axis: AxisConfiguration) => { const tickVisible = axis.showLabels; + const position = getOriginalAxisPosition(axis.position, shouldRotate); const style = { tickLabel: { @@ -337,9 +343,9 @@ export function XYChart({ visible: tickVisible, rotation: axis.labelsOrientation, padding: - linesPaddings[axis.position] != null + linesPaddings[position] != null ? { - inner: linesPaddings[axis.position], + inner: linesPaddings[position], } : undefined, }, @@ -347,9 +353,9 @@ export function XYChart({ visible: axis.showTitle, // if labels are not visible add the padding to the title padding: - !tickVisible && linesPaddings[axis.position] != null + !tickVisible && linesPaddings[position] != null ? { - inner: linesPaddings[axis.position], + inner: linesPaddings[position], } : undefined, }, @@ -793,6 +799,7 @@ export function XYChart({ isHorizontal={shouldRotate} paddingMap={linesPaddings} titles={titles} + yAxesMap={yAxesMap} /> ) : null} {rangeAnnotations.length || groupedLineAnnotations.length ? ( 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 d6dc9c0ce5f67..c4aebbfb96902 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/annotations.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/annotations.tsx @@ -16,6 +16,7 @@ import type { } from '../../common/types'; import { getBaseIconPlacement } from '../components'; import { hasIcon, iconSet } from './icon'; +import { AxesMap, getOriginalAxisPosition } from './axes_configuration'; export const LINES_MARKER_SIZE = 20; @@ -36,7 +37,8 @@ const isExtendedDecorationConfig = ( // Note: it does not take into consideration whether the reference line is in view or not export const getLinesCausedPaddings = ( visualConfigs: Array, - axesMap: Record<'left' | 'right', unknown> + axesMap: AxesMap, + shouldRotate: boolean ) => { // collect all paddings for the 4 axis: if any text is detected double it. const paddings: Partial> = {}; @@ -49,7 +51,11 @@ export const getLinesCausedPaddings = ( const iconPosition = isExtendedDecorationConfig(config) ? config.iconPosition : undefined; if (position && (hasIcon(icon) || textVisibility)) { - const placement = getBaseIconPlacement(iconPosition, axesMap, position); + const placement = getBaseIconPlacement( + iconPosition, + axesMap, + getOriginalAxisPosition(position, shouldRotate) + ); paddings[placement] = Math.max( paddings[placement] || 0, LINES_MARKER_SIZE * (textVisibility ? 2 : 1) // double the padding size if there's text 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 5d38bac4ddb1e..98b0570e3097c 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 @@ -45,6 +45,8 @@ export interface AxisConfiguration extends Omit { export type GroupsConfiguration = AxisConfiguration[]; +export type AxesMap = Record<'left' | 'right', AxisConfiguration | undefined>; + export function isFormatterCompatible( formatter1: SerializedFieldFormat, formatter2: SerializedFieldFormat @@ -163,6 +165,27 @@ export function getAxisPosition(position: Position, shouldRotate: boolean) { return position; } +export function getOriginalAxisPosition(position: Position, shouldRotate: boolean) { + if (shouldRotate) { + switch (position) { + case Position.Bottom: { + return Position.Left; + } + case Position.Right: { + return Position.Bottom; + } + case Position.Top: { + return Position.Right; + } + case Position.Left: { + return Position.Top; + } + } + } + + return position; +} + function axisGlobalConfig(position: Position, yAxisConfigs?: YAxisConfig[]) { return yAxisConfigs?.find((axis) => !axis.id && axis.position === position) || {}; } @@ -187,7 +210,7 @@ export function getAxesConfiguration( groupId, formatter: formatFactory?.(series[groupId][0].fieldFormat), series: series[groupId].map(({ fieldFormat, ...currentSeries }) => currentSeries), - ...axisGlobalConfig(position, yAxisConfigs), + ...axisGlobalConfig(axis.position || Position.Left, yAxisConfigs), ...axis, position, }); @@ -200,7 +223,7 @@ export function getAxesConfiguration( groupId: LEFT_GLOBAL_AXIS_ID, formatter: formatFactory?.(series.left[0].fieldFormat), series: series.left.map(({ fieldFormat, ...currentSeries }) => currentSeries), - ...axisGlobalConfig(position, yAxisConfigs), + ...axisGlobalConfig(Position.Left, yAxisConfigs), position, }); } @@ -211,7 +234,7 @@ export function getAxesConfiguration( groupId: RIGHT_GLOBAL_AXIS_ID, formatter: formatFactory?.(series.right[0].fieldFormat), series: series.right.map(({ fieldFormat, ...currentSeries }) => currentSeries), - ...axisGlobalConfig(position, yAxisConfigs), + ...axisGlobalConfig(Position.Right, yAxisConfigs), position, }); } 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 ae55603debaa9..1b9ea44fac496 100644 --- a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts +++ b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts @@ -366,6 +366,7 @@ const yAxisConfigsToExpression = (yAxisConfigs: AxisConfig[]): Ast[] => { showLabels: [axis.showLabels ?? true], showGridLines: [axis.showGridLines ?? true], labelsOrientation: axis.labelsOrientation !== undefined ? [axis.labelsOrientation] : [], + scaleType: axis.scaleType ? [axis.scaleType] : [], }, }, ], From 5e3dffac84746e89c57635853e040c60d735b81b Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Fri, 17 Jun 2022 16:34:53 +0000 Subject: [PATCH 206/213] [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' --- .../reference_lines/reference_line_annotations.tsx | 7 ++++++- .../public/components/reference_lines/reference_lines.tsx | 7 ++++++- .../expression_xy/public/components/xy_chart.tsx | 4 +++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_line_annotations.tsx b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_line_annotations.tsx index 7214094f0e42f..b97c105e2ab3e 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_line_annotations.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_line_annotations.tsx @@ -10,7 +10,12 @@ import { AnnotationDomainType, LineAnnotation, Position, RectAnnotation } from ' import { euiLightVars } from '@kbn/ui-theme'; import React, { FC } from 'react'; import { FieldFormat } from '@kbn/field-formats-plugin/common'; -import { AxesMap, AxisConfiguration, getOriginalAxisPosition, LINES_MARKER_SIZE } from '../../helpers'; +import { + AxesMap, + AxisConfiguration, + getOriginalAxisPosition, + LINES_MARKER_SIZE, +} from '../../helpers'; import { AvailableReferenceLineIcon, FillStyle, diff --git a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_lines.tsx b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_lines.tsx index 28d6bb7263e69..a648fbc7d357e 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_lines.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_lines.tsx @@ -12,7 +12,12 @@ import React from 'react'; import { Position } from '@elastic/charts'; import type { FieldFormat } from '@kbn/field-formats-plugin/common'; import type { CommonXYReferenceLineLayerConfig, ReferenceLineConfig } from '../../../common/types'; -import { AxesMap, GroupsConfiguration, isReferenceLine, LayersAccessorsTitles } from '../../helpers'; +import { + AxesMap, + GroupsConfiguration, + isReferenceLine, + LayersAccessorsTitles, +} from '../../helpers'; import { ReferenceLineLayer } from './reference_line_layer'; import { ReferenceLine } from './reference_line'; import { getNextValuesForReferenceLines } from './utils'; 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 bf873e762b363..c32b651729573 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 @@ -335,7 +335,9 @@ export function XYChart({ ].filter(Boolean); const shouldHideDetails = annotationsLayers.length > 0 ? annotationsLayers[0].hide : false; - const linesPaddings = !shouldHideDetails ? getLinesCausedPaddings(visualConfigs, yAxesMap, shouldRotate) : {}; + const linesPaddings = !shouldHideDetails + ? getLinesCausedPaddings(visualConfigs, yAxesMap, shouldRotate) + : {}; const getYAxesStyle = (axis: AxisConfiguration) => { const tickVisible = axis.showLabels; From c64c1fdd0a7e26e1edd266ead3bf6facf1ea372a Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Fri, 1 Jul 2022 16:56:52 +0300 Subject: [PATCH 207/213] fix some problems after resolving merge confilcts --- .../expression_functions/reference_line_decoration_config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line_decoration_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line_decoration_config.ts index 36a00ea768476..ad25d7e0c3226 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line_decoration_config.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line_decoration_config.ts @@ -29,7 +29,7 @@ export const referenceLineDecorationConfigFunction: ReferenceLineDecorationConfi ...commonDecorationConfigArgs, position: { types: ['string'], - options: [Position.Right, Position.Left], + options: [Position.Right, Position.Left, Position.Bottom], help: i18n.translate('expressionXY.referenceLine.position.help', { defaultMessage: 'Position of axis (first axis of that position) to which the reference line belongs.', From 518390961d0422bf4fe5acd96a87c6b708d2991d Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Fri, 1 Jul 2022 17:56:37 +0300 Subject: [PATCH 208/213] Fix snapshot --- .../public/components/__snapshots__/xy_chart.test.tsx.snap | 4 ++-- 1 file changed, 2 insertions(+), 2 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 0c6bcdf68c359..ebc22bb20af9e 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 @@ -1594,7 +1594,7 @@ exports[`XYChart component it renders horizontal bar 1`] = ` } hide={false} id="x" - position="right" + position="left" style={ Object { "axisTitle": Object { @@ -4082,7 +4082,7 @@ exports[`XYChart component it renders stacked horizontal bar 1`] = ` } hide={false} id="x" - position="right" + position="left" style={ Object { "axisTitle": Object { From 6f8b830303f22803103f8c413d6d6be44d646709 Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Mon, 4 Jul 2022 19:20:51 +0300 Subject: [PATCH 209/213] Fix comments --- .../public/components/legend_action.tsx | 34 ++++++---- .../public/components/xy_chart.tsx | 38 +++++++---- .../public/helpers/color_assignment.ts | 65 ++++++++++--------- .../public/helpers/data_layers.tsx | 33 +++++----- .../expression_xy/public/helpers/layers.ts | 5 +- 5 files changed, 98 insertions(+), 77 deletions(-) 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 2dcc0deb91e4f..aafef6a9a332a 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 @@ -43,21 +43,27 @@ export const getLegendAction = ( const { table } = layer; - const data: FilterEvent['data']['data'] = []; - - series.splitAccessors.forEach((value, key) => { - const rowIndex = table.rows.findIndex((row) => { - return row[key] === value; - }); - if (rowIndex !== -1) { - data.push({ - row: rowIndex, - column: table.columns.findIndex((column) => column.id === key), - value: table.rows[rowIndex][key], - table, + const data = [...series.splitAccessors].reduce( + (acc, [accessor, value]) => { + const rowIndex = table.rows.findIndex((row) => { + return row[accessor] === value; }); - } - }); + if (rowIndex !== -1) { + return [ + ...acc, + { + row: rowIndex, + column: table.columns.findIndex((column) => column.id === accessor), + value: table.rows[rowIndex][accessor], + table, + }, + ]; + } + + return acc; + }, + [] + ); if (data.length === 0) { return null; 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 059958916e54b..c3b29de76b398 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 @@ -490,7 +490,7 @@ export function XYChart({ ? formatFactory(xFormat) : xAxisFormatter; - let rowIndex = table.rows.findIndex((row) => { + const rowIndex = table.rows.findIndex((row) => { if (xAccessor) { if (formattedDatatables[layer.layerId]?.formattedColumns[xAccessor]) { // stringify the value to compare with the chart value @@ -505,23 +505,37 @@ export function XYChart({ row: rowIndex, column: table.columns.findIndex((col) => col.id === xAccessor), value: xAccessor ? table.rows[rowIndex][xAccessor] : xyGeometry.x, + table, }, ]; + let splitPoints: FilterEvent['data']['data'] = []; + if (xySeries.seriesKeys.length > 1) { - xySeries.splitAccessors.forEach((value, key) => { - rowIndex = table.rows.findIndex((row) => { - return row[key] === value; - }); - points.push({ - row: rowIndex, - column: table.columns.findIndex((column) => column.id === key), - value: table.rows[rowIndex][key], - }); - }); + splitPoints = [...xySeries.splitAccessors].reduce( + (acc, [accessor, value]) => { + const splitPointRowIndex = table.rows.findIndex((row) => { + return row[accessor] === value; + }); + if (splitPointRowIndex !== -1) { + return [ + ...acc, + { + row: splitPointRowIndex, + column: table.columns.findIndex((column) => column.id === accessor), + value: table.rows[splitPointRowIndex][accessor], + table, + }, + ]; + } + + return acc; + }, + [] + ); } const context: FilterEvent['data'] = { - data: points.map(({ row, column, value }) => ({ row, column, value, table })), + data: [...points, ...splitPoints], }; onClickValue(context); }; 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 150448cfca853..90149633bc3d8 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,12 +7,13 @@ */ import { mapValues } from 'lodash'; -import { Datatable } from '@kbn/expressions-plugin'; +import { Datatable, DatatableRow } from '@kbn/expressions-plugin'; import { euiLightVars } from '@kbn/ui-theme'; import { getAccessorByDimension, getColumnByAccessor, } from '@kbn/visualizations-plugin/common/utils'; +import type { ExpressionValueVisDimension } from '@kbn/visualizations-plugin/common'; import { FormatFactory } from '../types'; import { isDataLayer } from './visualization'; import { CommonXYDataLayerConfig, CommonXYLayerConfig } from '../../common'; @@ -29,54 +30,56 @@ export type ColorAssignments = Record< } >; +function getSplitName( + splitAccessors: Array = [], + table: Datatable, + row: DatatableRow, + formatFactory: FormatFactory +) { + return splitAccessors.reduce((splitName, accessor) => { + const splitColumn = getColumnByAccessor(accessor, table.columns); + if (!splitColumn) return; + const splitAccessor = getAccessorByDimension(accessor, table.columns); + const columnFormatter = splitAccessor && formatFactory(getFormat(table.columns, splitAccessor)); + const name = columnFormatter ? columnFormatter.convert(row[splitAccessor]) : row[splitAccessor]; + if (splitName) { + return `${splitName} - ${name}`; + } else { + return name; + } + }, ''); +} + export const getAllSeries = ( table: Datatable, splitAccessors: CommonXYDataLayerConfig['splitAccessors'] = [], - accessors: CommonXYDataLayerConfig['accessors'], + accessors: Array, columnToLabel: CommonXYDataLayerConfig['columnToLabel'], titles: LayerAccessorsTitles, formatFactory: FormatFactory ) => { - const allSeries: string[] = []; if (!table) { return []; } const columnToLabelMap = columnToLabel ? JSON.parse(columnToLabel) : {}; - table.rows.forEach((row) => { - let seriesName = ''; - splitAccessors.forEach((accessor) => { - const splitColumn = getColumnByAccessor(accessor, table.columns); - if (!splitColumn) return; - const splitAccessor = getAccessorByDimension(accessor, table.columns); - const columnFormatter = - splitAccessor && formatFactory(getFormat(table.columns, splitAccessor)); - const name = columnFormatter - ? columnFormatter.convert(row[splitAccessor]) - : row[splitAccessor]; - if (seriesName) { - seriesName += ` - ${name}`; - } else { - seriesName = name; - } - }); + return table.rows.reduce((acc, row) => { + const splitName = getSplitName(splitAccessors, table, row, formatFactory); - accessors.forEach((accessor) => { + const allRowSeries = accessors.reduce((names, accessor) => { const yAccessor = getAccessorByDimension(accessor, table.columns); const yTitle = columnToLabelMap[yAccessor] ?? titles?.yTitles?.[yAccessor] ?? null; - let name; - if (seriesName) { - name = accessors.length > 1 ? `${seriesName} - ${yTitle}` : seriesName; + if (splitName) { + return [...names, accessors.length > 1 ? `${splitName} - ${yTitle}` : splitName]; } else { - name = yTitle; - } - if (!allSeries.includes(name)) { - allSeries.push(name); + return [...names, yTitle]; } - }); - }); - return allSeries; + }, []); + + // need only uniq values + return [...new Set([...acc, ...allRowSeries])]; + }, []); }; export function getColorAssignments( 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 008579882b8b9..e39caf52e622c 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 @@ -209,21 +209,22 @@ function getSplitValues( return []; } - const splitValues: Array = []; - splitAccessorsMap.forEach((value, key) => { + return [...splitAccessorsMap].reduce>((acc, [splitAccessor, value]) => { const split = splitAccessors.find( - (accessor) => getAccessorByDimension(accessor, columns) === key + (accessor) => getAccessorByDimension(accessor, columns) === splitAccessor ); if (split) { const splitColumnId = getAccessorByDimension(split, columns); const splitFormatByAccessor = getFormat(columns, split); const splitFormatter = formatFactory(splitFormatByAccessor); - splitValues.push( - alreadyFormattedColumns[splitColumnId] ? value : splitFormatter.convert(value) - ); + return [ + ...acc, + alreadyFormattedColumns[splitColumnId] ? value : splitFormatter.convert(value), + ]; } - }); - return splitValues; + + return acc; + }, []); } export const getSeriesName: GetSeriesNameFn = ( @@ -366,11 +367,10 @@ export const getSeriesProps: GetSeriesPropsFn = ({ const scaleType = yAxis?.scaleType || ScaleType.Linear; const isBarChart = layer.seriesType === SeriesTypes.BAR; const xColumnId = layer.xAccessor && getAccessorByDimension(layer.xAccessor, table.columns); - const splitColumnIds = layer.splitAccessors - ? layer.splitAccessors.map((splitAccessor) => { - return getAccessorByDimension(splitAccessor, table.columns); - }) - : []; + const splitColumnIds = + layer.splitAccessors?.map((splitAccessor) => { + return getAccessorByDimension(splitAccessor, table.columns); + }) || []; const enableHistogramMode = layer.isHistogram && (isStacked || !splitColumnIds.length) && @@ -395,11 +395,10 @@ export const getSeriesProps: GetSeriesPropsFn = ({ // To not display them in the legend, they need to be filtered out. let rows = formattedTable.rows.filter( (row) => - !(xColumnId && typeof row[xColumnId] === 'undefined') && + !(xColumnId && row[xColumnId] === undefined) && !( - splitColumnIds.length && - splitColumnIds.some((splitColumnId) => typeof row[splitColumnId] === 'undefined') && - typeof row[accessor] === 'undefined' + splitColumnIds.some((splitColumnId) => row[splitColumnId] === undefined) && + row[accessor] === 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 9cdf76382de15..c76d1fba4acc2 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/layers.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/layers.ts @@ -84,13 +84,12 @@ export function getFilteredLayers(layers: CommonXYLayerConfig[]) { !accessors.length || !table || table.rows.length === 0 || - (xAccessor && - table.rows.every((row) => xAccessor && typeof row[xAccessor] === 'undefined')) || + (xAccessor && table.rows.every((row) => xAccessor && row[xAccessor] === undefined)) || // stacked percentage bars have no xAccessors but splitAccessor with undefined values in them when empty (!xAccessor && splitAccessors.length && table.rows.every((row) => - splitAccessors.every((splitAccessor) => typeof row[splitAccessor] === 'undefined') + splitAccessors.every((splitAccessor) => row[splitAccessor] === undefined) )) ); } From 8e9ba98d816b0e30d35b7ef346d9acaa6ebea958 Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Thu, 7 Jul 2022 12:22:15 +0300 Subject: [PATCH 210/213] Fix performance issue --- .../__snapshots__/xy_chart.test.tsx.snap | 977 +++++++++++++++++- .../public/components/data_layers.tsx | 6 +- .../public/components/legend_action.test.tsx | 25 +- .../public/components/legend_action.tsx | 12 +- .../components/tooltip/tooltip.test.tsx | 7 +- .../public/components/tooltip/tooltip.tsx | 2 +- .../public/components/xy_chart.tsx | 7 +- .../public/helpers/axes_configuration.test.ts | 5 +- .../public/helpers/color_assignment.ts | 54 +- .../public/helpers/data_layers.tsx | 17 +- .../expression_xy/public/helpers/layers.ts | 36 +- 11 files changed, 1080 insertions(+), 68 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 ebc22bb20af9e..71e3b38b57072 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 @@ -413,6 +413,62 @@ exports[`XYChart component it renders area 1`] = ` = ({ @@ -73,8 +75,9 @@ export const DataLayers: FC = ({ formattedDatatables, chartHasMoreThanOneBarSeries, defaultXScaleType, + fieldFormats, }) => { - const colorAssignments = getColorAssignments(layers, formatFactory, titles); + const colorAssignments = getColorAssignments(layers, titles, fieldFormats, formattedDatatables); return ( <> {layers.flatMap((layer) => @@ -114,6 +117,7 @@ export const DataLayers: FC = ({ emphasizeFitting, fillOpacity, defaultXScaleType, + fieldFormats, }); const index = `${layer.layerId}-${accessorIndex}`; 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 9dd6ccdcbc9a0..f82428993ce46 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 @@ -17,7 +17,8 @@ import { LayerTypes } from '../../common/constants'; import { getLegendAction } from './legend_action'; import { LegendActionPopover } from './legend_action_popover'; import { mockPaletteOutput } from '../../common/__mocks__'; -import { FormatFactory } from '../types'; +import { FieldFormat } from '@kbn/field-formats-plugin/common'; +import { LayerFieldFormats } from '../helpers'; const table: Datatable = { type: 'datatable', @@ -172,19 +173,25 @@ const sampleLayer: DataLayerConfig = { table, }; -const formatFactory = (() => - ({ - convert(x: unknown) { - return x; - }, - } as unknown)) as FormatFactory; - describe('getLegendAction', function () { let wrapperProps: LegendActionProps; const Component: ComponentType = getLegendAction( [sampleLayer], jest.fn(), - formatFactory, + { + first: { + splitSeriesAccessors: { + splitAccessorId: { + format: { id: 'string' }, + formatter: { + convert(x: unknown) { + return x; + }, + } as FieldFormat, + }, + }, + } as unknown as LayerFieldFormats, + }, { first: { table, 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 aafef6a9a332a..abac767eeac6c 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 @@ -11,14 +11,18 @@ import type { LegendAction, XYChartSeriesIdentifier } from '@elastic/charts'; import { getAccessorByDimension } from '@kbn/visualizations-plugin/common/utils'; import type { FilterEvent } from '../types'; import type { CommonXYDataLayerConfig } from '../../common'; -import type { FormatFactory } from '../types'; import { LegendActionPopover } from './legend_action_popover'; -import { DatatablesWithFormatInfo, getSeriesName, LayersAccessorsTitles } from '../helpers'; +import { + DatatablesWithFormatInfo, + getSeriesName, + LayersAccessorsTitles, + LayersFieldFormats, +} from '../helpers'; export const getLegendAction = ( dataLayers: CommonXYDataLayerConfig[], onFilter: (data: FilterEvent['data']) => void, - formatFactory: FormatFactory, + fieldFormats: LayersFieldFormats, formattedDatatables: DatatablesWithFormatInfo, titles: LayersAccessorsTitles ): LegendAction => @@ -82,7 +86,7 @@ export const getLegendAction = ( splitAccessors: layer.splitAccessors, accessorsCount: layer.accessors.length, columns: table.columns, - formatFactory, + splitAccessorsFormats: fieldFormats[layer.layerId].splitSeriesAccessors, alreadyFormattedColumns: formattedDatatables[layer.layerId].formattedColumns, columnToLabelMap: layer.columnToLabel ? JSON.parse(layer.columnToLabel) : {}, }, diff --git a/src/plugins/chart_expressions/expression_xy/public/components/tooltip/tooltip.test.tsx b/src/plugins/chart_expressions/expression_xy/public/components/tooltip/tooltip.test.tsx index 4e427c82cf175..9ba90e03b8629 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/tooltip/tooltip.test.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/tooltip/tooltip.test.tsx @@ -86,7 +86,12 @@ describe('Tooltip', () => { [layerId]: { xAccessors: { [xAccessor as string]: { id: 'number' } }, yAccessors: { [accessor]: { id: 'string' } }, - splitSeriesAccessors: { [splitAccessors[0] as string]: { id: 'date' } }, + splitSeriesAccessors: { + [splitAccessors[0] as string]: { + format: { id: 'date' }, + formatter: {} as FieldFormat, + }, + }, splitRowAccessors: { [splitRowAccessor]: { id: 'number' } }, splitColumnAccessors: { [splitColumnAccessor]: { id: 'number' } }, }, diff --git a/src/plugins/chart_expressions/expression_xy/public/components/tooltip/tooltip.tsx b/src/plugins/chart_expressions/expression_xy/public/components/tooltip/tooltip.tsx index 6c7a3e586e8e6..7ab7c9f549e24 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/tooltip/tooltip.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/tooltip/tooltip.tsx @@ -78,7 +78,7 @@ export const Tooltip: FC = ({ seriesIdentifier.splitAccessors.forEach((splitValue, key) => { const splitSeriesFormatter = formattedColumns[key] ? null - : formatFactory(layerFormats.splitSeriesAccessors[key]); + : layerFormats.splitSeriesAccessors[key].formatter; const label = layerTitles?.splitSeriesTitles?.[key]; const value = splitSeriesFormatter ? splitSeriesFormatter.convert(splitValue) : `${splitValue}`; 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 c3b29de76b398..6b8dc2df9b13c 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 @@ -221,8 +221,8 @@ export function XYChart({ ); const fieldFormats = useMemo( - () => getLayersFormats(dataLayers, { splitColumnAccessor, splitRowAccessor }), - [dataLayers, splitColumnAccessor, splitRowAccessor] + () => getLayersFormats(dataLayers, { splitColumnAccessor, splitRowAccessor }, formatFactory), + [dataLayers, splitColumnAccessor, splitRowAccessor, formatFactory] ); if (dataLayers.length === 0) { @@ -709,7 +709,7 @@ export function XYChart({ onElementClick={interactive ? clickHandler : undefined} legendAction={ interactive - ? getLegendAction(dataLayers, onClickValue, formatFactory, formattedDatatables, titles) + ? getLegendAction(dataLayers, onClickValue, fieldFormats, formattedDatatables, titles) : undefined } showLegendExtra={isHistogramViz && valuesInLegend} @@ -823,6 +823,7 @@ export function XYChart({ formattedDatatables={formattedDatatables} chartHasMoreThanOneBarSeries={chartHasMoreThanOneBarSeries} defaultXScaleType={defaultXScaleType} + fieldFormats={fieldFormats} /> )} {referenceLineLayers.length ? ( 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 fe9a4ecec6a07..35019d75e0554 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,6 +6,7 @@ * Side Public License, v 1. */ +import { FieldFormat } from '@kbn/field-formats-plugin/common'; import { DataLayerConfig, YAxisConfigResult } from '../../common'; import { LayerTypes } from '../../common/constants'; import { Datatable } from '@kbn/expressions-plugin/public'; @@ -256,7 +257,9 @@ describe('axes_configuration', () => { yAccessorId3: { id: 'currency', params: {} }, yAccessorId4: { id: 'currency', params: {} }, }, - splitSeriesAccessors: { d: { id: 'number', params: {} } }, + splitSeriesAccessors: { + d: { format: { id: 'number', params: {} }, formatter: {} as FieldFormat }, + }, splitColumnAccessors: {}, splitRowAccessors: {}, }, 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 90149633bc3d8..5460f7aceb135 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,18 +7,14 @@ */ import { mapValues } from 'lodash'; -import { Datatable, DatatableRow } from '@kbn/expressions-plugin'; +import { DatatableRow } from '@kbn/expressions-plugin'; import { euiLightVars } from '@kbn/ui-theme'; -import { - getAccessorByDimension, - getColumnByAccessor, -} from '@kbn/visualizations-plugin/common/utils'; +import { getAccessorByDimension } from '@kbn/visualizations-plugin/common/utils'; import type { ExpressionValueVisDimension } from '@kbn/visualizations-plugin/common'; -import { FormatFactory } from '../types'; import { isDataLayer } from './visualization'; import { CommonXYDataLayerConfig, CommonXYLayerConfig } from '../../common'; -import { getFormat } from './format'; -import { LayerAccessorsTitles } from './layers'; +import { LayerAccessorsTitles, LayerFieldFormats, LayersFieldFormats } from './layers'; +import { DatatablesWithFormatInfo, DatatableWithFormatInfo } from './data_layers'; export const defaultReferenceLineColor = euiLightVars.euiColorDarkShade; @@ -32,16 +28,16 @@ export type ColorAssignments = Record< function getSplitName( splitAccessors: Array = [], - table: Datatable, + formattedDatatable: DatatableWithFormatInfo, row: DatatableRow, - formatFactory: FormatFactory + fieldFormats: LayerFieldFormats ) { return splitAccessors.reduce((splitName, accessor) => { - const splitColumn = getColumnByAccessor(accessor, table.columns); - if (!splitColumn) return; - const splitAccessor = getAccessorByDimension(accessor, table.columns); - const columnFormatter = splitAccessor && formatFactory(getFormat(table.columns, splitAccessor)); - const name = columnFormatter ? columnFormatter.convert(row[splitAccessor]) : row[splitAccessor]; + const splitAccessor = getAccessorByDimension(accessor, formattedDatatable.table.columns); + const splitFormatterObj = fieldFormats.splitSeriesAccessors[splitAccessor]; + const name = formattedDatatable.formattedColumns[splitAccessor] + ? row[splitAccessor] + : splitFormatterObj.formatter.convert(row[splitAccessor]); if (splitName) { return `${splitName} - ${name}`; } else { @@ -51,30 +47,31 @@ function getSplitName( } export const getAllSeries = ( - table: Datatable, + formattedDatatable: DatatableWithFormatInfo, splitAccessors: CommonXYDataLayerConfig['splitAccessors'] = [], accessors: Array, columnToLabel: CommonXYDataLayerConfig['columnToLabel'], titles: LayerAccessorsTitles, - formatFactory: FormatFactory + fieldFormats: LayerFieldFormats ) => { - if (!table) { + if (!formattedDatatable.table) { return []; } const columnToLabelMap = columnToLabel ? JSON.parse(columnToLabel) : {}; - return table.rows.reduce((acc, row) => { - const splitName = getSplitName(splitAccessors, table, row, formatFactory); + return formattedDatatable.table.rows.reduce((acc, row) => { + const splitName = getSplitName(splitAccessors, formattedDatatable, row, fieldFormats); const allRowSeries = accessors.reduce((names, accessor) => { - const yAccessor = getAccessorByDimension(accessor, table.columns); + const yAccessor = getAccessorByDimension(accessor, formattedDatatable.table.columns); const yTitle = columnToLabelMap[yAccessor] ?? titles?.yTitles?.[yAccessor] ?? null; + let name = yTitle; if (splitName) { - return [...names, accessors.length > 1 ? `${splitName} - ${yTitle}` : splitName]; - } else { - return [...names, yTitle]; + name = accessors.length > 1 ? `${splitName} - ${yTitle}` : splitName; } + + return names.includes(name) ? names : [...names, name]; }, []); // need only uniq values @@ -84,8 +81,9 @@ export const getAllSeries = ( export function getColorAssignments( layers: CommonXYLayerConfig[], - formatFactory: FormatFactory, - titles: LayerAccessorsTitles + titles: LayerAccessorsTitles, + fieldFormats: LayersFieldFormats, + formattedDatatables: DatatablesWithFormatInfo ): ColorAssignments { const layersPerPalette: Record = {}; @@ -105,12 +103,12 @@ export function getColorAssignments( const seriesPerLayer = paletteLayers.map((layer) => { const allSeries = getAllSeries( - layer.table, + formattedDatatables[layer.layerId], layer.splitAccessors, layer.accessors, layer.columnToLabel, titles, - formatFactory + fieldFormats[layer.layerId] ) || []; return { numberOfSeries: allSeries.length, allSeries }; 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 e39caf52e622c..bb1bdbe113dbc 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 @@ -29,7 +29,7 @@ import { FormatFactory } from '../types'; import { getSeriesColor } from './state'; import { ColorAssignments } from './color_assignment'; import { GroupsConfiguration } from './axes_configuration'; -import { LayerAccessorsTitles } from './layers'; +import { LayerAccessorsTitles, LayerFieldFormats, LayersFieldFormats } from './layers'; import { getFormat } from './format'; type SeriesSpec = LineSeriesProps & BarSeriesProps & AreaSeriesProps; @@ -50,6 +50,7 @@ type GetSeriesPropsFn = (config: { fillOpacity?: number; formattedDatatableInfo: DatatableWithFormatInfo; defaultXScaleType: XScaleType; + fieldFormats: LayersFieldFormats; }) => SeriesSpec; type GetSeriesNameFn = ( @@ -58,7 +59,7 @@ type GetSeriesNameFn = ( splitAccessors: Array; accessorsCount: number; columns: Datatable['columns']; - formatFactory: FormatFactory; + splitAccessorsFormats: LayerFieldFormats['splitSeriesAccessors']; alreadyFormattedColumns: Record; columnToLabelMap: Record; }, @@ -203,7 +204,7 @@ function getSplitValues( splitAccessors: Array, alreadyFormattedColumns: Record, columns: Datatable['columns'], - formatFactory: FormatFactory + splitAccessorsFormats: LayerFieldFormats['splitSeriesAccessors'] ) { if (splitAccessorsMap.size < 0) { return []; @@ -215,8 +216,7 @@ function getSplitValues( ); if (split) { const splitColumnId = getAccessorByDimension(split, columns); - const splitFormatByAccessor = getFormat(columns, split); - const splitFormatter = formatFactory(splitFormatByAccessor); + const splitFormatter = splitAccessorsFormats[splitColumnId].formatter; return [ ...acc, alreadyFormattedColumns[splitColumnId] ? value : splitFormatter.convert(value), @@ -233,7 +233,7 @@ export const getSeriesName: GetSeriesNameFn = ( splitAccessors, accessorsCount, columns, - formatFactory, + splitAccessorsFormats, alreadyFormattedColumns, columnToLabelMap, }, @@ -248,7 +248,7 @@ export const getSeriesName: GetSeriesNameFn = ( splitAccessors, alreadyFormattedColumns, columns, - formatFactory + splitAccessorsFormats ); const key = data.seriesKeys[data.seriesKeys.length - 1]; @@ -357,6 +357,7 @@ export const getSeriesProps: GetSeriesPropsFn = ({ fillOpacity, formattedDatatableInfo, defaultXScaleType, + fieldFormats, }): SeriesSpec => { const { table, isStacked, markSizeAccessor } = layer; const isPercentage = layer.isPercentage; @@ -419,7 +420,7 @@ export const getSeriesProps: GetSeriesPropsFn = ({ accessorsCount: layer.accessors.length, alreadyFormattedColumns: formattedColumns, columns: formattedTable.columns, - formatFactory, + splitAccessorsFormats: fieldFormats[layer.layerId].splitSeriesAccessors, columnToLabelMap, }, titles 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 c76d1fba4acc2..359f1c27879a9 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/layers.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/layers.ts @@ -7,7 +7,11 @@ */ import { Datatable } from '@kbn/expressions-plugin/common'; -import { SerializedFieldFormat } from '@kbn/field-formats-plugin/common'; +import { + FieldFormat, + FormatFactory, + SerializedFieldFormat, +} from '@kbn/field-formats-plugin/common'; import { ExpressionValueVisDimension } from '@kbn/visualizations-plugin/common/expression_functions'; import { getAccessorByDimension, @@ -34,11 +38,15 @@ interface SplitAccessors { } export type AccessorsFieldFormats = Record; +export type SplitAccessorsFieldFormats = Record< + string, + { format: SerializedFieldFormat | undefined; formatter: FieldFormat } +>; export interface LayerFieldFormats { xAccessors: AccessorsFieldFormats; yAccessors: AccessorsFieldFormats; - splitSeriesAccessors: AccessorsFieldFormats; + splitSeriesAccessors: SplitAccessorsFieldFormats; splitColumnAccessors: AccessorsFieldFormats; splitRowAccessors: AccessorsFieldFormats; } @@ -128,7 +136,8 @@ const getYAccessorWithFieldFormat = ( export const getLayerFormats = ( { xAccessor, accessors, splitAccessors = [], table, isPercentage }: CommonXYDataLayerConfig, - { splitColumnAccessor, splitRowAccessor }: SplitAccessors + { splitColumnAccessor, splitRowAccessor }: SplitAccessors, + formatFactory: FormatFactory ): LayerFieldFormats => { const yAccessors: Array = accessors; const splitColumnAccessors: Array = splitAccessors; @@ -141,13 +150,17 @@ export const getLayerFormats = ( }), {} ), - splitSeriesAccessors: splitColumnAccessors?.reduce( - (formatters, splitAccessor) => ({ + splitSeriesAccessors: splitColumnAccessors?.reduce((formatters, splitAccessor) => { + const formatObj = getAccessorWithFieldFormat(splitAccessor, table.columns); + const accessor = Object.keys(formatObj)[0]; + return { ...formatters, - ...getAccessorWithFieldFormat(splitAccessor, table.columns), - }), - {} - ), + [accessor]: { + format: formatObj[accessor], + formatter: formatFactory(formatObj[accessor]), + }, + }; + }, {}), splitColumnAccessors: getAccessorWithFieldFormat(splitColumnAccessor, table.columns), splitRowAccessors: getAccessorWithFieldFormat(splitRowAccessor, table.columns), }; @@ -155,12 +168,13 @@ export const getLayerFormats = ( export const getLayersFormats = ( layers: CommonXYDataLayerConfig[], - splitAccessors: SplitAccessors + splitAccessors: SplitAccessors, + formatFactory: FormatFactory ): LayersFieldFormats => layers.reduce( (formatters, layer) => ({ ...formatters, - [layer.layerId]: getLayerFormats(layer, splitAccessors), + [layer.layerId]: getLayerFormats(layer, splitAccessors, formatFactory), }), {} ); From cac17280432c9ac04da2d2033507fd69ecf986d7 Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Thu, 7 Jul 2022 13:49:32 +0300 Subject: [PATCH 211/213] Fix CI --- .../components/tooltip/tooltip.test.tsx | 2 +- .../public/helpers/color_assignment.test.ts | 222 +++++++++++++----- .../public/helpers/color_assignment.ts | 1 + 3 files changed, 162 insertions(+), 63 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/public/components/tooltip/tooltip.test.tsx b/src/plugins/chart_expressions/expression_xy/public/components/tooltip/tooltip.test.tsx index 9ba90e03b8629..389c6c7571b24 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/tooltip/tooltip.test.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/tooltip/tooltip.test.tsx @@ -89,7 +89,7 @@ describe('Tooltip', () => { splitSeriesAccessors: { [splitAccessors[0] as string]: { format: { id: 'date' }, - formatter: {} as FieldFormat, + formatter: { convert: (value) => `formatted-date-${value}` } as FieldFormat, }, }, splitRowAccessors: { [splitRowAccessor]: { id: 'number' } }, 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 bbc9fe5c33c12..4a4c0ac5b7af1 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 @@ -8,9 +8,10 @@ import { getColorAssignments } from './color_assignment'; import type { DataLayerConfig } from '../../common'; -import type { FormatFactory } from '../types'; import { LayerTypes } from '../../common/constants'; import { Datatable } from '@kbn/expressions-plugin'; +import { FieldFormat } from '@kbn/field-formats-plugin/common'; +import { LayersFieldFormats } from './layers'; describe('color_assignment', () => { const tables: Record = { @@ -83,23 +84,55 @@ describe('color_assignment', () => { }, ]; - const formatFactory = (() => - ({ - convert(x: unknown) { - return x; + const fieldFormats = { + first: { + splitSeriesAccessors: { + split1: { + format: { id: 'string' }, + formatter: { + convert: (x) => x, + } as FieldFormat, + }, + }, + }, + second: { + splitSeriesAccessors: { + split2: { + format: { id: 'string' }, + formatter: { + convert: (x) => x, + } as FieldFormat, + }, }, - } as unknown)) as FormatFactory; + }, + } as unknown as LayersFieldFormats; + + const formattedDatatables = { + first: { + table: tables['1'], + formattedColumns: {}, + }, + second: { + table: tables['2'], + formattedColumns: {}, + }, + }; describe('totalSeriesCount', () => { it('should calculate total number of series per palette', () => { - const assignments = getColorAssignments(layers, formatFactory, { - yTitles: { - y1: 'test1', - y2: 'test2', - y3: 'test3', - y4: 'test4', + const assignments = getColorAssignments( + layers, + { + yTitles: { + y1: 'test1', + y2: 'test2', + y3: 'test3', + y4: 'test4', + }, }, - }); + fieldFormats, + formattedDatatables + ); // two y accessors, with 3 splitted series expect(assignments.palette1.totalSeriesCount).toEqual(2 * 3); expect(assignments.palette2.totalSeriesCount).toEqual(2 * 3); @@ -108,7 +141,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 }], - formatFactory, { yTitles: { y1: 'test1', @@ -116,7 +148,9 @@ describe('color_assignment', () => { y3: 'test3', y4: 'test4', }, - } + }, + fieldFormats, + formattedDatatables ); // two y accessors, with 3 splitted series, two times expect(assignments.palette1.totalSeriesCount).toEqual(2 * 3 + 2 * 3); @@ -126,7 +160,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, splitAccessors: undefined }], - formatFactory, { yTitles: { y1: 'test1', @@ -134,7 +167,9 @@ describe('color_assignment', () => { y3: 'test3', y4: 'test4', }, - } + }, + fieldFormats, + formattedDatatables ); // 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); @@ -151,13 +186,20 @@ describe('color_assignment', () => { }, layers[1], ]; + fieldFormats.first.splitSeriesAccessors.split1.formatter.convert = formatMock; + const newFormattedDatatables = { + first: { + formattedColumns: formattedDatatables.first.formattedColumns, + table: { + ...formattedDatatables.first.table, + rows: [{ split1: complexObject }, { split1: 'abc' }], + }, + }, + second: formattedDatatables.second, + }; const assignments = getColorAssignments( newLayers, - (() => - ({ - convert: formatMock, - } as unknown)) as FormatFactory, { yTitles: { y1: 'test1', @@ -165,8 +207,13 @@ describe('color_assignment', () => { y3: 'test3', y4: 'test4', }, - } + }, + fieldFormats, + newFormattedDatatables ); + + fieldFormats.first.splitSeriesAccessors.split1.formatter.convert = (x) => x as string; + expect(formatMock).toHaveBeenCalledWith(complexObject); expect(assignments.palette1.totalSeriesCount).toEqual(2 * 2); expect(assignments.palette2.totalSeriesCount).toEqual(2 * 3); expect(formatMock).toHaveBeenCalledWith(complexObject); @@ -174,14 +221,26 @@ describe('color_assignment', () => { it('should handle missing columns', () => { const newLayers = [{ ...layers[0], table: { ...tables['1'], columns: [] } }, layers[1]]; - const assignments = getColorAssignments(newLayers, formatFactory, { - yTitles: { - y1: 'test1', - y2: 'test2', - y3: 'test3', - y4: 'test4', + const newFormattedDatatables = { + first: { + formattedColumns: formattedDatatables.first.formattedColumns, + table: { ...formattedDatatables.first.table, columns: [] }, + }, + second: formattedDatatables.second, + }; + const assignments = getColorAssignments( + newLayers, + { + yTitles: { + y1: 'test1', + y2: 'test2', + y3: 'test3', + y4: 'test4', + }, }, - }); + fieldFormats, + newFormattedDatatables + ); // if the split column is missing, just assume a single split expect(assignments.palette1.totalSeriesCount).toEqual(2); @@ -190,14 +249,19 @@ describe('color_assignment', () => { describe('getRank', () => { it('should return the correct rank for a series key', () => { - const assignments = getColorAssignments(layers, formatFactory, { - yTitles: { - y1: 'test1', - y2: 'test2', - y3: 'test3', - y4: 'test4', + const assignments = getColorAssignments( + layers, + { + yTitles: { + y1: 'test1', + y2: 'test2', + y3: 'test3', + y4: 'test4', + }, }, - }); + fieldFormats, + formattedDatatables + ); // 3 series in front of 2/y2 - 1/y1, 1/y2 and 2/y1 expect(assignments.palette1.getRank(layers[0], '2 - test2')).toEqual(3); // 1 series in front of 1/y4 - 1/y3 @@ -206,14 +270,19 @@ describe('color_assignment', () => { 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, { - yTitles: { - y1: 'test1', - y2: 'test2', - y3: 'test3', - y4: 'test4', + const assignments = getColorAssignments( + newLayers, + { + yTitles: { + y1: 'test1', + y2: 'test2', + y3: 'test3', + y4: 'test4', + }, }, - }); + fieldFormats, + formattedDatatables + ); // 3 series in front of 2/y2 - 1/y1, 1/y2 and 2/y1 expect(assignments.palette1.getRank(newLayers[0], '2 - test2')).toEqual(3); // 2 series in front for the current layer (1/y3, 1/y4), plus all 6 series from the first layer @@ -225,14 +294,19 @@ describe('color_assignment', () => { layers[0], { ...layers[1], palette: layers[0].palette, splitAccessors: undefined }, ]; - const assignments = getColorAssignments(newLayers, formatFactory, { - yTitles: { - y1: 'test1', - y2: 'test2', - y3: 'test3', - y4: 'test4', + const assignments = getColorAssignments( + newLayers, + { + yTitles: { + y1: 'test1', + y2: 'test2', + y3: 'test3', + y4: 'test4', + }, }, - }); + fieldFormats, + formattedDatatables + ); // 3 series in front of 2/y2 - 1/y1, 1/y2 and 2/y1 expect(assignments.palette1.getRank(newLayers[0], '2 - test2')).toEqual(3); // 1 series in front for the current layer (y3), plus all 6 series from the first layer @@ -247,13 +321,21 @@ describe('color_assignment', () => { }, layers[1], ]; + fieldFormats.first.splitSeriesAccessors.split1.formatter.convert = (value: unknown) => + (typeof value === 'object' ? 'formatted' : value) as string; + const newFormattedDatatables = { + first: { + formattedColumns: formattedDatatables.first.formattedColumns, + table: { + ...formattedDatatables.first.table, + rows: [{ split1: 'abc' }, { split1: { aProp: 123 } }], + }, + }, + second: formattedDatatables.second, + }; const assignments = getColorAssignments( newLayers, - (() => - ({ - convert: (value: unknown) => (typeof value === 'object' ? 'formatted' : value), - } as unknown)) as FormatFactory, { yTitles: { y1: 'test1', @@ -261,23 +343,39 @@ describe('color_assignment', () => { y3: 'test3', y4: 'test4', }, - } + }, + fieldFormats, + newFormattedDatatables ); + + fieldFormats.first.splitSeriesAccessors.split1.formatter.convert = (x) => x as string; // 3 series in front of (complex object)/y1 - abc/y1, abc/y2 expect(assignments.palette1.getRank(layers[0], 'formatted - test1')).toEqual(2); }); it('should handle missing columns', () => { const newLayers = [{ ...layers[0], table: { ...tables['1'], columns: [] } }, layers[1]]; + const newFormattedDatatables = { + first: { + formattedColumns: formattedDatatables.first.formattedColumns, + table: { ...formattedDatatables.first.table, columns: [] }, + }, + second: formattedDatatables.second, + }; - const assignments = getColorAssignments(newLayers, formatFactory, { - yTitles: { - y1: 'test1', - y2: 'test2', - y3: 'test3', - y4: 'test4', + const assignments = getColorAssignments( + newLayers, + { + yTitles: { + y1: 'test1', + y2: 'test2', + y3: 'test3', + y4: 'test4', + }, }, - }); + fieldFormats, + newFormattedDatatables + ); // 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], 'test2')).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 5460f7aceb135..fcb160cf4a20d 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 @@ -33,6 +33,7 @@ function getSplitName( fieldFormats: LayerFieldFormats ) { return splitAccessors.reduce((splitName, accessor) => { + if (!formattedDatatable.table.columns.length) return; const splitAccessor = getAccessorByDimension(accessor, formattedDatatable.table.columns); const splitFormatterObj = fieldFormats.splitSeriesAccessors[splitAccessor]; const name = formattedDatatable.formattedColumns[splitAccessor] From ea7c9411d374c2839fb436adb4b8510815465796 Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Fri, 8 Jul 2022 12:07:51 +0300 Subject: [PATCH 212/213] Fix performance part 2 --- .../public/components/legend_action.tsx | 34 ++++++++----------- .../public/components/xy_chart.tsx | 34 +++++++------------ .../public/helpers/color_assignment.ts | 17 ++++++---- 3 files changed, 37 insertions(+), 48 deletions(-) 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 abac767eeac6c..6952823ec9512 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 @@ -47,27 +47,21 @@ export const getLegendAction = ( const { table } = layer; - const data = [...series.splitAccessors].reduce( - (acc, [accessor, value]) => { - const rowIndex = table.rows.findIndex((row) => { - return row[accessor] === value; - }); - if (rowIndex !== -1) { - return [ - ...acc, - { - row: rowIndex, - column: table.columns.findIndex((column) => column.id === accessor), - value: table.rows[rowIndex][accessor], - table, - }, - ]; - } + const data: FilterEvent['data']['data'] = []; - return acc; - }, - [] - ); + series.splitAccessors.forEach((value, accessor) => { + const rowIndex = table.rows.findIndex((row) => { + return row[accessor] === value; + }); + if (rowIndex !== -1) { + data.push({ + row: rowIndex, + column: table.columns.findIndex((column) => column.id === accessor), + value: table.rows[rowIndex][accessor], + table, + }); + } + }); if (data.length === 0) { return null; 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 6b8dc2df9b13c..41a05c43631a9 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 @@ -509,30 +509,22 @@ export function XYChart({ }, ]; - let splitPoints: FilterEvent['data']['data'] = []; + const splitPoints: FilterEvent['data']['data'] = []; if (xySeries.seriesKeys.length > 1) { - splitPoints = [...xySeries.splitAccessors].reduce( - (acc, [accessor, value]) => { - const splitPointRowIndex = table.rows.findIndex((row) => { - return row[accessor] === value; + xySeries.splitAccessors.forEach((value, accessor) => { + const splitPointRowIndex = table.rows.findIndex((row) => { + return row[accessor] === value; + }); + if (splitPointRowIndex !== -1) { + splitPoints.push({ + row: splitPointRowIndex, + column: table.columns.findIndex((column) => column.id === accessor), + value: table.rows[splitPointRowIndex][accessor], + table, }); - if (splitPointRowIndex !== -1) { - return [ - ...acc, - { - row: splitPointRowIndex, - column: table.columns.findIndex((column) => column.id === accessor), - value: table.rows[splitPointRowIndex][accessor], - table, - }, - ]; - } - - return acc; - }, - [] - ); + } + }); } const context: FilterEvent['data'] = { data: [...points, ...splitPoints], 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 fcb160cf4a20d..150c27434e1be 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 @@ -61,10 +61,12 @@ export const getAllSeries = ( const columnToLabelMap = columnToLabel ? JSON.parse(columnToLabel) : {}; - return formattedDatatable.table.rows.reduce((acc, row) => { + const allSeries: string[] = []; + + formattedDatatable.table.rows.forEach((row) => { const splitName = getSplitName(splitAccessors, formattedDatatable, row, fieldFormats); - const allRowSeries = accessors.reduce((names, accessor) => { + accessors.forEach((accessor) => { const yAccessor = getAccessorByDimension(accessor, formattedDatatable.table.columns); const yTitle = columnToLabelMap[yAccessor] ?? titles?.yTitles?.[yAccessor] ?? null; let name = yTitle; @@ -72,12 +74,13 @@ export const getAllSeries = ( name = accessors.length > 1 ? `${splitName} - ${yTitle}` : splitName; } - return names.includes(name) ? names : [...names, name]; - }, []); + if (!allSeries.includes(name)) { + allSeries.push(name); + } + }); + }); - // need only uniq values - return [...new Set([...acc, ...allRowSeries])]; - }, []); + return allSeries; }; export function getColorAssignments( From d54664ab8c6e658b636b891aa56b32525b8f5438 Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Mon, 11 Jul 2022 18:02:25 +0300 Subject: [PATCH 213/213] Fix legend actions --- .../expression_xy/public/components/legend_action.tsx | 2 +- .../expression_xy/public/components/xy_chart.tsx | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) 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 6952823ec9512..e27bb716c35c7 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 @@ -50,7 +50,7 @@ export const getLegendAction = ( const data: FilterEvent['data']['data'] = []; series.splitAccessors.forEach((value, accessor) => { - const rowIndex = table.rows.findIndex((row) => { + const rowIndex = formattedDatatables[layer.layerId].table.rows.findIndex((row) => { return row[accessor] === value; }); if (rowIndex !== -1) { 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 41a05c43631a9..a47f356c33dda 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 @@ -513,9 +513,11 @@ export function XYChart({ if (xySeries.seriesKeys.length > 1) { xySeries.splitAccessors.forEach((value, accessor) => { - const splitPointRowIndex = table.rows.findIndex((row) => { - return row[accessor] === value; - }); + const splitPointRowIndex = formattedDatatables[layer.layerId].table.rows.findIndex( + (row) => { + return row[accessor] === value; + } + ); if (splitPointRowIndex !== -1) { splitPoints.push({ row: splitPointRowIndex,