[XY] Add axes support (#129476)

* Added `axes` support

* Refactoring auto-assignment logic

* Fixed reference line position

* Fixed bug with auto-assignment.

* Fixed snapshots

* Fixed behavior of the horizontal reference lines.

Co-authored-by: Yaroslav Kuznietsov <kuznetsov.yaroslav.yk@gmail.com>
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Marta Bondyra <marta.bondyra@gmail.com>
This commit is contained in:
Uladzislau Lasitsa 2022-06-23 07:09:46 -07:00 committed by GitHub
parent ec3e3b27c6
commit 8fa2608172
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
81 changed files with 2784 additions and 1737 deletions

View file

@ -58,6 +58,9 @@ export const sampleLayer: DataLayerConfig = {
columnToLabel: '{"a": "Label A", "b": "Label B", "d": "Label D"}', columnToLabel: '{"a": "Label A", "b": "Label B", "d": "Label D"}',
xScaleType: 'ordinal', xScaleType: 'ordinal',
isHistogram: false, isHistogram: false,
isHorizontal: false,
isPercentage: false,
isStacked: false,
palette: mockPaletteOutput, palette: mockPaletteOutput,
table: createSampleDatatableWithRows([]), table: createSampleDatatableWithRows([]),
}; };
@ -73,6 +76,9 @@ export const sampleExtendedLayer: ExtendedDataLayerConfig = {
columnToLabel: '{"a": "Label A", "b": "Label B", "d": "Label D"}', columnToLabel: '{"a": "Label A", "b": "Label B", "d": "Label D"}',
xScaleType: 'ordinal', xScaleType: 'ordinal',
isHistogram: false, isHistogram: false,
isHorizontal: false,
isStacked: false,
isPercentage: false,
palette: mockPaletteOutput, palette: mockPaletteOutput,
table: createSampleDatatableWithRows([]), table: createSampleDatatableWithRows([]),
}; };
@ -80,9 +86,6 @@ export const sampleExtendedLayer: ExtendedDataLayerConfig = {
export const createArgsWithLayers = ( export const createArgsWithLayers = (
layers: DataLayerConfig | DataLayerConfig[] = sampleLayer layers: DataLayerConfig | DataLayerConfig[] = sampleLayer
): XYProps => ({ ): XYProps => ({
xTitle: '',
yTitle: '',
yRightTitle: '',
showTooltip: true, showTooltip: true,
legend: { legend: {
type: 'legendConfig', type: 'legendConfig',
@ -91,41 +94,44 @@ export const createArgsWithLayers = (
}, },
valueLabels: 'hide', valueLabels: 'hide',
valuesInLegend: false, valuesInLegend: false,
axisTitlesVisibilitySettings: { xAxisConfig: {
type: 'axisTitlesVisibilityConfig', type: 'xAxisConfig',
x: true, position: 'bottom',
yLeft: true, showGridLines: true,
yRight: true, labelsOrientation: 0,
}, showLabels: true,
tickLabelsVisibilitySettings: { showTitle: true,
type: 'tickLabelsConfig', title: '',
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',
}, },
yAxisConfigs: [
{
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], layers: Array.isArray(layers) ? layers : [layers],
yLeftScale: 'linear',
yRightScale: 'linear',
}); });
export function sampleArgs() { export function sampleArgs() {

View file

@ -8,22 +8,20 @@
export const XY_VIS = 'xyVis'; export const XY_VIS = 'xyVis';
export const LAYERED_XY_VIS = 'layeredXyVis'; export const LAYERED_XY_VIS = 'layeredXyVis';
export const Y_CONFIG = 'yConfig'; export const DATA_DECORATION_CONFIG = 'dataDecorationConfig';
export const REFERENCE_LINE_Y_CONFIG = 'referenceLineYConfig'; export const REFERENCE_LINE_DECORATION_CONFIG = 'referenceLineDecorationConfig';
export const EXTENDED_Y_CONFIG = 'extendedYConfig'; export const EXTENDED_REFERENCE_LINE_DECORATION_CONFIG = 'extendedReferenceLineDecorationConfig';
export const X_AXIS_CONFIG = 'xAxisConfig';
export const Y_AXIS_CONFIG = 'yAxisConfig';
export const DATA_LAYER = 'dataLayer'; export const DATA_LAYER = 'dataLayer';
export const EXTENDED_DATA_LAYER = 'extendedDataLayer'; export const EXTENDED_DATA_LAYER = 'extendedDataLayer';
export const LEGEND_CONFIG = 'legendConfig'; export const LEGEND_CONFIG = 'legendConfig';
export const XY_VIS_RENDERER = 'xyVis'; export const XY_VIS_RENDERER = 'xyVis';
export const GRID_LINES_CONFIG = 'gridlinesConfig';
export const ANNOTATION_LAYER = 'annotationLayer'; export const ANNOTATION_LAYER = 'annotationLayer';
export const EXTENDED_ANNOTATION_LAYER = 'extendedAnnotationLayer'; export const EXTENDED_ANNOTATION_LAYER = 'extendedAnnotationLayer';
export const TICK_LABELS_CONFIG = 'tickLabelsConfig';
export const AXIS_EXTENT_CONFIG = 'axisExtentConfig'; export const AXIS_EXTENT_CONFIG = 'axisExtentConfig';
export const REFERENCE_LINE = 'referenceLine'; export const REFERENCE_LINE = 'referenceLine';
export const REFERENCE_LINE_LAYER = 'referenceLineLayer'; export const REFERENCE_LINE_LAYER = 'referenceLineLayer';
export const LABELS_ORIENTATION_CONFIG = 'labelsOrientationConfig';
export const AXIS_TITLES_VISIBILITY_CONFIG = 'axisTitlesVisibilityConfig';
export const LayerTypes = { export const LayerTypes = {
DATA: 'data', DATA: 'data',
@ -82,13 +80,6 @@ export const SeriesTypes = {
BAR: 'bar', BAR: 'bar',
LINE: 'line', LINE: 'line',
AREA: 'area', 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; } as const;
export const YScaleTypes = { export const YScaleTypes = {
@ -131,3 +122,10 @@ export const AvailableReferenceLineIcons = {
TAG: 'tag', TAG: 'tag',
TRIANGLE: 'triangle', TRIANGLE: 'triangle',
} as const; } as const;
export const AxisModes = {
NORMAL: 'normal',
PERCENTAGE: 'percentage',
WIGGLE: 'wiggle',
SILHOUETTE: 'silhouette',
} as const;

View file

@ -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 '@kbn/expressions-plugin/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,
};
},
};

View file

@ -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 { 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' | 'boundsMargin'
> = {
title: {
types: ['string'],
help: strings.getAxisTitleHelp(),
},
id: {
types: ['string'],
help: strings.getAxisIdHelp(),
},
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(),
},
extent: {
types: [AXIS_EXTENT_CONFIG],
help: strings.getAxisExtentHelp(),
default: `{${AXIS_EXTENT_CONFIG}}`,
},
};

View file

@ -7,7 +7,7 @@
*/ */
import { ArgumentType } from '@kbn/expressions-plugin/common'; 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 { strings } from '../i18n';
import { DataLayerArgs, ExtendedDataLayerArgs } from '../types'; import { DataLayerArgs, ExtendedDataLayerArgs } from '../types';
@ -43,6 +43,21 @@ export const commonDataLayerArgs: Omit<
default: false, default: false,
help: strings.getIsHistogramHelp(), 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(),
},
lineWidth: { lineWidth: {
types: ['number'], types: ['number'],
help: strings.getLineWidthHelp(), help: strings.getLineWidthHelp(),
@ -59,9 +74,9 @@ export const commonDataLayerArgs: Omit<
types: ['boolean'], types: ['boolean'],
help: strings.getShowLinesHelp(), help: strings.getShowLinesHelp(),
}, },
yConfig: { decorations: {
types: [Y_CONFIG], types: [DATA_DECORATION_CONFIG],
help: strings.getYConfigHelp(), help: strings.getDecorationsHelp(),
multi: true, multi: true,
}, },
columnToLabel: { columnToLabel: {

View file

@ -7,17 +7,13 @@
*/ */
import { import {
AXIS_EXTENT_CONFIG,
AXIS_TITLES_VISIBILITY_CONFIG,
EndValues, EndValues,
FittingFunctions, FittingFunctions,
GRID_LINES_CONFIG,
LABELS_ORIENTATION_CONFIG,
LEGEND_CONFIG, LEGEND_CONFIG,
TICK_LABELS_CONFIG,
ValueLabelModes, ValueLabelModes,
XYCurveTypes, XYCurveTypes,
YScaleTypes, X_AXIS_CONFIG,
Y_AXIS_CONFIG,
} from '../constants'; } from '../constants';
import { strings } from '../i18n'; import { strings } from '../i18n';
import { LayeredXyVisFn, XyVisFn } from '../types'; import { LayeredXyVisFn, XyVisFn } from '../types';
@ -25,45 +21,6 @@ import { LayeredXyVisFn, XyVisFn } from '../types';
type CommonXYFn = XyVisFn | LayeredXyVisFn; type CommonXYFn = XyVisFn | LayeredXyVisFn;
export const commonXYArgs: CommonXYFn['args'] = { export const commonXYArgs: CommonXYFn['args'] = {
xTitle: {
types: ['string'],
help: strings.getXTitleHelp(),
},
yTitle: {
types: ['string'],
help: strings.getYTitleHelp(),
},
yRightTitle: {
types: ['string'],
help: strings.getYRightTitleHelp(),
},
xExtent: {
types: [AXIS_EXTENT_CONFIG],
help: strings.getXExtentHelp(),
default: `{${AXIS_EXTENT_CONFIG}}`,
},
yLeftExtent: {
types: [AXIS_EXTENT_CONFIG],
help: strings.getYLeftExtentHelp(),
default: `{${AXIS_EXTENT_CONFIG}}`,
},
yRightExtent: {
types: [AXIS_EXTENT_CONFIG],
help: strings.getYRightExtentHelp(),
default: `{${AXIS_EXTENT_CONFIG}}`,
},
yLeftScale: {
options: [...Object.values(YScaleTypes)],
help: strings.getYLeftScaleTypeHelp(),
default: YScaleTypes.LINEAR,
strict: true,
},
yRightScale: {
options: [...Object.values(YScaleTypes)],
help: strings.getYRightScaleTypeHelp(),
default: YScaleTypes.LINEAR,
strict: true,
},
legend: { legend: {
types: [LEGEND_CONFIG], types: [LEGEND_CONFIG],
help: strings.getLegendHelp(), help: strings.getLegendHelp(),
@ -93,22 +50,6 @@ export const commonXYArgs: CommonXYFn['args'] = {
strict: true, strict: true,
default: ValueLabelModes.HIDE, 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: { curveType: {
types: ['string'], types: ['string'],
options: [...Object.values(XYCurveTypes)], options: [...Object.values(XYCurveTypes)],
@ -133,6 +74,15 @@ export const commonXYArgs: CommonXYFn['args'] = {
types: ['string'], types: ['string'],
help: strings.getAriaLabelHelp(), help: strings.getAriaLabelHelp(),
}, },
xAxisConfig: {
types: [X_AXIS_CONFIG],
help: strings.getXAxisConfigHelp(),
},
yAxisConfigs: {
types: [Y_AXIS_CONFIG],
help: strings.getyAxisConfigsHelp(),
multi: true,
},
detailedTooltip: { detailedTooltip: {
types: ['boolean'], types: ['boolean'],
help: strings.getDetailedTooltipHelp(), help: strings.getDetailedTooltipHelp(),

View file

@ -6,22 +6,19 @@
* Side Public License, v 1. * Side Public License, v 1.
*/ */
import { YAxisModes } from '../constants';
import { strings } from '../i18n'; 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: { forAccessor: {
types: ['string'], types: ['string'],
help: strings.getForAccessorHelp(), help: strings.getForAccessorHelp(),
}, },
axisMode: { axisId: {
types: ['string'], types: ['string'],
options: [...Object.values(YAxisModes)], help: strings.getAxisIdHelp(),
help: strings.getAxisModeHelp(),
strict: true,
}, },
color: { color: {
types: ['string'], types: ['string'],

View file

@ -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,
};
},
};

View file

@ -20,6 +20,9 @@ describe('extendedDataLayerConfig', () => {
splitAccessor: 'd', splitAccessor: 'd',
xScaleType: 'linear', xScaleType: 'linear',
isHistogram: false, isHistogram: false,
isHorizontal: false,
isPercentage: false,
isStacked: false,
palette: mockPaletteOutput, palette: mockPaletteOutput,
}; };

View file

@ -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 '.';
import { createMockExecutionContext } from '@kbn/expressions-plugin/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 });
});
});

View file

@ -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 '@kbn/expressions-plugin/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,
};
},
};

View file

@ -11,13 +11,11 @@ export * from './layered_xy_vis';
export * from './legend_config'; export * from './legend_config';
export * from './annotation_layer'; export * from './annotation_layer';
export * from './extended_annotation_layer'; export * from './extended_annotation_layer';
export * from './data_decoration_config';
export * from './y_axis_config'; export * from './y_axis_config';
export * from './extended_y_axis_config'; export * from './x_axis_config';
export * from './reference_line_decoration_config';
export * from './extended_data_layer'; export * from './extended_data_layer';
export * from './grid_lines_config';
export * from './axis_extent_config'; export * from './axis_extent_config';
export * from './tick_labels_config';
export * from './labels_orientation_config';
export * from './reference_line'; export * from './reference_line';
export * from './reference_line_layer'; export * from './reference_line_layer';
export * from './axis_titles_visibility_config';

View file

@ -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 '.';
import { createMockExecutionContext } from '@kbn/expressions-plugin/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 });
});
});

View file

@ -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 '@kbn/expressions-plugin/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,
};
},
};

View file

@ -15,6 +15,7 @@ import {
validateMinTimeBarInterval, validateMinTimeBarInterval,
hasBarLayer, hasBarLayer,
errors, errors,
validateAxes,
} from './validate'; } from './validate';
import { appendLayerIds, getDataLayers } from '../helpers'; import { appendLayerIds, getDataLayers } from '../helpers';
@ -35,6 +36,8 @@ export const layeredXyVisFn: LayeredXyVisFn['fn'] = async (data, args, handlers)
throw new Error(errors.markSizeRatioWithoutAccessor()); throw new Error(errors.markSizeRatioWithoutAccessor());
} }
validateAxes(dataLayers, args.yAxisConfigs);
return { return {
type: 'render', type: 'render',
as: XY_VIS_RENDERER, as: XY_VIS_RENDERER,

View file

@ -15,6 +15,7 @@ describe('referenceLine', () => {
const args: ReferenceLineArgs = { const args: ReferenceLineArgs = {
value: 100, value: 100,
fill: 'above', fill: 'above',
position: 'bottom',
}; };
const result = referenceLineFunction.fn(null, args, createMockExecutionContext()); const result = referenceLineFunction.fn(null, args, createMockExecutionContext());
@ -23,9 +24,9 @@ describe('referenceLine', () => {
type: 'referenceLine', type: 'referenceLine',
layerType: 'referenceLine', layerType: 'referenceLine',
lineLength: 0, lineLength: 0,
yConfig: [ decorations: [
{ {
type: 'referenceLineYConfig', type: 'extendedReferenceLineDecorationConfig',
...args, ...args,
textVisibility: false, textVisibility: false,
}, },
@ -40,7 +41,7 @@ describe('referenceLine', () => {
value: 100, value: 100,
icon: 'alert', icon: 'alert',
iconPosition: 'below', iconPosition: 'below',
axisMode: 'bottom', position: 'bottom',
lineStyle: 'solid', lineStyle: 'solid',
lineWidth: 10, lineWidth: 10,
color: '#fff', color: '#fff',
@ -54,9 +55,9 @@ describe('referenceLine', () => {
type: 'referenceLine', type: 'referenceLine',
layerType: 'referenceLine', layerType: 'referenceLine',
lineLength: 0, lineLength: 0,
yConfig: [ decorations: [
{ {
type: 'referenceLineYConfig', type: 'extendedReferenceLineDecorationConfig',
...args, ...args,
}, },
], ],
@ -69,6 +70,7 @@ describe('referenceLine', () => {
name: 'some name', name: 'some name',
value: 100, value: 100,
fill: 'none', fill: 'none',
position: 'bottom',
}; };
const result = referenceLineFunction.fn(null, args, createMockExecutionContext()); const result = referenceLineFunction.fn(null, args, createMockExecutionContext());
@ -77,9 +79,9 @@ describe('referenceLine', () => {
type: 'referenceLine', type: 'referenceLine',
layerType: 'referenceLine', layerType: 'referenceLine',
lineLength: 0, lineLength: 0,
yConfig: [ decorations: [
{ {
type: 'referenceLineYConfig', type: 'extendedReferenceLineDecorationConfig',
...args, ...args,
textVisibility: true, textVisibility: true,
}, },
@ -93,6 +95,7 @@ describe('referenceLine', () => {
value: 100, value: 100,
textVisibility: true, textVisibility: true,
fill: 'none', fill: 'none',
position: 'bottom',
}; };
const result = referenceLineFunction.fn(null, args, createMockExecutionContext()); const result = referenceLineFunction.fn(null, args, createMockExecutionContext());
@ -101,9 +104,9 @@ describe('referenceLine', () => {
type: 'referenceLine', type: 'referenceLine',
layerType: 'referenceLine', layerType: 'referenceLine',
lineLength: 0, lineLength: 0,
yConfig: [ decorations: [
{ {
type: 'referenceLineYConfig', type: 'extendedReferenceLineDecorationConfig',
...args, ...args,
textVisibility: false, textVisibility: false,
}, },
@ -119,6 +122,7 @@ describe('referenceLine', () => {
name: 'some text', name: 'some text',
textVisibility, textVisibility,
fill: 'none', fill: 'none',
position: 'bottom',
}; };
const result = referenceLineFunction.fn(null, args, createMockExecutionContext()); const result = referenceLineFunction.fn(null, args, createMockExecutionContext());
@ -127,9 +131,9 @@ describe('referenceLine', () => {
type: 'referenceLine', type: 'referenceLine',
layerType: 'referenceLine', layerType: 'referenceLine',
lineLength: 0, lineLength: 0,
yConfig: [ decorations: [
{ {
type: 'referenceLineYConfig', type: 'extendedReferenceLineDecorationConfig',
...args, ...args,
textVisibility, textVisibility,
}, },

View file

@ -6,6 +6,7 @@
* Side Public License, v 1. * Side Public License, v 1.
*/ */
import { Position } from '@elastic/charts';
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import { import {
AvailableReferenceLineIcons, AvailableReferenceLineIcons,
@ -14,8 +15,7 @@ import {
LayerTypes, LayerTypes,
LineStyles, LineStyles,
REFERENCE_LINE, REFERENCE_LINE,
REFERENCE_LINE_Y_CONFIG, EXTENDED_REFERENCE_LINE_DECORATION_CONFIG,
YAxisModes,
} from '../constants'; } from '../constants';
import { ReferenceLineFn } from '../types'; import { ReferenceLineFn } from '../types';
import { strings } from '../i18n'; import { strings } from '../i18n';
@ -36,13 +36,23 @@ export const referenceLineFunction: ReferenceLineFn = {
help: strings.getReferenceLineValueHelp(), help: strings.getReferenceLineValueHelp(),
required: true, required: true,
}, },
axisMode: { position: {
types: ['string'], types: ['string'],
options: [...Object.values(YAxisModes)], options: [Position.Right, Position.Left],
help: strings.getAxisModeHelp(), help: i18n.translate('expressionXY.referenceLine.position.help', {
default: YAxisModes.AUTO, defaultMessage:
'Position of axis (first axis of that position) to which the reference line belongs.',
}),
default: Position.Left,
strict: true, strict: true,
}, },
axisId: {
types: ['string'],
help: i18n.translate('expressionXY.referenceLine.axisId.help', {
defaultMessage:
'Id of axis to which the reference line belongs. It has higher priority than "position"',
}),
},
color: { color: {
types: ['string'], types: ['string'],
help: strings.getColorHelp(), help: strings.getColorHelp(),
@ -50,7 +60,7 @@ export const referenceLineFunction: ReferenceLineFn = {
lineStyle: { lineStyle: {
types: ['string'], types: ['string'],
options: [...Object.values(LineStyles)], 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', defaultMessage: 'The style of the reference line',
}), }),
default: LineStyles.SOLID, default: LineStyles.SOLID,
@ -58,14 +68,14 @@ export const referenceLineFunction: ReferenceLineFn = {
}, },
lineWidth: { lineWidth: {
types: ['number'], types: ['number'],
help: i18n.translate('expressionXY.yConfig.lineWidth.help', { help: i18n.translate('expressionXY.decorationConfig.lineWidth.help', {
defaultMessage: 'The width of the reference line', defaultMessage: 'The width of the reference line',
}), }),
default: 1, default: 1,
}, },
icon: { icon: {
types: ['string'], types: ['string'],
help: i18n.translate('expressionXY.yConfig.icon.help', { help: i18n.translate('expressionXY.decorationConfig.icon.help', {
defaultMessage: 'An optional icon used for reference lines', defaultMessage: 'An optional icon used for reference lines',
}), }),
options: [...Object.values(AvailableReferenceLineIcons)], options: [...Object.values(AvailableReferenceLineIcons)],
@ -74,7 +84,7 @@ export const referenceLineFunction: ReferenceLineFn = {
iconPosition: { iconPosition: {
types: ['string'], types: ['string'],
options: [...Object.values(IconPositions)], 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', defaultMessage: 'The placement of the icon for the reference line',
}), }),
default: IconPositions.AUTO, default: IconPositions.AUTO,
@ -82,14 +92,14 @@ export const referenceLineFunction: ReferenceLineFn = {
}, },
textVisibility: { textVisibility: {
types: ['boolean'], 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', defaultMessage: 'Visibility of the label on the reference line',
}), }),
}, },
fill: { fill: {
types: ['string'], types: ['string'],
options: [...Object.values(FillStyles)], options: [...Object.values(FillStyles)],
help: i18n.translate('expressionXY.yConfig.fill.help', { help: i18n.translate('expressionXY.decorationConfig.fill.help', {
defaultMessage: 'Fill', defaultMessage: 'Fill',
}), }),
default: FillStyles.NONE, default: FillStyles.NONE,
@ -108,7 +118,7 @@ export const referenceLineFunction: ReferenceLineFn = {
type: REFERENCE_LINE, type: REFERENCE_LINE,
layerType: LayerTypes.REFERENCELINE, layerType: LayerTypes.REFERENCELINE,
lineLength: table?.rows.length ?? 0, lineLength: table?.rows.length ?? 0,
yConfig: [{ ...args, textVisibility, type: REFERENCE_LINE_Y_CONFIG }], decorations: [{ ...args, textVisibility, type: EXTENDED_REFERENCE_LINE_DECORATION_CONFIG }],
}; };
}, },
}; };

View file

@ -6,43 +6,54 @@
* Side Public License, v 1. * Side Public License, v 1.
*/ */
import { Position } from '@elastic/charts';
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import { import {
AvailableReferenceLineIcons, AvailableReferenceLineIcons,
EXTENDED_Y_CONFIG, REFERENCE_LINE_DECORATION_CONFIG,
FillStyles, FillStyles,
IconPositions, IconPositions,
LineStyles, LineStyles,
} from '../constants'; } from '../constants';
import { strings } from '../i18n'; import { strings } from '../i18n';
import { ExtendedYConfigFn } from '../types'; import { ReferenceLineDecorationConfigFn } from '../types';
import { commonYConfigArgs } from './common_y_config_args'; import { commonDecorationConfigArgs } from './common_y_config_args';
export const extendedYAxisConfigFunction: ExtendedYConfigFn = { export const referenceLineDecorationConfigFunction: ReferenceLineDecorationConfigFn = {
name: EXTENDED_Y_CONFIG, name: REFERENCE_LINE_DECORATION_CONFIG,
aliases: [], aliases: [],
type: EXTENDED_Y_CONFIG, type: REFERENCE_LINE_DECORATION_CONFIG,
help: strings.getYConfigFnHelp(), help: strings.getDecorationsHelp(),
inputTypes: ['null'], inputTypes: ['null'],
args: { args: {
...commonYConfigArgs, ...commonDecorationConfigArgs,
position: {
types: ['string'],
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.',
}),
default: Position.Left,
strict: true,
},
lineStyle: { lineStyle: {
types: ['string'], types: ['string'],
options: [...Object.values(LineStyles)], 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', defaultMessage: 'The style of the reference line',
}), }),
strict: true, strict: true,
}, },
lineWidth: { lineWidth: {
types: ['number'], types: ['number'],
help: i18n.translate('expressionXY.yConfig.lineWidth.help', { help: i18n.translate('expressionXY.decorationConfig.lineWidth.help', {
defaultMessage: 'The width of the reference line', defaultMessage: 'The width of the reference line',
}), }),
}, },
icon: { icon: {
types: ['string'], types: ['string'],
help: i18n.translate('expressionXY.yConfig.icon.help', { help: i18n.translate('expressionXY.decorationConfig.icon.help', {
defaultMessage: 'An optional icon used for reference lines', defaultMessage: 'An optional icon used for reference lines',
}), }),
options: [...Object.values(AvailableReferenceLineIcons)], options: [...Object.values(AvailableReferenceLineIcons)],
@ -51,21 +62,21 @@ export const extendedYAxisConfigFunction: ExtendedYConfigFn = {
iconPosition: { iconPosition: {
types: ['string'], types: ['string'],
options: [...Object.values(IconPositions)], 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', defaultMessage: 'The placement of the icon for the reference line',
}), }),
strict: true, strict: true,
}, },
textVisibility: { textVisibility: {
types: ['boolean'], 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', defaultMessage: 'Visibility of the label on the reference line',
}), }),
}, },
fill: { fill: {
types: ['string'], types: ['string'],
options: [...Object.values(FillStyles)], options: [...Object.values(FillStyles)],
help: i18n.translate('expressionXY.yConfig.fill.help', { help: i18n.translate('expressionXY.decorationConfig.fill.help', {
defaultMessage: 'Fill', defaultMessage: 'Fill',
}), }),
strict: true, strict: true,
@ -73,7 +84,7 @@ export const extendedYAxisConfigFunction: ExtendedYConfigFn = {
}, },
fn(input, args) { fn(input, args) {
return { return {
type: EXTENDED_Y_CONFIG, type: REFERENCE_LINE_DECORATION_CONFIG,
...args, ...args,
}; };
}, },

View file

@ -6,7 +6,7 @@
* Side Public License, v 1. * 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 { ReferenceLineLayerFn } from '../types';
import { strings } from '../i18n'; import { strings } from '../i18n';
@ -22,9 +22,9 @@ export const referenceLineLayerFunction: ReferenceLineLayerFn = {
help: strings.getRLAccessorsHelp(), help: strings.getRLAccessorsHelp(),
multi: true, multi: true,
}, },
yConfig: { decorations: {
types: [EXTENDED_Y_CONFIG], types: [REFERENCE_LINE_DECORATION_CONFIG],
help: strings.getRLYConfigHelp(), help: strings.getRLDecorationConfigHelp(),
multi: true, multi: true,
}, },
columnToLabel: { columnToLabel: {

View file

@ -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 '.';
import { createMockExecutionContext } from '@kbn/expressions-plugin/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 });
});
});

View file

@ -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 '@kbn/expressions-plugin/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,
};
},
};

View file

@ -9,7 +9,7 @@
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import { isValidInterval } from '@kbn/data-plugin/common'; import { isValidInterval } from '@kbn/data-plugin/common';
import { ExpressionValueVisDimension } from '@kbn/visualizations-plugin/common'; import { ExpressionValueVisDimension } from '@kbn/visualizations-plugin/common';
import { AxisExtentModes, ValueLabelModes } from '../constants'; import { AxisExtentModes, ValueLabelModes, SeriesTypes } from '../constants';
import { import {
SeriesType, SeriesType,
AxisExtentConfigResult, AxisExtentConfigResult,
@ -17,7 +17,9 @@ import {
CommonXYDataLayerConfigResult, CommonXYDataLayerConfigResult,
ValueLabelMode, ValueLabelMode,
CommonXYDataLayerConfig, CommonXYDataLayerConfig,
YAxisConfigResult,
ExtendedDataLayerConfigResult, ExtendedDataLayerConfigResult,
XAxisConfigResult,
} from '../types'; } from '../types';
import { isTimeChart } from '../helpers'; import { isTimeChart } from '../helpers';
@ -111,19 +113,23 @@ export const errors = {
defaultMessage: '`minTimeBarInterval` argument is applicable only for time bar charts.', 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: `decorations=\\{dataDecorationConfig forAccessor="your-accessor" axisId="{axisId}"\\}`',
values: { axisId },
}),
}; };
export const hasBarLayer = (layers: Array<DataLayerConfigResult | CommonXYDataLayerConfig>) => export const hasBarLayer = (layers: Array<DataLayerConfigResult | CommonXYDataLayerConfig>) =>
layers.filter(({ seriesType }) => seriesType.includes('bar')).length > 0; layers.some(({ seriesType }) => seriesType === SeriesTypes.BAR);
export const hasAreaLayer = (layers: Array<DataLayerConfigResult | CommonXYDataLayerConfig>) => export const hasAreaLayer = (layers: Array<DataLayerConfigResult | CommonXYDataLayerConfig>) =>
layers.filter(({ seriesType }) => seriesType.includes('area')).length > 0; layers.some(({ seriesType }) => seriesType === SeriesTypes.AREA);
export const hasHistogramBarLayer = ( export const hasHistogramBarLayer = (
layers: Array<DataLayerConfigResult | CommonXYDataLayerConfig> layers: Array<DataLayerConfigResult | CommonXYDataLayerConfig>
) => ) => layers.some(({ seriesType, isHistogram }) => seriesType === SeriesTypes.BAR && isHistogram);
layers.filter(({ seriesType, isHistogram }) => seriesType.includes('bar') && isHistogram).length >
0;
export const isValidExtentWithCustomMode = (extent: AxisExtentConfigResult) => { export const isValidExtentWithCustomMode = (extent: AxisExtentConfigResult) => {
const isValidLowerBound = const isValidLowerBound =
@ -138,8 +144,8 @@ export const validateExtentForDataBounds = (
extent: AxisExtentConfigResult, extent: AxisExtentConfigResult,
layers: Array<DataLayerConfigResult | CommonXYDataLayerConfig> layers: Array<DataLayerConfigResult | CommonXYDataLayerConfig>
) => { ) => {
const lineSeries = layers.filter(({ seriesType }) => seriesType.includes('line')); const hasLineSeries = layers.some(({ seriesType }) => seriesType === SeriesTypes.LINE);
if (!lineSeries.length && extent.mode === AxisExtentModes.DATA_BOUNDS) { if (!hasLineSeries && extent.mode === AxisExtentModes.DATA_BOUNDS) {
throw new Error(errors.dataBoundsForNotLineChartError()); throw new Error(errors.dataBoundsForNotLineChartError());
} }
}; };
@ -158,20 +164,46 @@ export const validateXExtent = (
} }
}; };
export const validateExtent = ( export const validateExtents = (
extent: AxisExtentConfigResult, dataLayers: Array<DataLayerConfigResult | CommonXYDataLayerConfig>,
hasBarOrArea: boolean, hasBarOrArea: boolean,
dataLayers: Array<DataLayerConfigResult | CommonXYDataLayerConfig> yAxisConfigs?: YAxisConfigResult[],
xAxisConfig?: XAxisConfigResult
) => { ) => {
if ( yAxisConfigs?.forEach((axis) => {
extent.mode === AxisExtentModes.CUSTOM && if (!axis.extent) {
hasBarOrArea && return;
!isValidExtentWithCustomMode(extent) }
) { if (
throw new Error(errors.extendBoundsAreInvalidError()); hasBarOrArea &&
} axis.extent?.mode === AxisExtentModes.CUSTOM &&
!isValidExtentWithCustomMode(axis.extent)
) {
throw new Error(errors.extendBoundsAreInvalidError());
}
validateExtentForDataBounds(extent, dataLayers); validateExtentForDataBounds(axis.extent, dataLayers);
});
validateXExtent(xAxisConfig?.extent, dataLayers);
};
export const validateAxes = (
dataLayers: Array<DataLayerConfigResult | CommonXYDataLayerConfig>,
yAxisConfigs?: YAxisConfigResult[]
) => {
yAxisConfigs?.forEach((axis) => {
if (
axis.id &&
dataLayers.every(
(layer) =>
!layer.decorations ||
layer.decorations?.every((decorationConfig) => decorationConfig.axisId !== axis.id)
)
) {
throw new Error(errors.axisIsNotAssignedError(axis.id));
}
});
}; };
export const validateFillOpacity = (fillOpacity: number | undefined, hasArea: boolean) => { export const validateFillOpacity = (fillOpacity: number | undefined, hasArea: boolean) => {
@ -191,7 +223,7 @@ export const validateValueLabels = (
}; };
const isAreaOrLineChart = (seriesType: SeriesType) => const isAreaOrLineChart = (seriesType: SeriesType) =>
seriesType.includes('line') || seriesType.includes('area'); seriesType === SeriesTypes.LINE || seriesType === SeriesTypes.AREA;
export const validateAddTimeMarker = ( export const validateAddTimeMarker = (
dataLayers: Array<DataLayerConfigResult | ExtendedDataLayerConfigResult>, dataLayers: Array<DataLayerConfigResult | ExtendedDataLayerConfigResult>,
@ -206,7 +238,7 @@ export const validateMarkSizeForChartType = (
markSizeAccessor: ExpressionValueVisDimension | string | undefined, markSizeAccessor: ExpressionValueVisDimension | string | undefined,
seriesType: SeriesType seriesType: SeriesType
) => { ) => {
if (markSizeAccessor && !seriesType.includes('line') && !seriesType.includes('area')) { if (markSizeAccessor && !isAreaOrLineChart(seriesType)) {
throw new Error(errors.markSizeAccessorForNonLineOrAreaChartsError()); throw new Error(errors.markSizeAccessorForNonLineOrAreaChartsError());
} }
}; };
@ -248,7 +280,7 @@ export const validateLinesVisibilityForChartType = (
showLines: boolean | undefined, showLines: boolean | undefined,
seriesType: SeriesType seriesType: SeriesType
) => { ) => {
if (showLines && !(seriesType.includes('line') || seriesType.includes('area'))) { if (showLines && !isAreaOrLineChart(seriesType)) {
throw new Error(errors.linesVisibilityForNonLineChartError()); throw new Error(errors.linesVisibilityForNonLineChartError());
} }
}; };

View file

@ -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 { Position } from '@elastic/charts';
import { strings } from '../i18n';
import { XAxisConfigFn } from '../types';
import { X_AXIS_CONFIG } from '../constants';
import { commonAxisConfigArgs } from './common_axis_args';
export const xAxisConfigFunction: XAxisConfigFn = {
name: X_AXIS_CONFIG,
aliases: [],
type: X_AXIS_CONFIG,
help: strings.getXAxisConfigFnHelp(),
inputTypes: ['null'],
args: {
...commonAxisConfigArgs,
position: {
types: ['string'],
options: [Position.Top, Position.Bottom],
help: strings.getAxisPositionHelp(),
strict: true,
},
},
fn(input, args) {
return {
type: X_AXIS_CONFIG,
...args,
position: args.position ?? Position.Bottom,
};
},
};

View file

@ -233,7 +233,10 @@ describe('xyVis', () => {
annotationLayers: [], annotationLayers: [],
isHistogram: true, isHistogram: true,
xScaleType: 'time', xScaleType: 'time',
xExtent: { type: 'axisExtentConfig', mode: 'dataBounds' }, xAxisConfig: {
type: 'xAxisConfig',
extent: { type: 'axisExtentConfig', mode: 'dataBounds' },
},
}, },
createMockExecutionContext() createMockExecutionContext()
) )
@ -255,11 +258,14 @@ describe('xyVis', () => {
...restLayerArgs, ...restLayerArgs,
referenceLines: [], referenceLines: [],
annotationLayers: [], annotationLayers: [],
xExtent: { xAxisConfig: {
type: 'axisExtentConfig', type: 'xAxisConfig',
mode: 'full', extent: {
lowerBound: undefined, type: 'axisExtentConfig',
upperBound: undefined, mode: 'full',
lowerBound: undefined,
upperBound: undefined,
},
}, },
}, },
createMockExecutionContext() createMockExecutionContext()
@ -282,9 +288,9 @@ describe('xyVis', () => {
...restLayerArgs, ...restLayerArgs,
referenceLines: [], referenceLines: [],
annotationLayers: [], annotationLayers: [],
xExtent: { xAxisConfig: {
type: 'axisExtentConfig', type: 'xAxisConfig',
mode: 'dataBounds', extent: { type: 'axisExtentConfig', mode: 'dataBounds' },
}, },
}, },
createMockExecutionContext() createMockExecutionContext()
@ -292,7 +298,7 @@ describe('xyVis', () => {
).rejects.toThrowErrorMatchingSnapshot(); ).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 { data, args } = sampleArgs();
const { layers, ...rest } = args; const { layers, ...rest } = args;
const { layerId, layerType, table, type, ...restLayerArgs } = sampleLayer; const { layerId, layerType, table, type, ...restLayerArgs } = sampleLayer;
@ -304,11 +310,14 @@ describe('xyVis', () => {
referenceLines: [], referenceLines: [],
annotationLayers: [], annotationLayers: [],
isHistogram: true, isHistogram: true,
xExtent: { xAxisConfig: {
type: 'axisExtentConfig', type: 'xAxisConfig',
mode: 'custom', extent: {
lowerBound: 0, type: 'axisExtentConfig',
upperBound: 10, mode: 'custom',
lowerBound: 0,
upperBound: 10,
},
}, },
}, },
createMockExecutionContext() createMockExecutionContext()
@ -320,11 +329,14 @@ describe('xyVis', () => {
value: { value: {
args: { args: {
...rest, ...rest,
xExtent: { xAxisConfig: {
type: 'axisExtentConfig', type: 'xAxisConfig',
mode: 'custom', extent: {
lowerBound: 0, type: 'axisExtentConfig',
upperBound: 10, mode: 'custom',
lowerBound: 0,
upperBound: 10,
},
}, },
layers: [ layers: [
{ {

View file

@ -21,7 +21,7 @@ import {
hasAreaLayer, hasAreaLayer,
hasBarLayer, hasBarLayer,
hasHistogramBarLayer, hasHistogramBarLayer,
validateExtent, validateExtents,
validateFillOpacity, validateFillOpacity,
validateMarkSizeRatioLimits, validateMarkSizeRatioLimits,
validateValueLabels, validateValueLabels,
@ -33,7 +33,7 @@ import {
validateLineWidthForChartType, validateLineWidthForChartType,
validatePointsRadiusForChartType, validatePointsRadiusForChartType,
validateLinesVisibilityForChartType, validateLinesVisibilityForChartType,
validateXExtent, validateAxes,
} from './validate'; } from './validate';
const createDataLayer = (args: XYArgs, table: Datatable): DataLayerConfigResult => { const createDataLayer = (args: XYArgs, table: Datatable): DataLayerConfigResult => {
@ -46,8 +46,11 @@ const createDataLayer = (args: XYArgs, table: Datatable): DataLayerConfigResult
columnToLabel: args.columnToLabel, columnToLabel: args.columnToLabel,
xScaleType: args.xScaleType, xScaleType: args.xScaleType,
isHistogram: args.isHistogram, isHistogram: args.isHistogram,
isPercentage: args.isPercentage,
isHorizontal: args.isHorizontal,
isStacked: args.isStacked,
palette: args.palette, palette: args.palette,
yConfig: args.yConfig, decorations: args.decorations,
showPoints: args.showPoints, showPoints: args.showPoints,
pointsRadius: args.pointsRadius, pointsRadius: args.pointsRadius,
lineWidth: args.lineWidth, lineWidth: args.lineWidth,
@ -74,7 +77,10 @@ export const xyVisFn: XyVisFn['fn'] = async (data, args, handlers) => {
columnToLabel, columnToLabel,
xScaleType, xScaleType,
isHistogram, isHistogram,
yConfig, isHorizontal,
isPercentage,
isStacked,
decorations,
palette, palette,
markSizeAccessor, markSizeAccessor,
showPoints, showPoints,
@ -121,9 +127,7 @@ export const xyVisFn: XyVisFn['fn'] = async (data, args, handlers) => {
const hasBar = hasBarLayer(dataLayers); const hasBar = hasBarLayer(dataLayers);
const hasArea = hasAreaLayer(dataLayers); const hasArea = hasAreaLayer(dataLayers);
validateXExtent(args.xExtent, dataLayers); validateExtents(dataLayers, hasBar || hasArea, args.yAxisConfigs, args.xAxisConfig);
validateExtent(args.yLeftExtent, hasBar || hasArea, dataLayers);
validateExtent(args.yRightExtent, hasBar || hasArea, dataLayers);
validateFillOpacity(args.fillOpacity, hasArea); validateFillOpacity(args.fillOpacity, hasArea);
validateAddTimeMarker(dataLayers, args.addTimeMarker); validateAddTimeMarker(dataLayers, args.addTimeMarker);
validateMinTimeBarInterval(dataLayers, hasBar, args.minTimeBarInterval); validateMinTimeBarInterval(dataLayers, hasBar, args.minTimeBarInterval);
@ -136,6 +140,7 @@ export const xyVisFn: XyVisFn['fn'] = async (data, args, handlers) => {
validateLineWidthForChartType(lineWidth, args.seriesType); validateLineWidthForChartType(lineWidth, args.seriesType);
validateShowPointsForChartType(showPoints, args.seriesType); validateShowPointsForChartType(showPoints, args.seriesType);
validatePointsRadiusForChartType(pointsRadius, args.seriesType); validatePointsRadiusForChartType(pointsRadius, args.seriesType);
validateAxes(dataLayers, args.yAxisConfigs);
return { return {
type: 'render', type: 'render',

View file

@ -6,22 +6,46 @@
* Side Public License, v 1. * Side Public License, v 1.
*/ */
import { Y_CONFIG } from '../constants'; import { Position } from '@elastic/charts';
import { YConfigFn } from '../types';
import { strings } from '../i18n'; import { strings } from '../i18n';
import { commonYConfigArgs } from './common_y_config_args'; import { Y_AXIS_CONFIG, AxisModes, YScaleTypes } from '../constants';
import { YAxisConfigFn } from '../types';
import { commonAxisConfigArgs } from './common_axis_args';
export const yAxisConfigFunction: YConfigFn = { export const yAxisConfigFunction: YAxisConfigFn = {
name: Y_CONFIG, name: Y_AXIS_CONFIG,
aliases: [], aliases: [],
type: Y_CONFIG, type: Y_AXIS_CONFIG,
help: strings.getYConfigFnHelp(), help: strings.getYAxisConfigFnHelp(),
inputTypes: ['null'], inputTypes: ['null'],
args: { ...commonYConfigArgs }, args: {
...commonAxisConfigArgs,
mode: {
types: ['string'],
options: [...Object.values(AxisModes)],
help: strings.getAxisModeHelp(),
},
boundsMargin: {
types: ['number'],
help: strings.getAxisBoundsMarginHelp(),
},
scaleType: {
options: [...Object.values(YScaleTypes)],
help: strings.getAxisScaleTypeHelp(),
default: YScaleTypes.LINEAR,
},
position: {
types: ['string'],
options: [Position.Right, Position.Left],
help: strings.getAxisPositionHelp(),
strict: true,
},
},
fn(input, args) { fn(input, args) {
return { return {
type: Y_CONFIG, type: Y_AXIS_CONFIG,
...args, ...args,
position: args.position ?? Position.Left,
}; };
}, },
}; };

View file

@ -59,6 +59,9 @@ describe('#getDataLayers', () => {
seriesType: 'bar', seriesType: 'bar',
xScaleType: 'time', xScaleType: 'time',
isHistogram: false, isHistogram: false,
isHorizontal: false,
isPercentage: false,
isStacked: false,
table: { rows: [], columns: [], type: 'datatable' }, table: { rows: [], columns: [], type: 'datatable' },
palette: { type: 'system_palette', name: 'system' }, palette: { type: 'system_palette', name: 'system' },
}, },

View file

@ -14,7 +14,7 @@ import {
ExtendedDataLayerArgs, ExtendedDataLayerArgs,
DataLayerArgs, DataLayerArgs,
} from '../types'; } from '../types';
import { LayerTypes } from '../constants'; import { LayerTypes, SeriesTypes } from '../constants';
function isWithLayerId<T>(layer: T): layer is T & WithLayerId { function isWithLayerId<T>(layer: T): layer is T & WithLayerId {
return (layer as T & WithLayerId).layerId ? true : false; return (layer as T & WithLayerId).layerId ? true : false;
@ -35,9 +35,7 @@ export function appendLayerIds<T>(
} }
export const getShowLines = (args: DataLayerArgs | ExtendedDataLayerArgs) => export const getShowLines = (args: DataLayerArgs | ExtendedDataLayerArgs) =>
args.seriesType.includes('line') || args.seriesType.includes('area') args.showLines ?? (args.seriesType === SeriesTypes.LINE || args.seriesType !== SeriesTypes.AREA);
? args.showLines ?? true
: args.showLines;
export function getDataLayers(layers: XYExtendedLayerConfigResult[]) { export function getDataLayers(layers: XYExtendedLayerConfigResult[]) {
return layers.filter<ExtendedDataLayerConfig>( return layers.filter<ExtendedDataLayerConfig>(

View file

@ -20,6 +20,9 @@ describe('#isTimeChart', () => {
seriesType: 'bar', seriesType: 'bar',
xScaleType: 'time', xScaleType: 'time',
isHistogram: false, isHistogram: false,
isHorizontal: false,
isPercentage: false,
isStacked: false,
table: { table: {
rows: [], rows: [],
columns: [ columns: [

View file

@ -29,38 +29,6 @@ export const strings = {
i18n.translate('expressionXY.xyVis.logDatatable.breakDown', { i18n.translate('expressionXY.xyVis.logDatatable.breakDown', {
defaultMessage: 'Break down by', 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',
}),
getXExtentHelp: () =>
i18n.translate('expressionXY.xyVis.xExtent.help', {
defaultMessage: 'X axis extents',
}),
getYLeftExtentHelp: () =>
i18n.translate('expressionXY.xyVis.yLeftExtent.help', {
defaultMessage: 'Y left axis extents',
}),
getYRightExtentHelp: () =>
i18n.translate('expressionXY.xyVis.yRightExtent.help', {
defaultMessage: 'Y right axis extents',
}),
getYLeftScaleTypeHelp: () =>
i18n.translate('expressionXY.xyVis.yLeftScaleType.help', {
defaultMessage: 'The scale type of the left y axis',
}),
getYRightScaleTypeHelp: () =>
i18n.translate('expressionXY.xyVis.yRightScaleType.help', {
defaultMessage: 'The scale type of the right y axis',
}),
getLegendHelp: () => getLegendHelp: () =>
i18n.translate('expressionXY.xyVis.legend.help', { i18n.translate('expressionXY.xyVis.legend.help', {
defaultMessage: 'Configure the chart legend.', defaultMessage: 'Configure the chart legend.',
@ -77,22 +45,6 @@ export const strings = {
i18n.translate('expressionXY.xyVis.valueLabels.help', { i18n.translate('expressionXY.xyVis.valueLabels.help', {
defaultMessage: 'Value labels mode', 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: () => getDataLayerHelp: () =>
i18n.translate('expressionXY.xyVis.dataLayer.help', { i18n.translate('expressionXY.xyVis.dataLayer.help', {
defaultMessage: 'Data layer of visual series', defaultMessage: 'Data layer of visual series',
@ -125,6 +77,14 @@ export const strings = {
i18n.translate('expressionXY.xyVis.ariaLabel.help', { i18n.translate('expressionXY.xyVis.ariaLabel.help', {
defaultMessage: 'Specifies the aria label of the xy chart', defaultMessage: 'Specifies the aria label of the xy chart',
}), }),
getXAxisConfigHelp: () =>
i18n.translate('expressionXY.xyVis.xAxisConfig.help', {
defaultMessage: 'Specifies x-axis config',
}),
getyAxisConfigsHelp: () =>
i18n.translate('expressionXY.xyVis.yAxisConfigs.help', {
defaultMessage: 'Specifies y-axes configs',
}),
getDetailedTooltipHelp: () => getDetailedTooltipHelp: () =>
i18n.translate('expressionXY.xyVis.detailedTooltip.help', { i18n.translate('expressionXY.xyVis.detailedTooltip.help', {
defaultMessage: 'Show detailed tooltip', defaultMessage: 'Show detailed tooltip',
@ -185,6 +145,18 @@ export const strings = {
i18n.translate('expressionXY.dataLayer.isHistogram.help', { i18n.translate('expressionXY.dataLayer.isHistogram.help', {
defaultMessage: 'Whether to layout the chart as a histogram', defaultMessage: 'Whether to layout the chart as a histogram',
}), }),
getIsStackedHelp: () =>
i18n.translate('expressionXY.dataLayer.isStacked.help', {
defaultMessage: 'Layout of the chart in stacked mode',
}),
getIsPercentageHelp: () =>
i18n.translate('expressionXY.dataLayer.isPercentage.help', {
defaultMessage: 'Whether to layout the chart has percentage mode',
}),
getIsHorizontalHelp: () =>
i18n.translate('expressionXY.dataLayer.isHorizontal.help', {
defaultMessage: 'Layout of the chart is horizontal',
}),
getSplitAccessorHelp: () => getSplitAccessorHelp: () =>
i18n.translate('expressionXY.dataLayer.splitAccessor.help', { i18n.translate('expressionXY.dataLayer.splitAccessor.help', {
defaultMessage: 'The column to split by', defaultMessage: 'The column to split by',
@ -213,9 +185,9 @@ export const strings = {
i18n.translate('expressionXY.dataLayer.showLines.help', { i18n.translate('expressionXY.dataLayer.showLines.help', {
defaultMessage: 'Show lines between points', defaultMessage: 'Show lines between points',
}), }),
getYConfigHelp: () => getDecorationsHelp: () =>
i18n.translate('expressionXY.dataLayer.yConfig.help', { i18n.translate('expressionXY.dataLayer.decorations.help', {
defaultMessage: 'Additional configuration for y axes', defaultMessage: 'Additional decoration for data',
}), }),
getColumnToLabelHelp: () => getColumnToLabelHelp: () =>
i18n.translate('expressionXY.layer.columnToLabel.help', { i18n.translate('expressionXY.layer.columnToLabel.help', {
@ -237,30 +209,26 @@ export const strings = {
i18n.translate('expressionXY.referenceLineLayer.accessors.help', { i18n.translate('expressionXY.referenceLineLayer.accessors.help', {
defaultMessage: 'The columns to display on the y axis.', defaultMessage: 'The columns to display on the y axis.',
}), }),
getRLYConfigHelp: () => getRLDecorationConfigHelp: () =>
i18n.translate('expressionXY.referenceLineLayer.yConfig.help', { i18n.translate('expressionXY.referenceLineLayer.decorationConfig.help', {
defaultMessage: 'Additional configuration for y axes', defaultMessage: 'Additional decoration for reference line',
}), }),
getRLHelp: () => getRLHelp: () =>
i18n.translate('expressionXY.referenceLineLayer.help', { i18n.translate('expressionXY.referenceLineLayer.help', {
defaultMessage: `Configure a reference line in the xy chart`, 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: () => getForAccessorHelp: () =>
i18n.translate('expressionXY.yConfig.forAccessor.help', { i18n.translate('expressionXY.decorationConfig.forAccessor.help', {
defaultMessage: 'The accessor this configuration is for', defaultMessage: 'The accessor this configuration is for',
}), }),
getAxisModeHelp: () =>
i18n.translate('expressionXY.yConfig.axisMode.help', {
defaultMessage: 'The axis mode of the metric',
}),
getColorHelp: () => getColorHelp: () =>
i18n.translate('expressionXY.yConfig.color.help', { i18n.translate('expressionXY.decorationConfig.color.help', {
defaultMessage: 'The color of the series', defaultMessage: 'The color of the series',
}), }),
getAxisIdHelp: () =>
i18n.translate('expressionXY.decorationConfig.axisId.help', {
defaultMessage: 'Id of axis',
}),
getAnnotationLayerFnHelp: () => getAnnotationLayerFnHelp: () =>
i18n.translate('expressionXY.annotationLayer.help', { i18n.translate('expressionXY.annotationLayer.help', {
defaultMessage: `Configure an annotation layer in the xy chart`, defaultMessage: `Configure an annotation layer in the xy chart`,
@ -273,6 +241,74 @@ export const strings = {
i18n.translate('expressionXY.annotationLayer.annotations.help', { i18n.translate('expressionXY.annotationLayer.annotations.help', {
defaultMessage: 'Annotations', defaultMessage: 'Annotations',
}), }),
getXAxisConfigFnHelp: () =>
i18n.translate('expressionXY.xAxisConfigFn.help', {
defaultMessage: `Configure the xy chart's x-axis config`,
}),
getYAxisConfigFnHelp: () =>
i18n.translate('expressionXY.yAxisConfigFn.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.labelColor.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',
}),
getReferenceLineNameHelp: () => getReferenceLineNameHelp: () =>
i18n.translate('expressionXY.referenceLine.name.help', { i18n.translate('expressionXY.referenceLine.name.help', {
defaultMessage: 'Reference line name', defaultMessage: 'Reference line name',

View file

@ -11,17 +11,17 @@ export const PLUGIN_NAME = 'expressionXy';
export type { export type {
XYArgs, XYArgs,
YConfig,
EndValue, EndValue,
XYRender, XYRender,
LayerType, LayerType,
YAxisMode,
LineStyle, LineStyle,
FillStyle, FillStyle,
SeriesType, SeriesType,
YScaleType, YScaleType,
XScaleType, XScaleType,
AxisMode,
AxisConfig, AxisConfig,
YAxisConfig,
ValidLayer, ValidLayer,
XYLayerArgs, XYLayerArgs,
XYCurveType, XYCurveType,
@ -33,30 +33,28 @@ export type {
AxisExtentMode, AxisExtentMode,
DataLayerConfig, DataLayerConfig,
FittingFunction, FittingFunction,
ExtendedYConfig,
AxisExtentConfig, AxisExtentConfig,
CollectiveConfig, CollectiveConfig,
LegendConfigResult, LegendConfigResult,
AxesSettingsConfig, AxesSettingsConfig,
XAxisConfigResult,
YAxisConfigResult,
CommonXYLayerConfig, CommonXYLayerConfig,
DataDecorationConfig,
AnnotationLayerArgs, AnnotationLayerArgs,
ExtendedYConfigResult,
GridlinesConfigResult,
DataLayerConfigResult, DataLayerConfigResult,
TickLabelsConfigResult,
AxisExtentConfigResult, AxisExtentConfigResult,
ReferenceLineLayerArgs, ReferenceLineLayerArgs,
CommonXYDataLayerConfig, CommonXYDataLayerConfig,
LabelsOrientationConfig,
ReferenceLineLayerConfig, ReferenceLineLayerConfig,
AvailableReferenceLineIcon, AvailableReferenceLineIcon,
XYExtendedLayerConfigResult, XYExtendedLayerConfigResult,
CommonXYAnnotationLayerConfig, CommonXYAnnotationLayerConfig,
ExtendedDataLayerConfigResult, ExtendedDataLayerConfigResult,
LabelsOrientationConfigResult,
CommonXYDataLayerConfigResult, CommonXYDataLayerConfigResult,
ReferenceLineLayerConfigResult, ReferenceLineLayerConfigResult,
ReferenceLineDecorationConfig,
CommonXYReferenceLineLayerConfig, CommonXYReferenceLineLayerConfig,
AxisTitlesVisibilityConfigResult, ReferenceLineDecorationConfigResult,
CommonXYReferenceLineLayerConfigResult, CommonXYReferenceLineLayerConfigResult,
} from './types'; } from './types';

View file

@ -25,14 +25,11 @@ import {
ValueLabelModes, ValueLabelModes,
XScaleTypes, XScaleTypes,
XYCurveTypes, XYCurveTypes,
YAxisModes,
YScaleTypes, YScaleTypes,
AxisModes,
REFERENCE_LINE, REFERENCE_LINE,
Y_CONFIG, DATA_DECORATION_CONFIG,
AXIS_TITLES_VISIBILITY_CONFIG, REFERENCE_LINE_DECORATION_CONFIG,
LABELS_ORIENTATION_CONFIG,
TICK_LABELS_CONFIG,
GRID_LINES_CONFIG,
LEGEND_CONFIG, LEGEND_CONFIG,
DATA_LAYER, DATA_LAYER,
AXIS_EXTENT_CONFIG, AXIS_EXTENT_CONFIG,
@ -40,23 +37,24 @@ import {
REFERENCE_LINE_LAYER, REFERENCE_LINE_LAYER,
ANNOTATION_LAYER, ANNOTATION_LAYER,
EndValues, EndValues,
EXTENDED_Y_CONFIG, X_AXIS_CONFIG,
Y_AXIS_CONFIG,
AvailableReferenceLineIcons, AvailableReferenceLineIcons,
XY_VIS, XY_VIS,
LAYERED_XY_VIS, LAYERED_XY_VIS,
EXTENDED_ANNOTATION_LAYER, EXTENDED_ANNOTATION_LAYER,
REFERENCE_LINE_Y_CONFIG, EXTENDED_REFERENCE_LINE_DECORATION_CONFIG,
} from '../constants'; } from '../constants';
import { XYRender } from './expression_renderers'; import { XYRender } from './expression_renderers';
export type EndValue = $Values<typeof EndValues>; export type EndValue = $Values<typeof EndValues>;
export type LayerType = $Values<typeof LayerTypes>; export type LayerType = $Values<typeof LayerTypes>;
export type YAxisMode = $Values<typeof YAxisModes>;
export type LineStyle = $Values<typeof LineStyles>; export type LineStyle = $Values<typeof LineStyles>;
export type FillStyle = $Values<typeof FillStyles>; export type FillStyle = $Values<typeof FillStyles>;
export type SeriesType = $Values<typeof SeriesTypes>; export type SeriesType = $Values<typeof SeriesTypes>;
export type YScaleType = $Values<typeof YScaleTypes>; export type YScaleType = $Values<typeof YScaleTypes>;
export type XScaleType = $Values<typeof XScaleTypes>; export type XScaleType = $Values<typeof XScaleTypes>;
export type AxisMode = $Values<typeof AxisModes>;
export type XYCurveType = $Values<typeof XYCurveTypes>; export type XYCurveType = $Values<typeof XYCurveTypes>;
export type IconPosition = $Values<typeof IconPositions>; export type IconPosition = $Values<typeof IconPositions>;
export type ValueLabelMode = $Values<typeof ValueLabelModes>; export type ValueLabelMode = $Values<typeof ValueLabelModes>;
@ -65,7 +63,6 @@ export type FittingFunction = $Values<typeof FittingFunctions>;
export type AvailableReferenceLineIcon = $Values<typeof AvailableReferenceLineIcons>; export type AvailableReferenceLineIcon = $Values<typeof AvailableReferenceLineIcons>;
export interface AxesSettingsConfig { export interface AxesSettingsConfig {
x: boolean;
yLeft: boolean; yLeft: boolean;
yRight: boolean; yRight: boolean;
} }
@ -77,23 +74,41 @@ export interface AxisExtentConfig {
} }
export interface AxisConfig { export interface AxisConfig {
title: string; title?: string;
hide?: boolean; hide?: boolean;
id?: string;
position?: Position;
labelColor?: string;
showOverlappingLabels?: boolean;
showDuplicates?: boolean;
labelsOrientation?: number;
truncate?: number;
showLabels?: boolean;
showTitle?: boolean;
showGridLines?: boolean;
extent?: AxisExtentConfigResult;
} }
export interface ExtendedYConfig extends YConfig { export interface YAxisConfig extends AxisConfig {
mode?: AxisMode;
boundsMargin?: number;
scaleType?: YScaleType;
}
export interface ReferenceLineDecorationConfig extends DataDecorationConfig {
icon?: AvailableReferenceLineIcon; icon?: AvailableReferenceLineIcon;
lineWidth?: number; lineWidth?: number;
lineStyle?: LineStyle; lineStyle?: LineStyle;
fill?: FillStyle; fill?: FillStyle;
iconPosition?: IconPosition; iconPosition?: IconPosition;
textVisibility?: boolean; textVisibility?: boolean;
position?: Position;
} }
export interface YConfig { export interface DataDecorationConfig {
forAccessor: string; forAccessor: string;
axisMode?: YAxisMode;
color?: string; color?: string;
axisId?: string;
} }
export interface DataLayerArgs { export interface DataLayerArgs {
@ -110,8 +125,11 @@ export interface DataLayerArgs {
columnToLabel?: string; // Actually a JSON key-value pair columnToLabel?: string; // Actually a JSON key-value pair
xScaleType: XScaleType; xScaleType: XScaleType;
isHistogram: boolean; isHistogram: boolean;
isPercentage: boolean;
isStacked: boolean;
isHorizontal: boolean;
palette: PaletteOutput; palette: PaletteOutput;
yConfig?: YConfigResult[]; decorations?: DataDecorationConfigResult[];
} }
export interface ValidLayer extends DataLayerConfigResult { export interface ValidLayer extends DataLayerConfigResult {
@ -133,9 +151,12 @@ export interface ExtendedDataLayerArgs {
columnToLabel?: string; // Actually a JSON key-value pair columnToLabel?: string; // Actually a JSON key-value pair
xScaleType: XScaleType; xScaleType: XScaleType;
isHistogram: boolean; isHistogram: boolean;
isPercentage: boolean;
isStacked: boolean;
isHorizontal: boolean;
palette: PaletteOutput; palette: PaletteOutput;
// palette will always be set on the expression // palette will always be set on the expression
yConfig?: YConfigResult[]; decorations?: DataDecorationConfigResult[];
table?: Datatable; table?: Datatable;
} }
@ -185,22 +206,8 @@ export interface LegendConfig {
legendSize?: LegendSize; legendSize?: LegendSize;
} }
export interface LabelsOrientationConfig {
x: number;
yLeft: number;
yRight: number;
}
// Arguments to XY chart expression, with computed properties // Arguments to XY chart expression, with computed properties
export interface XYArgs extends DataLayerArgs { export interface XYArgs extends DataLayerArgs {
xTitle: string;
yTitle: string;
yRightTitle: string;
xExtent?: AxisExtentConfigResult;
yLeftExtent: AxisExtentConfigResult;
yRightExtent: AxisExtentConfigResult;
yLeftScale: YScaleType;
yRightScale: YScaleType;
legend: LegendConfigResult; legend: LegendConfigResult;
endValue?: EndValue; endValue?: EndValue;
emphasizeFitting?: boolean; emphasizeFitting?: boolean;
@ -208,15 +215,13 @@ export interface XYArgs extends DataLayerArgs {
referenceLines: ReferenceLineConfigResult[]; referenceLines: ReferenceLineConfigResult[];
annotationLayers: AnnotationLayerConfigResult[]; annotationLayers: AnnotationLayerConfigResult[];
fittingFunction?: FittingFunction; fittingFunction?: FittingFunction;
axisTitlesVisibilitySettings?: AxisTitlesVisibilityConfigResult;
tickLabelsVisibilitySettings?: TickLabelsConfigResult;
gridlinesVisibilitySettings?: GridlinesConfigResult;
labelsOrientation?: LabelsOrientationConfigResult;
curveType?: XYCurveType; curveType?: XYCurveType;
fillOpacity?: number; fillOpacity?: number;
hideEndzones?: boolean; hideEndzones?: boolean;
valuesInLegend?: boolean; valuesInLegend?: boolean;
ariaLabel?: string; ariaLabel?: string;
yAxisConfigs?: YAxisConfigResult[];
xAxisConfig?: XAxisConfigResult;
addTimeMarker?: boolean; addTimeMarker?: boolean;
markSizeRatio?: number; markSizeRatio?: number;
minTimeBarInterval?: string; minTimeBarInterval?: string;
@ -228,29 +233,19 @@ export interface XYArgs extends DataLayerArgs {
} }
export interface LayeredXYArgs { export interface LayeredXYArgs {
xTitle: string;
yTitle: string;
yRightTitle: string;
xExtent?: AxisExtentConfigResult;
yLeftExtent: AxisExtentConfigResult;
yRightExtent: AxisExtentConfigResult;
yLeftScale: YScaleType;
yRightScale: YScaleType;
legend: LegendConfigResult; legend: LegendConfigResult;
endValue?: EndValue; endValue?: EndValue;
emphasizeFitting?: boolean; emphasizeFitting?: boolean;
valueLabels: ValueLabelMode; valueLabels: ValueLabelMode;
layers?: XYExtendedLayerConfigResult[]; layers?: XYExtendedLayerConfigResult[];
fittingFunction?: FittingFunction; fittingFunction?: FittingFunction;
axisTitlesVisibilitySettings?: AxisTitlesVisibilityConfigResult;
tickLabelsVisibilitySettings?: TickLabelsConfigResult;
gridlinesVisibilitySettings?: GridlinesConfigResult;
labelsOrientation?: LabelsOrientationConfigResult;
curveType?: XYCurveType; curveType?: XYCurveType;
fillOpacity?: number; fillOpacity?: number;
hideEndzones?: boolean; hideEndzones?: boolean;
valuesInLegend?: boolean; valuesInLegend?: boolean;
ariaLabel?: string; ariaLabel?: string;
yAxisConfigs?: YAxisConfigResult[];
xAxisConfig?: XAxisConfigResult;
detailedTooltip?: boolean; detailedTooltip?: boolean;
addTimeMarker?: boolean; addTimeMarker?: boolean;
markSizeRatio?: number; markSizeRatio?: number;
@ -260,29 +255,19 @@ export interface LayeredXYArgs {
} }
export interface XYProps { export interface XYProps {
xTitle: string;
yTitle: string;
yRightTitle: string;
xExtent?: AxisExtentConfigResult;
yLeftExtent: AxisExtentConfigResult;
yRightExtent: AxisExtentConfigResult;
yLeftScale: YScaleType;
yRightScale: YScaleType;
legend: LegendConfigResult; legend: LegendConfigResult;
endValue?: EndValue; endValue?: EndValue;
emphasizeFitting?: boolean; emphasizeFitting?: boolean;
valueLabels: ValueLabelMode; valueLabels: ValueLabelMode;
layers: CommonXYLayerConfig[]; layers: CommonXYLayerConfig[];
fittingFunction?: FittingFunction; fittingFunction?: FittingFunction;
axisTitlesVisibilitySettings?: AxisTitlesVisibilityConfigResult;
tickLabelsVisibilitySettings?: TickLabelsConfigResult;
gridlinesVisibilitySettings?: GridlinesConfigResult;
labelsOrientation?: LabelsOrientationConfigResult;
curveType?: XYCurveType; curveType?: XYCurveType;
fillOpacity?: number; fillOpacity?: number;
hideEndzones?: boolean; hideEndzones?: boolean;
valuesInLegend?: boolean; valuesInLegend?: boolean;
ariaLabel?: string; ariaLabel?: string;
yAxisConfigs?: YAxisConfigResult[];
xAxisConfig?: XAxisConfigResult;
addTimeMarker?: boolean; addTimeMarker?: boolean;
markSizeRatio?: number; markSizeRatio?: number;
minTimeBarInterval?: string; minTimeBarInterval?: string;
@ -312,7 +297,8 @@ export type ExtendedAnnotationLayerConfigResult = ExtendedAnnotationLayerArgs &
layerType: typeof LayerTypes.ANNOTATIONS; layerType: typeof LayerTypes.ANNOTATIONS;
}; };
export interface ReferenceLineArgs extends Omit<ExtendedYConfig, 'forAccessor' | 'fill'> { export interface ReferenceLineArgs
extends Omit<ReferenceLineDecorationConfig, 'forAccessor' | 'fill'> {
name?: string; name?: string;
value: number; value: number;
fill: FillStyle; fill: FillStyle;
@ -322,7 +308,7 @@ export interface ReferenceLineLayerArgs {
layerId?: string; layerId?: string;
accessors: string[]; accessors: string[];
columnToLabel?: string; columnToLabel?: string;
yConfig?: ExtendedYConfigResult[]; decorations?: ReferenceLineDecorationConfigResult[];
table?: Datatable; table?: Datatable;
} }
@ -338,15 +324,15 @@ export type XYExtendedLayerConfigResult =
| ReferenceLineLayerConfigResult | ReferenceLineLayerConfigResult
| ExtendedAnnotationLayerConfigResult; | ExtendedAnnotationLayerConfigResult;
export interface ReferenceLineYConfig extends ReferenceLineArgs { export interface ExtendedReferenceLineDecorationConfig extends ReferenceLineArgs {
type: typeof REFERENCE_LINE_Y_CONFIG; type: typeof EXTENDED_REFERENCE_LINE_DECORATION_CONFIG;
} }
export interface ReferenceLineConfigResult { export interface ReferenceLineConfigResult {
type: typeof REFERENCE_LINE; type: typeof REFERENCE_LINE;
layerType: typeof LayerTypes.REFERENCELINE; layerType: typeof LayerTypes.REFERENCELINE;
lineLength: number; lineLength: number;
yConfig: [ReferenceLineYConfig]; decorations: [ExtendedReferenceLineDecorationConfig];
} }
export type ReferenceLineLayerConfigResult = ReferenceLineLayerArgs & { export type ReferenceLineLayerConfigResult = ReferenceLineLayerArgs & {
@ -381,21 +367,18 @@ export type ExtendedDataLayerConfigResult = Omit<ExtendedDataLayerArgs, 'palette
table: Datatable; table: Datatable;
}; };
export type YConfigResult = YConfig & { type: typeof Y_CONFIG }; export type DataDecorationConfigResult = DataDecorationConfig & {
export type ExtendedYConfigResult = ExtendedYConfig & { type: typeof EXTENDED_Y_CONFIG }; type: typeof DATA_DECORATION_CONFIG;
};
export type AxisTitlesVisibilityConfigResult = AxesSettingsConfig & { export type ReferenceLineDecorationConfigResult = ReferenceLineDecorationConfig & {
type: typeof AXIS_TITLES_VISIBILITY_CONFIG; type: typeof REFERENCE_LINE_DECORATION_CONFIG;
}; };
export type LabelsOrientationConfigResult = LabelsOrientationConfig & { export type XAxisConfigResult = AxisConfig & { type: typeof X_AXIS_CONFIG };
type: typeof LABELS_ORIENTATION_CONFIG; export type YAxisConfigResult = YAxisConfig & { type: typeof Y_AXIS_CONFIG };
};
export type LegendConfigResult = LegendConfig & { type: typeof LEGEND_CONFIG }; export type LegendConfigResult = LegendConfig & { type: typeof LEGEND_CONFIG };
export type AxisExtentConfigResult = AxisExtentConfig & { type: typeof AXIS_EXTENT_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 CommonXYLayerConfig = XYLayerConfig | XYExtendedLayerConfig; export type CommonXYLayerConfig = XYLayerConfig | XYExtendedLayerConfig;
export type CommonXYDataLayerConfigResult = DataLayerConfigResult | ExtendedDataLayerConfigResult; export type CommonXYDataLayerConfigResult = DataLayerConfigResult | ExtendedDataLayerConfigResult;
@ -441,12 +424,17 @@ export type ReferenceLineLayerFn = ExpressionFunctionDefinition<
Promise<ReferenceLineLayerConfigResult> Promise<ReferenceLineLayerConfigResult>
>; >;
export type YConfigFn = ExpressionFunctionDefinition<typeof Y_CONFIG, null, YConfig, YConfigResult>; export type DataDecorationConfigFn = ExpressionFunctionDefinition<
export type ExtendedYConfigFn = ExpressionFunctionDefinition< typeof DATA_DECORATION_CONFIG,
typeof EXTENDED_Y_CONFIG,
null, null,
ExtendedYConfig, DataDecorationConfig,
ExtendedYConfigResult DataDecorationConfigResult
>;
export type ReferenceLineDecorationConfigFn = ExpressionFunctionDefinition<
typeof REFERENCE_LINE_DECORATION_CONFIG,
null,
ReferenceLineDecorationConfig,
ReferenceLineDecorationConfigResult
>; >;
export type LegendConfigFn = ExpressionFunctionDefinition< export type LegendConfigFn = ExpressionFunctionDefinition<
@ -455,3 +443,17 @@ export type LegendConfigFn = ExpressionFunctionDefinition<
LegendConfig, LegendConfig,
Promise<LegendConfigResult> Promise<LegendConfigResult>
>; >;
export type XAxisConfigFn = ExpressionFunctionDefinition<
typeof X_AXIS_CONFIG,
null,
AxisConfig,
XAxisConfigResult
>;
export type YAxisConfigFn = ExpressionFunctionDefinition<
typeof Y_AXIS_CONFIG,
null,
YAxisConfig,
YAxisConfigResult
>;

View file

@ -26,7 +26,7 @@ export interface XYRender {
export interface CollectiveConfig extends Omit<ManualPointEventAnnotationArgs, 'icon'> { export interface CollectiveConfig extends Omit<ManualPointEventAnnotationArgs, 'icon'> {
roundedTimestamp: number; roundedTimestamp: number;
axisMode: 'bottom'; position: 'bottom';
icon?: AvailableAnnotationIcon | string; icon?: AvailableAnnotationIcon | string;
customTooltipDetails?: AnnotationTooltipFormatter | undefined; customTooltipDetails?: AnnotationTooltipFormatter | undefined;
} }

View file

@ -6,6 +6,7 @@
* Side Public License, v 1. * Side Public License, v 1.
*/ */
import { Position } from '@elastic/charts';
import { Datatable } from '@kbn/expressions-plugin/common'; import { Datatable } from '@kbn/expressions-plugin/common';
import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; import { chartPluginMock } from '@kbn/charts-plugin/public/mocks';
import { LayerTypes } from '../../common/constants'; import { LayerTypes } from '../../common/constants';
@ -167,8 +168,11 @@ export const dateHistogramLayer: DataLayerConfig = {
xAccessor: 'xAccessorId', xAccessor: 'xAccessorId',
xScaleType: 'time', xScaleType: 'time',
isHistogram: true, isHistogram: true,
isStacked: true,
isPercentage: false,
isHorizontal: false,
splitAccessor: 'splitAccessorId', splitAccessor: 'splitAccessorId',
seriesType: 'bar_stacked', seriesType: 'bar',
accessors: ['yAccessorId'], accessors: ['yAccessorId'],
palette: mockPaletteOutput, palette: mockPaletteOutput,
table: dateHistogramData, table: dateHistogramData,
@ -197,7 +201,13 @@ export function sampleArgsWithReferenceLine(value: number = 150) {
type: 'referenceLineLayer', type: 'referenceLineLayer',
layerType: LayerTypes.REFERENCELINE, layerType: LayerTypes.REFERENCELINE,
accessors: ['referenceLine-a'], accessors: ['referenceLine-a'],
yConfig: [{ axisMode: 'left', forAccessor: 'referenceLine-a', type: 'extendedYConfig' }], decorations: [
{
forAccessor: 'referenceLine-a',
type: 'referenceLineDecorationConfig',
position: Position.Left,
},
],
table: data, table: data,
}, },
], ],

View file

@ -155,7 +155,7 @@ export const getAnnotationsGroupedByInterval = (
collectiveConfig = { collectiveConfig = {
...configArr[0], ...configArr[0],
roundedTimestamp: Number(roundedTimestamp), roundedTimestamp: Number(roundedTimestamp),
axisMode: 'bottom', position: 'bottom',
}; };
if (configArr.length > 1) { if (configArr.length > 1) {
const commonStyles = getCommonStyles(configArr); const commonStyles = getCommonStyles(configArr);

View file

@ -25,7 +25,7 @@ import {
XYCurveType, XYCurveType,
XScaleType, XScaleType,
} from '../../common'; } from '../../common';
import { SeriesTypes, ValueLabelModes } from '../../common/constants'; import { SeriesTypes, ValueLabelModes, AxisModes } from '../../common/constants';
import { import {
getColorAssignments, getColorAssignments,
getFitOptions, getFitOptions,
@ -90,12 +90,14 @@ export const DataLayers: FC<Props> = ({
// 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 // 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 formattedDatatableInfo = formattedDatatables[layerId]; const formattedDatatableInfo = formattedDatatables[layerId];
const isPercentage = seriesType.includes('percentage');
const yAxis = yAxesConfiguration.find((axisConfiguration) => const yAxis = yAxesConfiguration.find((axisConfiguration) =>
axisConfiguration.series.find((currentSeries) => currentSeries.accessor === yColumnId) axisConfiguration.series.find((currentSeries) => currentSeries.accessor === yColumnId)
); );
const isPercentage = yAxis?.mode
? yAxis?.mode === AxisModes.PERCENTAGE
: layer.isPercentage;
const seriesProps = getSeriesProps({ const seriesProps = getSeriesProps({
layer, layer,
titles: titles[layer.layerId], titles: titles[layer.layerId],
@ -129,11 +131,6 @@ export const DataLayers: FC<Props> = ({
/> />
); );
case SeriesTypes.BAR: 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 = { const valueLabelsSettings = {
displayValueSettings: { displayValueSettings: {
// This format double fixes two issues in elastic-chart // This format double fixes two issues in elastic-chart
@ -150,22 +147,12 @@ export const DataLayers: FC<Props> = ({
}, },
}; };
return <BarSeries key={index} {...seriesProps} {...valueLabelsSettings} />; return <BarSeries key={index} {...seriesProps} {...valueLabelsSettings} />;
case SeriesTypes.AREA_STACKED:
case SeriesTypes.AREA_PERCENTAGE_STACKED:
return (
<AreaSeries
key={index}
{...seriesProps}
fit={isPercentage ? 'zero' : getFitOptions(fittingFunction, endValue)}
curve={curve}
/>
);
case SeriesTypes.AREA: case SeriesTypes.AREA:
return ( return (
<AreaSeries <AreaSeries
key={index} key={index}
{...seriesProps} {...seriesProps}
fit={getFitOptions(fittingFunction, endValue)} fit={isPercentage ? 'zero' : getFitOptions(fittingFunction, endValue)}
curve={curve} curve={curve}
/> />
); );

View file

@ -157,6 +157,9 @@ const sampleLayer: DataLayerConfig = {
type: 'dataLayer', type: 'dataLayer',
layerType: LayerTypes.DATA, layerType: LayerTypes.DATA,
seriesType: 'line', seriesType: 'line',
isStacked: false,
isPercentage: false,
isHorizontal: false,
showLines: true, showLines: true,
xAccessor: 'c', xAccessor: 'c',
accessors: ['a', 'b'], accessors: ['a', 'b'],

View file

@ -10,47 +10,49 @@ import React, { FC } from 'react';
import { Position } from '@elastic/charts'; import { Position } from '@elastic/charts';
import { FieldFormat } from '@kbn/field-formats-plugin/common'; import { FieldFormat } from '@kbn/field-formats-plugin/common';
import { ReferenceLineConfig } from '../../../common/types'; import { ReferenceLineConfig } from '../../../common/types';
import { getGroupId } from './utils';
import { ReferenceLineAnnotations } from './reference_line_annotations'; import { ReferenceLineAnnotations } from './reference_line_annotations';
import { AxesMap, GroupsConfiguration } from '../../helpers';
import { getAxisGroupForReferenceLine } from './utils';
interface ReferenceLineProps { interface ReferenceLineProps {
layer: ReferenceLineConfig; layer: ReferenceLineConfig;
paddingMap: Partial<Record<Position, number>>; paddingMap: Partial<Record<Position, number>>;
formatters: Record<'left' | 'right' | 'bottom', FieldFormat | undefined>; xAxisFormatter: FieldFormat;
axesMap: Record<'left' | 'right', boolean>; axesConfiguration: GroupsConfiguration;
isHorizontal: boolean; isHorizontal: boolean;
nextValue?: number; nextValue?: number;
yAxesMap: AxesMap;
} }
export const ReferenceLine: FC<ReferenceLineProps> = ({ export const ReferenceLine: FC<ReferenceLineProps> = ({
layer, layer,
axesMap, axesConfiguration,
formatters, xAxisFormatter,
paddingMap, paddingMap,
isHorizontal, isHorizontal,
nextValue, nextValue,
yAxesMap,
}) => { }) => {
const { const {
yConfig: [yConfig], decorations: [decorationConfig],
} = layer; } = layer;
if (!yConfig) { if (!decorationConfig) {
return null; return null;
} }
const { axisMode, value } = yConfig; const { value } = decorationConfig;
// Find the formatter for the given axis const axisGroup = getAxisGroupForReferenceLine(axesConfiguration, decorationConfig, isHorizontal);
const groupId = getGroupId(axisMode);
const formatter = formatters[groupId || 'bottom']; const formatter = axisGroup?.formatter || xAxisFormatter;
const id = `${layer.layerId}-${value}`; const id = `${layer.layerId}-${value}`;
return ( return (
<ReferenceLineAnnotations <ReferenceLineAnnotations
config={{ id, ...yConfig, nextValue }} config={{ id, ...decorationConfig, nextValue, axisGroup }}
paddingMap={paddingMap} paddingMap={paddingMap}
axesMap={axesMap} axesMap={yAxesMap}
formatter={formatter} formatter={formatter}
isHorizontal={isHorizontal} isHorizontal={isHorizontal}
/> />

View file

@ -10,18 +10,21 @@ import { AnnotationDomainType, LineAnnotation, Position, RectAnnotation } from '
import { euiLightVars } from '@kbn/ui-theme'; import { euiLightVars } from '@kbn/ui-theme';
import React, { FC } from 'react'; import React, { FC } from 'react';
import { FieldFormat } from '@kbn/field-formats-plugin/common'; import { FieldFormat } from '@kbn/field-formats-plugin/common';
import { LINES_MARKER_SIZE } from '../../helpers'; import {
AxesMap,
AxisConfiguration,
getOriginalAxisPosition,
LINES_MARKER_SIZE,
} from '../../helpers';
import { import {
AvailableReferenceLineIcon, AvailableReferenceLineIcon,
FillStyle, FillStyle,
IconPosition, IconPosition,
LineStyle, LineStyle,
YAxisMode,
} from '../../../common/types'; } from '../../../common/types';
import { import {
getBaseIconPlacement, getBaseIconPlacement,
getBottomRect, getBottomRect,
getGroupId,
getHorizontalRect, getHorizontalRect,
getLineAnnotationProps, getLineAnnotationProps,
getSharedStyle, getSharedStyle,
@ -38,7 +41,7 @@ export interface ReferenceLineAnnotationConfig {
fill?: FillStyle; fill?: FillStyle;
iconPosition?: IconPosition; iconPosition?: IconPosition;
textVisibility?: boolean; textVisibility?: boolean;
axisMode?: YAxisMode; axisGroup?: AxisConfiguration;
color?: string; color?: string;
} }
@ -46,18 +49,19 @@ interface Props {
config: ReferenceLineAnnotationConfig; config: ReferenceLineAnnotationConfig;
paddingMap: Partial<Record<Position, number>>; paddingMap: Partial<Record<Position, number>>;
formatter?: FieldFormat; formatter?: FieldFormat;
axesMap: Record<'left' | 'right', boolean>; axesMap: AxesMap;
isHorizontal: boolean; isHorizontal: boolean;
} }
const getRectDataValue = ( const getRectDataValue = (
annotationConfig: ReferenceLineAnnotationConfig, annotationConfig: ReferenceLineAnnotationConfig,
formatter: FieldFormat | undefined formatter: FieldFormat | undefined,
groupId: string
) => { ) => {
const { name, value, nextValue, fill, axisMode } = annotationConfig; const { name, value, nextValue, fill } = annotationConfig;
const isFillAbove = fill === 'above'; const isFillAbove = fill === 'above';
if (axisMode === 'bottom') { if (groupId === Position.Bottom) {
return getBottomRect(name, isFillAbove, formatter, value, nextValue); return getBottomRect(name, isFillAbove, formatter, value, nextValue);
} }
@ -71,13 +75,15 @@ export const ReferenceLineAnnotations: FC<Props> = ({
paddingMap, paddingMap,
isHorizontal, 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; const defaultColor = euiLightVars.euiColorDarkShade;
// get the position for vertical chart // get the position for vertical chart
const markerPositionVertical = getBaseIconPlacement(iconPosition, axesMap, axisMode); const markerPositionVertical = getBaseIconPlacement(
iconPosition,
axesMap,
getOriginalAxisPosition(axisGroup?.position ?? Position.Bottom, isHorizontal)
);
// the padding map is built for vertical chart // the padding map is built for vertical chart
const hasReducedPadding = paddingMap[markerPositionVertical] === LINES_MARKER_SIZE; const hasReducedPadding = paddingMap[markerPositionVertical] === LINES_MARKER_SIZE;
@ -89,7 +95,6 @@ export const ReferenceLineAnnotations: FC<Props> = ({
}, },
axesMap, axesMap,
paddingMap, paddingMap,
groupId,
isHorizontal isHorizontal
); );
@ -108,7 +113,9 @@ export const ReferenceLineAnnotations: FC<Props> = ({
key={`${id}-line`} key={`${id}-line`}
dataValues={[dataValues]} dataValues={[dataValues]}
domainType={ domainType={
axisMode === 'bottom' ? AnnotationDomainType.XDomain : AnnotationDomainType.YDomain props.groupId === Position.Bottom
? AnnotationDomainType.XDomain
: AnnotationDomainType.YDomain
} }
style={{ line: { ...sharedStyle, opacity: 1 } }} style={{ line: { ...sharedStyle, opacity: 1 } }}
/> />
@ -116,7 +123,7 @@ export const ReferenceLineAnnotations: FC<Props> = ({
let rect; let rect;
if (fill && fill !== 'none') { if (fill && fill !== 'none') {
const rectDataValues = getRectDataValue(config, formatter); const rectDataValues = getRectDataValue(config, formatter, props.groupId);
rect = ( rect = (
<RectAnnotation <RectAnnotation

View file

@ -11,67 +11,73 @@ import { FieldFormat } from '@kbn/field-formats-plugin/common';
import { groupBy } from 'lodash'; import { groupBy } from 'lodash';
import { Position } from '@elastic/charts'; import { Position } from '@elastic/charts';
import { ReferenceLineLayerConfig } from '../../../common/types'; import { ReferenceLineLayerConfig } from '../../../common/types';
import { getGroupId } from './utils';
import { ReferenceLineAnnotations } from './reference_line_annotations'; import { ReferenceLineAnnotations } from './reference_line_annotations';
import { LayerAccessorsTitles } from '../../helpers'; import { LayerAccessorsTitles, GroupsConfiguration, AxesMap } from '../../helpers';
import { getAxisGroupForReferenceLine } from './utils';
interface ReferenceLineLayerProps { interface ReferenceLineLayerProps {
layer: ReferenceLineLayerConfig; layer: ReferenceLineLayerConfig;
formatters: Record<'left' | 'right' | 'bottom', FieldFormat | undefined>;
paddingMap: Partial<Record<Position, number>>; paddingMap: Partial<Record<Position, number>>;
axesMap: Record<'left' | 'right', boolean>;
isHorizontal: boolean; isHorizontal: boolean;
titles?: LayerAccessorsTitles; titles?: LayerAccessorsTitles;
xAxisFormatter: FieldFormat;
axesConfiguration: GroupsConfiguration;
yAxesMap: AxesMap;
} }
export const ReferenceLineLayer: FC<ReferenceLineLayerProps> = ({ export const ReferenceLineLayer: FC<ReferenceLineLayerProps> = ({
layer, layer,
formatters, axesConfiguration,
xAxisFormatter,
paddingMap, paddingMap,
axesMap,
isHorizontal, isHorizontal,
titles, titles,
yAxesMap,
}) => { }) => {
if (!layer.yConfig) { if (!layer.decorations) {
return null; return null;
} }
const { columnToLabel, yConfig: yConfigs, table } = layer; const { columnToLabel, decorations, table } = layer;
const columnToLabelMap: Record<string, string> = columnToLabel ? JSON.parse(columnToLabel) : {}; const columnToLabelMap: Record<string, string> = columnToLabel ? JSON.parse(columnToLabel) : {};
const row = table.rows[0]; const row = table.rows[0];
const yConfigByValue = yConfigs.sort( const decorationConfigsByValue = decorations.sort(
({ forAccessor: idA }, { forAccessor: idB }) => row[idA] - row[idB] ({ forAccessor: idA }, { forAccessor: idB }) => row[idA] - row[idB]
); );
const groupedByDirection = groupBy(yConfigByValue, 'fill'); const groupedByDirection = groupBy(decorationConfigsByValue, 'fill');
if (groupedByDirection.below) { if (groupedByDirection.below) {
groupedByDirection.below.reverse(); groupedByDirection.below.reverse();
} }
const referenceLineElements = yConfigByValue.flatMap((yConfig) => { const referenceLineElements = decorationConfigsByValue.flatMap((decorationConfig) => {
const { axisMode } = yConfig; const axisGroup = getAxisGroupForReferenceLine(
axesConfiguration,
// Find the formatter for the given axis decorationConfig,
const groupId = getGroupId(axisMode); isHorizontal
const formatter = formatters[groupId || 'bottom'];
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 shouldCheckNextReferenceLine = indexFromSameType < yConfigsWithSameDirection.length - 1; const formatter = axisGroup?.formatter || xAxisFormatter;
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 < yDecorationsWithSameDirection.length - 1;
const nextValue = shouldCheckNextReferenceLine const nextValue = shouldCheckNextReferenceLine
? row[yConfigsWithSameDirection[indexFromSameType + 1].forAccessor] ? row[yDecorationsWithSameDirection[indexFromSameType + 1].forAccessor]
: undefined; : undefined;
const { forAccessor, type, ...restAnnotationConfig } = yConfig; const { forAccessor, type, ...restAnnotationConfig } = decorationConfig;
const id = `${layer.layerId}-${yConfig.forAccessor}`; const id = `${layer.layerId}-${decorationConfig.forAccessor}`;
return ( return (
<ReferenceLineAnnotations <ReferenceLineAnnotations
@ -82,9 +88,10 @@ export const ReferenceLineLayer: FC<ReferenceLineLayerProps> = ({
nextValue, nextValue,
name, name,
...restAnnotationConfig, ...restAnnotationConfig,
axisGroup,
}} }}
axesMap={yAxesMap}
paddingMap={paddingMap} paddingMap={paddingMap}
axesMap={axesMap}
formatter={formatter} formatter={formatter}
isHorizontal={isHorizontal} isHorizontal={isHorizontal}
/> />

View file

@ -15,7 +15,7 @@ import { LayerTypes } from '../../../common/constants';
import { import {
ReferenceLineLayerArgs, ReferenceLineLayerArgs,
ReferenceLineLayerConfig, ReferenceLineLayerConfig,
ExtendedYConfig, ExtendedReferenceLineDecorationConfig,
ReferenceLineArgs, ReferenceLineArgs,
ReferenceLineConfig, ReferenceLineConfig,
} from '../../../common/types'; } from '../../../common/types';
@ -46,12 +46,14 @@ const data: Datatable = {
})), })),
}; };
function createLayers(yConfigs: ReferenceLineLayerArgs['yConfig']): ReferenceLineLayerConfig[] { function createLayers(
decorations: ReferenceLineLayerArgs['decorations']
): ReferenceLineLayerConfig[] {
return [ return [
{ {
layerId: 'first', layerId: 'first',
accessors: (yConfigs || []).map(({ forAccessor }) => forAccessor), accessors: (decorations || []).map(({ forAccessor }) => forAccessor),
yConfig: yConfigs, decorations,
type: 'referenceLineLayer', type: 'referenceLineLayer',
layerType: LayerTypes.REFERENCELINE, layerType: LayerTypes.REFERENCELINE,
table: data, table: data,
@ -69,7 +71,7 @@ function createReferenceLine(
type: 'referenceLine', type: 'referenceLine',
layerType: 'referenceLine', layerType: 'referenceLine',
lineLength, lineLength,
yConfig: [{ type: 'referenceLineYConfig', ...args }], decorations: [{ type: 'extendedReferenceLineDecorationConfig', ...args }],
}; };
} }
@ -82,7 +84,7 @@ interface XCoords {
x1: number | undefined; x1: number | undefined;
} }
function getAxisFromId(layerPrefix: string): ExtendedYConfig['axisMode'] { function getAxisFromId(layerPrefix: string): ExtendedReferenceLineDecorationConfig['position'] {
return /left/i.test(layerPrefix) ? 'left' : /right/i.test(layerPrefix) ? 'right' : 'bottom'; return /left/i.test(layerPrefix) ? 'left' : /right/i.test(layerPrefix) ? 'right' : 'bottom';
} }
@ -90,21 +92,42 @@ const emptyCoords = { x0: undefined, x1: undefined, y0: undefined, y1: undefined
describe('ReferenceLines', () => { describe('ReferenceLines', () => {
describe('referenceLineLayers', () => { describe('referenceLineLayers', () => {
let formatters: Record<'left' | 'right' | 'bottom', FieldFormat | undefined>;
let defaultProps: Omit<ReferenceLinesProps, 'data' | 'layers'>; let defaultProps: Omit<ReferenceLinesProps, 'data' | 'layers'>;
beforeEach(() => { 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 = { defaultProps = {
formatters, xAxisFormatter: { convert: jest.fn((x) => x) } as unknown as FieldFormat,
isHorizontal: false, isHorizontal: false,
axesMap: { left: true, right: false }, axesConfiguration: [
{
groupId: 'left',
position: 'left',
series: [],
},
{
groupId: 'right',
position: 'right',
series: [],
},
{
groupId: 'bottom',
position: 'bottom',
series: [],
},
],
paddingMap: {}, paddingMap: {},
yAxesMap: {
left: {
groupId: 'left',
position: 'left',
series: [],
},
right: {
groupId: 'right',
position: 'right',
series: [],
},
},
}; };
}); });
@ -113,20 +136,20 @@ describe('ReferenceLines', () => {
['yAccessorLeft', 'below'], ['yAccessorLeft', 'below'],
['yAccessorRight', 'above'], ['yAccessorRight', 'above'],
['yAccessorRight', 'below'], ['yAccessorRight', 'below'],
] as Array<[string, Exclude<ExtendedYConfig['fill'], undefined>]>)( ] as Array<[string, Exclude<ExtendedReferenceLineDecorationConfig['fill'], undefined>]>)(
'should render a RectAnnotation for a reference line with fill set: %s %s', 'should render a RectAnnotation for a reference line with fill set: %s %s',
(layerPrefix, fill) => { (layerPrefix, fill) => {
const axisMode = getAxisFromId(layerPrefix); const position = getAxisFromId(layerPrefix);
const wrapper = shallow( const wrapper = shallow(
<ReferenceLines <ReferenceLines
{...defaultProps} {...defaultProps}
layers={createLayers([ layers={createLayers([
{ {
forAccessor: `${layerPrefix}FirstId`, forAccessor: `${layerPrefix}FirstId`,
axisMode, position,
lineStyle: 'solid', lineStyle: 'solid',
fill, fill,
type: 'extendedYConfig', type: 'referenceLineDecorationConfig',
}, },
])} ])}
/> />
@ -154,7 +177,7 @@ describe('ReferenceLines', () => {
it.each([ it.each([
['xAccessor', 'above'], ['xAccessor', 'above'],
['xAccessor', 'below'], ['xAccessor', 'below'],
] as Array<[string, Exclude<ExtendedYConfig['fill'], undefined>]>)( ] as Array<[string, Exclude<ExtendedReferenceLineDecorationConfig['fill'], undefined>]>)(
'should render a RectAnnotation for a reference line with fill set: %s %s', 'should render a RectAnnotation for a reference line with fill set: %s %s',
(layerPrefix, fill) => { (layerPrefix, fill) => {
const wrapper = shallow( const wrapper = shallow(
@ -163,9 +186,9 @@ describe('ReferenceLines', () => {
layers={createLayers([ layers={createLayers([
{ {
forAccessor: `${layerPrefix}FirstId`, forAccessor: `${layerPrefix}FirstId`,
axisMode: 'bottom', position: 'bottom',
lineStyle: 'solid', lineStyle: 'solid',
type: 'extendedYConfig', type: 'referenceLineDecorationConfig',
fill, fill,
}, },
])} ])}
@ -196,26 +219,26 @@ describe('ReferenceLines', () => {
['yAccessorLeft', 'below', { y0: undefined, y1: 5 }, { y0: 5, y1: 10 }], ['yAccessorLeft', 'below', { y0: undefined, y1: 5 }, { y0: 5, y1: 10 }],
['yAccessorRight', 'above', { y0: 5, y1: 10 }, { y0: 10, y1: undefined }], ['yAccessorRight', 'above', { y0: 5, y1: 10 }, { y0: 10, y1: undefined }],
['yAccessorRight', 'below', { y0: undefined, y1: 5 }, { y0: 5, y1: 10 }], ['yAccessorRight', 'below', { y0: undefined, y1: 5 }, { y0: 5, y1: 10 }],
] as Array<[string, Exclude<ExtendedYConfig['fill'], undefined>, YCoords, YCoords]>)( ] as Array<[string, Exclude<ExtendedReferenceLineDecorationConfig['fill'], undefined>, YCoords, YCoords]>)(
'should avoid overlap between two reference lines with fill in the same direction: 2 x %s %s', 'should avoid overlap between two reference lines with fill in the same direction: 2 x %s %s',
(layerPrefix, fill, coordsA, coordsB) => { (layerPrefix, fill, coordsA, coordsB) => {
const axisMode = getAxisFromId(layerPrefix); const position = getAxisFromId(layerPrefix);
const wrapper = shallow( const wrapper = shallow(
<ReferenceLines <ReferenceLines
{...defaultProps} {...defaultProps}
layers={createLayers([ layers={createLayers([
{ {
forAccessor: `${layerPrefix}FirstId`, forAccessor: `${layerPrefix}FirstId`,
axisMode, position,
lineStyle: 'solid', lineStyle: 'solid',
type: 'extendedYConfig', type: 'referenceLineDecorationConfig',
fill, fill,
}, },
{ {
forAccessor: `${layerPrefix}SecondId`, forAccessor: `${layerPrefix}SecondId`,
axisMode, position,
lineStyle: 'solid', lineStyle: 'solid',
type: 'extendedYConfig', type: 'referenceLineDecorationConfig',
fill, fill,
}, },
])} ])}
@ -252,7 +275,7 @@ describe('ReferenceLines', () => {
it.each([ it.each([
['xAccessor', 'above', { x0: 1, x1: 2 }, { x0: 2, x1: undefined }], ['xAccessor', 'above', { x0: 1, x1: 2 }, { x0: 2, x1: undefined }],
['xAccessor', 'below', { x0: undefined, x1: 1 }, { x0: 1, x1: 2 }], ['xAccessor', 'below', { x0: undefined, x1: 1 }, { x0: 1, x1: 2 }],
] as Array<[string, Exclude<ExtendedYConfig['fill'], undefined>, XCoords, XCoords]>)( ] as Array<[string, Exclude<ExtendedReferenceLineDecorationConfig['fill'], undefined>, XCoords, XCoords]>)(
'should avoid overlap between two reference lines with fill in the same direction: 2 x %s %s', 'should avoid overlap between two reference lines with fill in the same direction: 2 x %s %s',
(layerPrefix, fill, coordsA, coordsB) => { (layerPrefix, fill, coordsA, coordsB) => {
const wrapper = shallow( const wrapper = shallow(
@ -261,16 +284,16 @@ describe('ReferenceLines', () => {
layers={createLayers([ layers={createLayers([
{ {
forAccessor: `${layerPrefix}FirstId`, forAccessor: `${layerPrefix}FirstId`,
axisMode: 'bottom', position: 'bottom',
lineStyle: 'solid', lineStyle: 'solid',
type: 'extendedYConfig', type: 'referenceLineDecorationConfig',
fill, fill,
}, },
{ {
forAccessor: `${layerPrefix}SecondId`, forAccessor: `${layerPrefix}SecondId`,
axisMode: 'bottom', position: 'bottom',
lineStyle: 'solid', lineStyle: 'solid',
type: 'extendedYConfig', type: 'referenceLineDecorationConfig',
fill, fill,
}, },
])} ])}
@ -307,7 +330,7 @@ describe('ReferenceLines', () => {
it.each(['yAccessorLeft', 'yAccessorRight', 'xAccessor'])( it.each(['yAccessorLeft', 'yAccessorRight', 'xAccessor'])(
'should let areas in different directions overlap: %s', 'should let areas in different directions overlap: %s',
(layerPrefix) => { (layerPrefix) => {
const axisMode = getAxisFromId(layerPrefix); const position = getAxisFromId(layerPrefix);
const wrapper = shallow( const wrapper = shallow(
<ReferenceLines <ReferenceLines
@ -315,17 +338,17 @@ describe('ReferenceLines', () => {
layers={createLayers([ layers={createLayers([
{ {
forAccessor: `${layerPrefix}FirstId`, forAccessor: `${layerPrefix}FirstId`,
axisMode, position,
lineStyle: 'solid', lineStyle: 'solid',
fill: 'above', fill: 'above',
type: 'extendedYConfig', type: 'referenceLineDecorationConfig',
}, },
{ {
forAccessor: `${layerPrefix}SecondId`, forAccessor: `${layerPrefix}SecondId`,
axisMode, position,
lineStyle: 'solid', lineStyle: 'solid',
fill: 'below', fill: 'below',
type: 'extendedYConfig', type: 'referenceLineDecorationConfig',
}, },
])} ])}
/> />
@ -338,8 +361,8 @@ describe('ReferenceLines', () => {
).toEqual( ).toEqual(
expect.arrayContaining([ expect.arrayContaining([
{ {
coordinates: { ...emptyCoords, ...(axisMode === 'bottom' ? { x0: 1 } : { y0: 5 }) }, coordinates: { ...emptyCoords, ...(position === 'bottom' ? { x0: 1 } : { y0: 5 }) },
details: axisMode === 'bottom' ? 1 : 5, details: position === 'bottom' ? 1 : 5,
header: undefined, header: undefined,
}, },
]) ])
@ -349,8 +372,8 @@ describe('ReferenceLines', () => {
).toEqual( ).toEqual(
expect.arrayContaining([ expect.arrayContaining([
{ {
coordinates: { ...emptyCoords, ...(axisMode === 'bottom' ? { x1: 2 } : { y1: 10 }) }, coordinates: { ...emptyCoords, ...(position === 'bottom' ? { x1: 2 } : { y1: 10 }) },
details: axisMode === 'bottom' ? 2 : 10, details: position === 'bottom' ? 2 : 10,
header: undefined, header: undefined,
}, },
]) ])
@ -361,7 +384,7 @@ describe('ReferenceLines', () => {
it.each([ it.each([
['above', { y0: 5, y1: 10 }, { y0: 10, y1: undefined }], ['above', { y0: 5, y1: 10 }, { y0: 10, y1: undefined }],
['below', { y0: undefined, y1: 5 }, { y0: 5, y1: 10 }], ['below', { y0: undefined, y1: 5 }, { y0: 5, y1: 10 }],
] as Array<[Exclude<ExtendedYConfig['fill'], undefined>, YCoords, YCoords]>)( ] as Array<[Exclude<ExtendedReferenceLineDecorationConfig['fill'], undefined>, YCoords, YCoords]>)(
'should be robust and works also for different axes when on same direction: 1x Left + 1x Right both %s', 'should be robust and works also for different axes when on same direction: 1x Left + 1x Right both %s',
(fill, coordsA, coordsB) => { (fill, coordsA, coordsB) => {
const wrapper = shallow( const wrapper = shallow(
@ -370,17 +393,17 @@ describe('ReferenceLines', () => {
layers={createLayers([ layers={createLayers([
{ {
forAccessor: `yAccessorLeftFirstId`, forAccessor: `yAccessorLeftFirstId`,
axisMode: 'left', position: 'left',
lineStyle: 'solid', lineStyle: 'solid',
fill, fill,
type: 'extendedYConfig', type: 'referenceLineDecorationConfig',
}, },
{ {
forAccessor: `yAccessorRightSecondId`, forAccessor: `yAccessorRightSecondId`,
axisMode: 'right', position: 'right',
lineStyle: 'solid', lineStyle: 'solid',
fill, fill,
type: 'extendedYConfig', type: 'referenceLineDecorationConfig',
}, },
])} ])}
/> />
@ -415,21 +438,42 @@ describe('ReferenceLines', () => {
}); });
describe('referenceLines', () => { describe('referenceLines', () => {
let formatters: Record<'left' | 'right' | 'bottom', FieldFormat | undefined>;
let defaultProps: Omit<ReferenceLinesProps, 'data' | 'layers'>; let defaultProps: Omit<ReferenceLinesProps, 'data' | 'layers'>;
beforeEach(() => { 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 = { defaultProps = {
formatters, xAxisFormatter: { convert: jest.fn((x) => x) } as unknown as FieldFormat,
isHorizontal: false, isHorizontal: false,
axesMap: { left: true, right: false }, axesConfiguration: [
{
groupId: 'left',
position: 'left',
series: [],
},
{
groupId: 'right',
position: 'right',
series: [],
},
{
groupId: 'bottom',
position: 'bottom',
series: [],
},
],
paddingMap: {}, paddingMap: {},
yAxesMap: {
left: {
groupId: 'left',
position: 'left',
series: [],
},
right: {
groupId: 'right',
position: 'right',
series: [],
},
},
}; };
}); });
@ -438,17 +482,17 @@ describe('ReferenceLines', () => {
['yAccessorLeft', 'below'], ['yAccessorLeft', 'below'],
['yAccessorRight', 'above'], ['yAccessorRight', 'above'],
['yAccessorRight', 'below'], ['yAccessorRight', 'below'],
] as Array<[string, Exclude<ExtendedYConfig['fill'], undefined>]>)( ] as Array<[string, Exclude<ExtendedReferenceLineDecorationConfig['fill'], undefined>]>)(
'should render a RectAnnotation for a reference line with fill set: %s %s', 'should render a RectAnnotation for a reference line with fill set: %s %s',
(layerPrefix, fill) => { (layerPrefix, fill) => {
const axisMode = getAxisFromId(layerPrefix); const position = getAxisFromId(layerPrefix);
const value = 5; const value = 5;
const wrapper = shallow( const wrapper = shallow(
<ReferenceLines <ReferenceLines
{...defaultProps} {...defaultProps}
layers={[ layers={[
createReferenceLine(layerPrefix, 1, { createReferenceLine(layerPrefix, 1, {
axisMode, position,
lineStyle: 'solid', lineStyle: 'solid',
fill, fill,
value, value,
@ -479,7 +523,7 @@ describe('ReferenceLines', () => {
it.each([ it.each([
['xAccessor', 'above'], ['xAccessor', 'above'],
['xAccessor', 'below'], ['xAccessor', 'below'],
] as Array<[string, Exclude<ExtendedYConfig['fill'], undefined>]>)( ] as Array<[string, Exclude<ExtendedReferenceLineDecorationConfig['fill'], undefined>]>)(
'should render a RectAnnotation for a reference line with fill set: %s %s', 'should render a RectAnnotation for a reference line with fill set: %s %s',
(layerPrefix, fill) => { (layerPrefix, fill) => {
const value = 1; const value = 1;
@ -488,7 +532,7 @@ describe('ReferenceLines', () => {
{...defaultProps} {...defaultProps}
layers={[ layers={[
createReferenceLine(layerPrefix, 1, { createReferenceLine(layerPrefix, 1, {
axisMode: 'bottom', position: 'bottom',
lineStyle: 'solid', lineStyle: 'solid',
fill, fill,
value, value,
@ -519,23 +563,23 @@ describe('ReferenceLines', () => {
it.each([ it.each([
['yAccessorLeft', 'above', { y0: 10, y1: undefined }, { y0: 10, y1: undefined }], ['yAccessorLeft', 'above', { y0: 10, y1: undefined }, { y0: 10, y1: undefined }],
['yAccessorLeft', 'below', { y0: undefined, y1: 5 }, { y0: undefined, y1: 5 }], ['yAccessorLeft', 'below', { y0: undefined, y1: 5 }, { y0: undefined, y1: 5 }],
] as Array<[string, Exclude<ExtendedYConfig['fill'], undefined>, YCoords, YCoords]>)( ] as Array<[string, Exclude<ExtendedReferenceLineDecorationConfig['fill'], undefined>, YCoords, YCoords]>)(
'should avoid overlap between two reference lines with fill in the same direction: 2 x %s %s', 'should avoid overlap between two reference lines with fill in the same direction: 2 x %s %s',
(layerPrefix, fill, coordsA, coordsB) => { (layerPrefix, fill, coordsA, coordsB) => {
const axisMode = getAxisFromId(layerPrefix); const position = getAxisFromId(layerPrefix);
const value = coordsA.y0 ?? coordsA.y1!; const value = coordsA.y0 ?? coordsA.y1!;
const wrapper = shallow( const wrapper = shallow(
<ReferenceLines <ReferenceLines
{...defaultProps} {...defaultProps}
layers={[ layers={[
createReferenceLine(layerPrefix, 10, { createReferenceLine(layerPrefix, 10, {
axisMode, position,
lineStyle: 'solid', lineStyle: 'solid',
fill, fill,
value, value,
}), }),
createReferenceLine(layerPrefix, 10, { createReferenceLine(layerPrefix, 10, {
axisMode, position,
lineStyle: 'solid', lineStyle: 'solid',
fill, fill,
value, value,
@ -570,7 +614,7 @@ describe('ReferenceLines', () => {
it.each([ it.each([
['xAccessor', 'above', { x0: 1, x1: undefined }, { x0: 1, x1: undefined }], ['xAccessor', 'above', { x0: 1, x1: undefined }, { x0: 1, x1: undefined }],
['xAccessor', 'below', { x0: undefined, x1: 1 }, { x0: undefined, x1: 1 }], ['xAccessor', 'below', { x0: undefined, x1: 1 }, { x0: undefined, x1: 1 }],
] as Array<[string, Exclude<ExtendedYConfig['fill'], undefined>, XCoords, XCoords]>)( ] as Array<[string, Exclude<ExtendedReferenceLineDecorationConfig['fill'], undefined>, XCoords, XCoords]>)(
'should avoid overlap between two reference lines with fill in the same direction: 2 x %s %s', 'should avoid overlap between two reference lines with fill in the same direction: 2 x %s %s',
(layerPrefix, fill, coordsA, coordsB) => { (layerPrefix, fill, coordsA, coordsB) => {
const value = coordsA.x0 ?? coordsA.x1!; const value = coordsA.x0 ?? coordsA.x1!;
@ -579,13 +623,13 @@ describe('ReferenceLines', () => {
{...defaultProps} {...defaultProps}
layers={[ layers={[
createReferenceLine(layerPrefix, 10, { createReferenceLine(layerPrefix, 10, {
axisMode: 'bottom', position: 'bottom',
lineStyle: 'solid', lineStyle: 'solid',
fill, fill,
value, value,
}), }),
createReferenceLine(layerPrefix, 10, { createReferenceLine(layerPrefix, 10, {
axisMode: 'bottom', position: 'bottom',
lineStyle: 'solid', lineStyle: 'solid',
fill, fill,
value, value,
@ -624,7 +668,7 @@ describe('ReferenceLines', () => {
it.each(['yAccessorLeft', 'yAccessorRight', 'xAccessor'])( it.each(['yAccessorLeft', 'yAccessorRight', 'xAccessor'])(
'should let areas in different directions overlap: %s', 'should let areas in different directions overlap: %s',
(layerPrefix) => { (layerPrefix) => {
const axisMode = getAxisFromId(layerPrefix); const position = getAxisFromId(layerPrefix);
const value1 = 1; const value1 = 1;
const value2 = 10; const value2 = 10;
const wrapper = shallow( const wrapper = shallow(
@ -632,13 +676,13 @@ describe('ReferenceLines', () => {
{...defaultProps} {...defaultProps}
layers={[ layers={[
createReferenceLine(layerPrefix, 10, { createReferenceLine(layerPrefix, 10, {
axisMode, position,
lineStyle: 'solid', lineStyle: 'solid',
fill: 'above', fill: 'above',
value: value1, value: value1,
}), }),
createReferenceLine(layerPrefix, 10, { createReferenceLine(layerPrefix, 10, {
axisMode, position,
lineStyle: 'solid', lineStyle: 'solid',
fill: 'below', fill: 'below',
value: value2, value: value2,
@ -654,7 +698,7 @@ describe('ReferenceLines', () => {
{ {
coordinates: { coordinates: {
...emptyCoords, ...emptyCoords,
...(axisMode === 'bottom' ? { x0: value1 } : { y0: value1 }), ...(position === 'bottom' ? { x0: value1 } : { y0: value1 }),
}, },
details: value1, details: value1,
header: undefined, header: undefined,
@ -670,7 +714,7 @@ describe('ReferenceLines', () => {
{ {
coordinates: { coordinates: {
...emptyCoords, ...emptyCoords,
...(axisMode === 'bottom' ? { x1: value2 } : { y1: value2 }), ...(position === 'bottom' ? { x1: value2 } : { y1: value2 }),
}, },
details: value2, details: value2,
header: undefined, header: undefined,

View file

@ -12,18 +12,24 @@ import React from 'react';
import { Position } from '@elastic/charts'; import { Position } from '@elastic/charts';
import type { FieldFormat } from '@kbn/field-formats-plugin/common'; import type { FieldFormat } from '@kbn/field-formats-plugin/common';
import type { CommonXYReferenceLineLayerConfig, ReferenceLineConfig } from '../../../common/types'; import type { CommonXYReferenceLineLayerConfig, ReferenceLineConfig } from '../../../common/types';
import { isReferenceLine, LayersAccessorsTitles } from '../../helpers'; import {
AxesMap,
GroupsConfiguration,
isReferenceLine,
LayersAccessorsTitles,
} from '../../helpers';
import { ReferenceLineLayer } from './reference_line_layer'; import { ReferenceLineLayer } from './reference_line_layer';
import { ReferenceLine } from './reference_line'; import { ReferenceLine } from './reference_line';
import { getNextValuesForReferenceLines } from './utils'; import { getNextValuesForReferenceLines } from './utils';
export interface ReferenceLinesProps { export interface ReferenceLinesProps {
layers: CommonXYReferenceLineLayerConfig[]; layers: CommonXYReferenceLineLayerConfig[];
formatters: Record<'left' | 'right' | 'bottom', FieldFormat | undefined>; xAxisFormatter: FieldFormat;
axesMap: Record<'left' | 'right', boolean>; axesConfiguration: GroupsConfiguration;
isHorizontal: boolean; isHorizontal: boolean;
paddingMap: Partial<Record<Position, number>>; paddingMap: Partial<Record<Position, number>>;
titles?: LayersAccessorsTitles; titles?: LayersAccessorsTitles;
yAxesMap: AxesMap;
} }
export const ReferenceLines = ({ layers, titles = {}, ...rest }: ReferenceLinesProps) => { export const ReferenceLines = ({ layers, titles = {}, ...rest }: ReferenceLinesProps) => {
@ -36,13 +42,13 @@ export const ReferenceLines = ({ layers, titles = {}, ...rest }: ReferenceLinesP
return ( return (
<> <>
{layers.flatMap((layer) => { {layers.flatMap((layer) => {
if (!layer.yConfig) { if (!layer.decorations) {
return null; return null;
} }
const key = `referenceLine-${layer.layerId}`; const key = `referenceLine-${layer.layerId}`;
if (isReferenceLine(layer)) { if (isReferenceLine(layer)) {
const nextValue = referenceLinesNextValues[layer.yConfig[0].fill][layer.layerId]; const nextValue = referenceLinesNextValues[layer.decorations[0].fill][layer.layerId];
return <ReferenceLine key={key} layer={layer} {...rest} nextValue={nextValue} />; return <ReferenceLine key={key} layer={layer} {...rest} nextValue={nextValue} />;
} }

View file

@ -11,13 +11,23 @@ import { Position } from '@elastic/charts';
import { euiLightVars } from '@kbn/ui-theme'; import { euiLightVars } from '@kbn/ui-theme';
import { FieldFormat } from '@kbn/field-formats-plugin/common'; import { FieldFormat } from '@kbn/field-formats-plugin/common';
import { groupBy, orderBy } from 'lodash'; import { groupBy, orderBy } from 'lodash';
import { IconPosition, ReferenceLineConfig, YAxisMode, FillStyle } from '../../../common/types'; import {
IconPosition,
ReferenceLineConfig,
FillStyle,
ExtendedReferenceLineDecorationConfig,
ReferenceLineDecorationConfig,
} from '../../../common/types';
import { FillStyles } from '../../../common/constants'; import { FillStyles } from '../../../common/constants';
import { import {
GroupsConfiguration,
LINES_MARKER_SIZE, LINES_MARKER_SIZE,
mapVerticalToHorizontalPlacement, mapVerticalToHorizontalPlacement,
Marker, Marker,
MarkerBody, MarkerBody,
getAxisPosition,
getOriginalAxisPosition,
AxesMap,
} from '../../helpers'; } from '../../helpers';
import { ReferenceLineAnnotationConfig } from './reference_line_annotations'; import { ReferenceLineAnnotationConfig } from './reference_line_annotations';
@ -26,17 +36,18 @@ import { ReferenceLineAnnotationConfig } from './reference_line_annotations';
// this function assume the chart is vertical // this function assume the chart is vertical
export function getBaseIconPlacement( export function getBaseIconPlacement(
iconPosition: IconPosition | undefined, iconPosition: IconPosition | undefined,
axesMap?: Record<string, unknown>, axesMap?: AxesMap,
axisMode?: YAxisMode position?: Position
) { ) {
if (iconPosition === 'auto') { if (iconPosition === 'auto') {
if (axisMode === 'bottom') { if (position === Position.Bottom) {
return Position.Top; return Position.Top;
} }
if (axesMap) { if (axesMap) {
if (axisMode === 'left') { if (position === Position.Left) {
return axesMap.right ? Position.Left : Position.Right; return axesMap.right ? Position.Left : Position.Right;
} }
return axesMap.left ? Position.Right : Position.Left; return axesMap.left ? Position.Right : Position.Left;
} }
} }
@ -67,22 +78,26 @@ export const getSharedStyle = (config: ReferenceLineAnnotationConfig) => ({
export const getLineAnnotationProps = ( export const getLineAnnotationProps = (
config: ReferenceLineAnnotationConfig, config: ReferenceLineAnnotationConfig,
labels: { markerLabel?: string; markerBodyLabel?: string }, labels: { markerLabel?: string; markerBodyLabel?: string },
axesMap: Record<'left' | 'right', boolean>, axesMap: AxesMap,
paddingMap: Partial<Record<Position, number>>, paddingMap: Partial<Record<Position, number>>,
groupId: 'left' | 'right' | undefined,
isHorizontal: boolean isHorizontal: boolean
) => { ) => {
// get the position for vertical chart // get the position for vertical chart
const markerPositionVertical = getBaseIconPlacement( const markerPositionVertical = getBaseIconPlacement(
config.iconPosition, config.iconPosition,
axesMap, axesMap,
config.axisMode getOriginalAxisPosition(config.axisGroup?.position ?? Position.Bottom, isHorizontal)
); );
// the padding map is built for vertical chart // the padding map is built for vertical chart
const hasReducedPadding = paddingMap[markerPositionVertical] === LINES_MARKER_SIZE; const hasReducedPadding = paddingMap[markerPositionVertical] === LINES_MARKER_SIZE;
const markerPosition = isHorizontal
? mapVerticalToHorizontalPlacement(markerPositionVertical)
: markerPositionVertical;
return { return {
groupId, groupId: config.axisGroup?.groupId || 'bottom',
marker: ( marker: (
<Marker <Marker
config={config} config={config}
@ -94,22 +109,14 @@ export const getLineAnnotationProps = (
markerBody: ( markerBody: (
<MarkerBody <MarkerBody
label={labels.markerBodyLabel} label={labels.markerBodyLabel}
isHorizontal={ isHorizontal={markerPosition === Position.Bottom || markerPosition === Position.Top}
(!isHorizontal && config.axisMode === 'bottom') ||
(isHorizontal && config.axisMode !== 'bottom')
}
/> />
), ),
// rotate the position if required // rotate the position if required
markerPosition: isHorizontal markerPosition,
? mapVerticalToHorizontalPlacement(markerPositionVertical)
: markerPositionVertical,
}; };
}; };
export const getGroupId = (axisMode: YAxisMode | undefined) =>
axisMode === 'bottom' ? undefined : axisMode === 'right' ? 'right' : 'left';
export const getBottomRect = ( export const getBottomRect = (
headerLabel: string | undefined, headerLabel: string | undefined,
isFillAbove: boolean, isFillAbove: boolean,
@ -147,13 +154,16 @@ export const getHorizontalRect = (
const sortReferenceLinesByGroup = (referenceLines: ReferenceLineConfig[], group: FillStyle) => { const sortReferenceLinesByGroup = (referenceLines: ReferenceLineConfig[], group: FillStyle) => {
if (group === FillStyles.ABOVE || group === FillStyles.BELOW) { if (group === FillStyles.ABOVE || group === FillStyles.BELOW) {
const order = group === FillStyles.ABOVE ? 'asc' : 'desc'; const order = group === FillStyles.ABOVE ? 'asc' : 'desc';
return orderBy(referenceLines, ({ yConfig: [{ value }] }) => value, [order]); return orderBy(referenceLines, ({ decorations: [{ value }] }) => value, [order]);
} }
return referenceLines; return referenceLines;
}; };
export const getNextValuesForReferenceLines = (referenceLines: ReferenceLineConfig[]) => { 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[]; const groups = Object.keys(grouppedReferenceLines) as FillStyle[];
return groups.reduce<Record<FillStyle, Record<string, number | undefined>>>( return groups.reduce<Record<FillStyle, Record<string, number | undefined>>>(
@ -164,8 +174,8 @@ export const getNextValuesForReferenceLines = (referenceLines: ReferenceLineConf
(nextValues, referenceLine, index, lines) => { (nextValues, referenceLine, index, lines) => {
let nextValue: number | undefined; let nextValue: number | undefined;
if (index < lines.length - 1) { if (index < lines.length - 1) {
const [yConfig] = lines[index + 1].yConfig; const [decorationConfig] = lines[index + 1].decorations;
nextValue = yConfig.value; nextValue = decorationConfig.value;
} }
return { ...nextValues, [referenceLine.layerId]: nextValue }; return { ...nextValues, [referenceLine.layerId]: nextValue };
@ -183,7 +193,7 @@ export const computeChartMargins = (
referenceLinePaddings: Partial<Record<Position, number>>, referenceLinePaddings: Partial<Record<Position, number>>,
labelVisibility: Partial<Record<'x' | 'yLeft' | 'yRight', boolean>>, labelVisibility: Partial<Record<'x' | 'yLeft' | 'yRight', boolean>>,
titleVisibility: Partial<Record<'x' | 'yLeft' | 'yRight', boolean>>, titleVisibility: Partial<Record<'x' | 'yLeft' | 'yRight', boolean>>,
axesMap: Record<'left' | 'right', unknown>, axesMap: AxesMap,
isHorizontal: boolean isHorizontal: boolean
) => { ) => {
const result: Partial<Record<Position, number>> = {}; const result: Partial<Record<Position, number>> = {};
@ -210,5 +220,18 @@ export const computeChartMargins = (
const placement = isHorizontal ? mapVerticalToHorizontalPlacement('top') : 'top'; const placement = isHorizontal ? mapVerticalToHorizontalPlacement('top') : 'top';
result[placement] = referenceLinePaddings.top; result[placement] = referenceLinePaddings.top;
} }
return result; return result;
}; };
export function getAxisGroupForReferenceLine(
axesConfiguration: GroupsConfiguration,
decorationConfig: ReferenceLineDecorationConfig | ExtendedReferenceLineDecorationConfig,
shouldRotate: boolean
) {
return axesConfiguration.find(
(axis) =>
(decorationConfig.axisId && axis.groupId.includes(decorationConfig.axisId)) ||
getAxisPosition(decorationConfig.position ?? Position.Left, shouldRotate) === axis.position
);
}

View file

@ -155,6 +155,9 @@ describe('XYChart component', () => {
type: 'dataLayer', type: 'dataLayer',
layerType: LayerTypes.DATA, layerType: LayerTypes.DATA,
seriesType: 'line', seriesType: 'line',
isPercentage: false,
isHorizontal: false,
isStacked: false,
xAccessor: 'c', xAccessor: 'c',
accessors: ['a', 'b'], accessors: ['a', 'b'],
showLines: true, showLines: true,
@ -255,6 +258,9 @@ describe('XYChart component', () => {
layerType: LayerTypes.DATA, layerType: LayerTypes.DATA,
showLines: true, showLines: true,
seriesType: 'line', seriesType: 'line',
isHorizontal: false,
isStacked: false,
isPercentage: false,
xAccessor: 'c', xAccessor: 'c',
accessors: ['a', 'b'], accessors: ['a', 'b'],
splitAccessor: 'd', splitAccessor: 'd',
@ -339,7 +345,8 @@ describe('XYChart component', () => {
test('it should enable the new time axis for a stacked vertical bar with break down dimension', () => { test('it should enable the new time axis for a stacked vertical bar with break down dimension', () => {
const timeLayer: DataLayerConfig = { const timeLayer: DataLayerConfig = {
...defaultTimeLayer, ...defaultTimeLayer,
seriesType: 'bar_stacked', seriesType: 'bar',
isStacked: true,
}; };
const timeLayerArgs = createArgsWithLayers([timeLayer]); const timeLayerArgs = createArgsWithLayers([timeLayer]);
@ -514,11 +521,14 @@ describe('XYChart component', () => {
isHistogram: true, isHistogram: true,
}, },
], ],
xExtent: { xAxisConfig: {
type: 'axisExtentConfig', type: 'xAxisConfig',
mode: 'custom', extent: {
lowerBound: 123, type: 'axisExtentConfig',
upperBound: 456, mode: 'custom',
lowerBound: 123,
upperBound: 456,
},
}, },
}} }}
/> />
@ -541,12 +551,18 @@ describe('XYChart component', () => {
{...defaultProps} {...defaultProps}
args={{ args={{
...args, ...args,
yLeftExtent: { yAxisConfigs: [
type: 'axisExtentConfig', {
mode: 'custom', type: 'yAxisConfig',
lowerBound: 123, position: 'left',
upperBound: 456, extent: {
}, type: 'axisExtentConfig',
mode: 'custom',
lowerBound: 123,
upperBound: 456,
},
},
],
}} }}
/> />
); );
@ -564,10 +580,16 @@ describe('XYChart component', () => {
{...defaultProps} {...defaultProps}
args={{ args={{
...args, ...args,
yLeftExtent: { yAxisConfigs: [
type: 'axisExtentConfig', {
mode: 'dataBounds', type: 'yAxisConfig',
}, position: 'left',
extent: {
type: 'axisExtentConfig',
mode: 'dataBounds',
},
},
],
}} }}
/> />
); );
@ -585,10 +607,16 @@ describe('XYChart component', () => {
{...defaultProps} {...defaultProps}
args={{ args={{
...args, ...args,
yLeftExtent: { yAxisConfigs: [
type: 'axisExtentConfig', {
mode: 'dataBounds', type: 'yAxisConfig',
}, position: 'left',
extent: {
type: 'axisExtentConfig',
mode: 'dataBounds',
},
},
],
layers: [ layers: [
{ {
...(args.layers[0] as DataLayerConfig), ...(args.layers[0] as DataLayerConfig),
@ -612,16 +640,16 @@ describe('XYChart component', () => {
{...defaultProps} {...defaultProps}
args={{ args={{
...args, ...args,
yLeftExtent: { yAxisConfigs: [
type: 'axisExtentConfig',
mode: 'custom',
lowerBound: 123,
upperBound: 456,
},
layers: [
{ {
...(args.layers[0] as DataLayerConfig), type: 'yAxisConfig',
seriesType: 'bar', position: 'left',
extent: {
type: 'axisExtentConfig',
mode: 'custom',
lowerBound: 123,
upperBound: 456,
},
}, },
], ],
}} }}
@ -629,8 +657,8 @@ describe('XYChart component', () => {
); );
expect(component.find(Axis).find('[id="left"]').prop('domain')).toEqual({ expect(component.find(Axis).find('[id="left"]').prop('domain')).toEqual({
fit: false, fit: false,
min: NaN, min: 123,
max: NaN, max: 456,
includeDataFromIds: [], includeDataFromIds: [],
}); });
}); });
@ -901,7 +929,9 @@ describe('XYChart component', () => {
{...defaultProps} {...defaultProps}
args={{ args={{
...args, ...args,
layers: [{ ...(args.layers[0] as DataLayerConfig), seriesType: 'bar_horizontal' }], layers: [
{ ...(args.layers[0] as DataLayerConfig), isHorizontal: true, seriesType: 'bar' },
],
}} }}
/> />
); );
@ -1020,7 +1050,10 @@ describe('XYChart component', () => {
xAccessor: 'xAccessorId', xAccessor: 'xAccessorId',
xScaleType: 'linear', xScaleType: 'linear',
isHistogram: true, isHistogram: true,
seriesType: 'bar_stacked', isHorizontal: false,
isStacked: true,
seriesType: 'bar',
isPercentage: false,
accessors: ['yAccessorId'], accessors: ['yAccessorId'],
palette: mockPaletteOutput, palette: mockPaletteOutput,
table: numberHistogramData, table: numberHistogramData,
@ -1091,8 +1124,11 @@ describe('XYChart component', () => {
type: 'dataLayer', type: 'dataLayer',
layerType: LayerTypes.DATA, layerType: LayerTypes.DATA,
isHistogram: true, isHistogram: true,
seriesType: 'bar',
isStacked: true,
isHorizontal: false,
isPercentage: false,
showLines: true, showLines: true,
seriesType: 'bar_stacked',
xAccessor: 'b', xAccessor: 'b',
xScaleType: 'time', xScaleType: 'time',
splitAccessor: 'b', splitAccessor: 'b',
@ -1218,7 +1254,10 @@ describe('XYChart component', () => {
xAccessor: 'xAccessorId', xAccessor: 'xAccessorId',
xScaleType: 'linear', xScaleType: 'linear',
isHistogram: true, isHistogram: true,
seriesType: 'bar_stacked', isPercentage: false,
seriesType: 'bar',
isStacked: true,
isHorizontal: false,
accessors: ['yAccessorId'], accessors: ['yAccessorId'],
palette: mockPaletteOutput, palette: mockPaletteOutput,
table: numberHistogramData, table: numberHistogramData,
@ -1291,6 +1330,9 @@ describe('XYChart component', () => {
type: 'dataLayer', type: 'dataLayer',
layerType: LayerTypes.DATA, layerType: LayerTypes.DATA,
seriesType: 'line', seriesType: 'line',
isHorizontal: false,
isStacked: false,
isPercentage: false,
showLines: true, showLines: true,
xAccessor: 'd', xAccessor: 'd',
accessors: ['a', 'b'], accessors: ['a', 'b'],
@ -1351,6 +1393,9 @@ describe('XYChart component', () => {
layerType: LayerTypes.DATA, layerType: LayerTypes.DATA,
showLines: true, showLines: true,
seriesType: 'line', seriesType: 'line',
isHorizontal: false,
isStacked: false,
isPercentage: false,
xAccessor: 'd', xAccessor: 'd',
accessors: ['a', 'b'], accessors: ['a', 'b'],
columnToLabel: '{"a": "Label A", "b": "Label B", "d": "Label D"}', columnToLabel: '{"a": "Label A", "b": "Label B", "d": "Label D"}',
@ -1382,6 +1427,9 @@ describe('XYChart component', () => {
layerType: LayerTypes.DATA, layerType: LayerTypes.DATA,
showLines: true, showLines: true,
seriesType: 'line', seriesType: 'line',
isHorizontal: false,
isStacked: false,
isPercentage: false,
xAccessor: 'd', xAccessor: 'd',
accessors: ['a', 'b'], accessors: ['a', 'b'],
columnToLabel: '{"a": "Label A", "b": "Label B", "d": "Label D"}', columnToLabel: '{"a": "Label A", "b": "Label B", "d": "Label D"}',
@ -1421,7 +1469,7 @@ describe('XYChart component', () => {
{...defaultProps} {...defaultProps}
args={{ args={{
...args, ...args,
layers: [{ ...(args.layers[0] as DataLayerConfig), seriesType: 'bar_stacked' }], layers: [{ ...(args.layers[0] as DataLayerConfig), seriesType: 'bar', isStacked: true }],
}} }}
/> />
); );
@ -1440,7 +1488,7 @@ describe('XYChart component', () => {
{...defaultProps} {...defaultProps}
args={{ args={{
...args, ...args,
layers: [{ ...(args.layers[0] as DataLayerConfig), seriesType: 'area_stacked' }], layers: [{ ...(args.layers[0] as DataLayerConfig), seriesType: 'area', isStacked: true }],
}} }}
/> />
); );
@ -1460,7 +1508,12 @@ describe('XYChart component', () => {
args={{ args={{
...args, ...args,
layers: [ layers: [
{ ...(args.layers[0] as DataLayerConfig), seriesType: 'bar_horizontal_stacked' }, {
...(args.layers[0] as DataLayerConfig),
seriesType: 'bar',
isStacked: true,
isHorizontal: true,
},
], ],
}} }}
/> />
@ -1487,7 +1540,8 @@ describe('XYChart component', () => {
...(args.layers[0] as DataLayerConfig), ...(args.layers[0] as DataLayerConfig),
xAccessor: undefined, xAccessor: undefined,
splitAccessor: 'e', splitAccessor: 'e',
seriesType: 'bar_stacked', seriesType: 'bar',
isStacked: true,
}, },
], ],
}} }}
@ -1574,7 +1628,8 @@ describe('XYChart component', () => {
layers: [ layers: [
{ {
...(args.layers[0] as DataLayerConfig), ...(args.layers[0] as DataLayerConfig),
seriesType: 'bar_stacked', seriesType: 'bar',
isStacked: true,
isHistogram: true, isHistogram: true,
}, },
], ],
@ -1630,21 +1685,33 @@ describe('XYChart component', () => {
{ {
...layer, ...layer,
accessors: ['a', 'b'], accessors: ['a', 'b'],
yConfig: [ decorations: [
{ {
type: 'yConfig', type: 'dataDecorationConfig',
forAccessor: 'a', forAccessor: 'a',
axisMode: 'left', axisId: '1',
}, },
{ {
type: 'yConfig', type: 'dataDecorationConfig',
forAccessor: 'b', forAccessor: 'b',
axisMode: 'right', axisId: '2',
}, },
], ],
table: dataWithoutFormats, table: dataWithoutFormats,
}, },
], ],
yAxisConfigs: [
{
type: 'yAxisConfig',
id: '1',
position: 'left',
},
{
type: 'yAxisConfig',
id: '2',
position: 'right',
},
],
}; };
const component = getRenderedComponent(newArgs); const component = getRenderedComponent(newArgs);
@ -1684,21 +1751,28 @@ describe('XYChart component', () => {
{ {
...layer, ...layer,
accessors: ['c', 'd'], accessors: ['c', 'd'],
yConfig: [ decorations: [
{ {
type: 'yConfig', type: 'dataDecorationConfig',
forAccessor: 'c', forAccessor: 'c',
axisMode: 'left', axisId: '1',
}, },
{ {
type: 'yConfig', type: 'dataDecorationConfig',
forAccessor: 'd', forAccessor: 'd',
axisMode: 'left', axisId: '1',
}, },
], ],
table: dataWithoutFormats, table: dataWithoutFormats,
}, },
], ],
yAxisConfigs: [
{
type: 'yAxisConfig',
id: '1',
position: 'left',
},
],
}; };
const component = getRenderedComponent(newArgs); const component = getRenderedComponent(newArgs);
@ -1720,14 +1794,14 @@ describe('XYChart component', () => {
type: 'extendedDataLayer', type: 'extendedDataLayer',
accessors: ['a', 'b'], accessors: ['a', 'b'],
splitAccessor: undefined, splitAccessor: undefined,
yConfig: [ decorations: [
{ {
type: 'yConfig', type: 'dataDecorationConfig',
forAccessor: 'a', forAccessor: 'a',
color: '#550000', color: '#550000',
}, },
{ {
type: 'yConfig', type: 'dataDecorationConfig',
forAccessor: 'b', forAccessor: 'b',
color: '#FFFF00', color: '#FFFF00',
}, },
@ -1739,9 +1813,9 @@ describe('XYChart component', () => {
type: 'extendedDataLayer', type: 'extendedDataLayer',
accessors: ['c'], accessors: ['c'],
splitAccessor: undefined, splitAccessor: undefined,
yConfig: [ decorations: [
{ {
type: 'yConfig', type: 'dataDecorationConfig',
forAccessor: 'c', forAccessor: 'c',
color: '#FEECDF', color: '#FEECDF',
}, },
@ -1772,16 +1846,16 @@ describe('XYChart component', () => {
}) })
).toEqual('#FEECDF'); ).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 = { const newArgs: XYProps = {
...args, ...args,
layers: [ layers: [
{ {
...layer, ...layer,
accessors: ['a'], accessors: ['a'],
yConfig: [ decorations: [
{ {
type: 'yConfig', type: 'dataDecorationConfig',
forAccessor: 'a', forAccessor: 'a',
color: '#550000', color: '#550000',
}, },
@ -2067,7 +2141,14 @@ describe('XYChart component', () => {
{...defaultProps} {...defaultProps}
args={{ args={{
...args, ...args,
yLeftScale: 'sqrt', yAxisConfigs: [
{
type: 'yAxisConfig',
position: 'left',
showLabels: true,
scaleType: 'sqrt',
},
],
}} }}
/> />
); );
@ -2115,11 +2196,24 @@ describe('XYChart component', () => {
test('it should set the tickLabel visibility on the x axis if the tick labels is hidden', () => { test('it should set the tickLabel visibility on the x axis if the tick labels is hidden', () => {
const { args } = sampleArgs(); const { args } = sampleArgs();
args.tickLabelsVisibilitySettings = { args.yAxisConfigs = [
x: false, {
yLeft: true, type: 'yAxisConfig',
yRight: true, position: 'left',
type: 'tickLabelsConfig', showLabels: true,
},
{
type: 'yAxisConfig',
position: 'right',
showLabels: true,
},
];
args.xAxisConfig = {
type: 'xAxisConfig',
id: 'x',
showLabels: false,
position: 'bottom',
}; };
const instance = shallow(<XYChart {...defaultProps} args={{ ...args }} />); const instance = shallow(<XYChart {...defaultProps} args={{ ...args }} />);
@ -2136,12 +2230,18 @@ describe('XYChart component', () => {
test('it should set the tickLabel visibility on the y axis if the tick labels is hidden', () => { test('it should set the tickLabel visibility on the y axis if the tick labels is hidden', () => {
const { args } = sampleArgs(); const { args } = sampleArgs();
args.tickLabelsVisibilitySettings = { args.yAxisConfigs = [
x: true, {
yLeft: false, type: 'yAxisConfig',
yRight: false, position: 'left',
type: 'tickLabelsConfig', showLabels: false,
}; },
{
type: 'yAxisConfig',
position: 'right',
showLabels: false,
},
];
const instance = shallow(<XYChart {...defaultProps} args={{ ...args }} />); const instance = shallow(<XYChart {...defaultProps} args={{ ...args }} />);
@ -2157,11 +2257,24 @@ describe('XYChart component', () => {
test('it should set the tickLabel visibility on the x axis if the tick labels is shown', () => { test('it should set the tickLabel visibility on the x axis if the tick labels is shown', () => {
const { args } = sampleArgs(); const { args } = sampleArgs();
args.tickLabelsVisibilitySettings = { args.yAxisConfigs = [
x: true, {
yLeft: true, type: 'yAxisConfig',
yRight: true, position: 'left',
type: 'tickLabelsConfig', showLabels: true,
},
{
type: 'yAxisConfig',
position: 'right',
showLabels: true,
},
];
args.xAxisConfig = {
type: 'xAxisConfig',
id: 'x',
showLabels: true,
position: 'bottom',
}; };
const instance = shallow(<XYChart {...defaultProps} args={{ ...args }} />); const instance = shallow(<XYChart {...defaultProps} args={{ ...args }} />);
@ -2178,11 +2291,25 @@ describe('XYChart component', () => {
test('it should set the tickLabel orientation on the x axis', () => { test('it should set the tickLabel orientation on the x axis', () => {
const { args } = sampleArgs(); const { args } = sampleArgs();
args.labelsOrientation = { args.yAxisConfigs = [
x: -45, {
yLeft: 0, type: 'yAxisConfig',
yRight: -90, position: 'left',
type: 'labelsOrientationConfig', labelsOrientation: 0,
},
{
type: 'yAxisConfig',
position: 'right',
labelsOrientation: -90,
},
];
args.xAxisConfig = {
type: 'xAxisConfig',
id: 'x',
showLabels: true,
labelsOrientation: -45,
position: 'bottom',
}; };
const instance = shallow(<XYChart {...defaultProps} args={{ ...args }} />); const instance = shallow(<XYChart {...defaultProps} args={{ ...args }} />);
@ -2199,11 +2326,24 @@ describe('XYChart component', () => {
test('it should set the tickLabel visibility on the y axis if the tick labels is shown', () => { test('it should set the tickLabel visibility on the y axis if the tick labels is shown', () => {
const { args } = sampleArgs(); const { args } = sampleArgs();
args.tickLabelsVisibilitySettings = { args.yAxisConfigs = [
x: false, {
yLeft: true, type: 'yAxisConfig',
yRight: true, position: 'left',
type: 'tickLabelsConfig', showLabels: true,
},
{
type: 'yAxisConfig',
position: 'right',
showLabels: true,
},
];
args.xAxisConfig = {
type: 'xAxisConfig',
id: 'x',
showLabels: true,
position: 'bottom',
}; };
const instance = shallow(<XYChart {...defaultProps} args={{ ...args }} />); const instance = shallow(<XYChart {...defaultProps} args={{ ...args }} />);
@ -2220,11 +2360,25 @@ describe('XYChart component', () => {
test('it should set the tickLabel orientation on the y axis', () => { test('it should set the tickLabel orientation on the y axis', () => {
const { args } = sampleArgs(); const { args } = sampleArgs();
args.labelsOrientation = { args.yAxisConfigs = [
x: -45, {
yLeft: -90, type: 'yAxisConfig',
yRight: -90, position: 'left',
type: 'labelsOrientationConfig', labelsOrientation: -90,
},
{
type: 'yAxisConfig',
position: 'right',
labelsOrientation: -90,
},
];
args.xAxisConfig = {
type: 'xAxisConfig',
id: 'x',
showLabels: true,
labelsOrientation: -45,
position: 'bottom',
}; };
const instance = shallow(<XYChart {...defaultProps} args={{ ...args }} />); const instance = shallow(<XYChart {...defaultProps} args={{ ...args }} />);
@ -2266,43 +2420,47 @@ describe('XYChart component', () => {
}; };
const args: XYProps = { const args: XYProps = {
xTitle: '',
yTitle: '',
yRightTitle: '',
yLeftScale: 'linear',
yRightScale: 'linear',
showTooltip: true, showTooltip: true,
legend: { type: 'legendConfig', isVisible: false, position: Position.Top }, legend: { type: 'legendConfig', isVisible: false, position: Position.Top },
valueLabels: 'hide', valueLabels: 'hide',
tickLabelsVisibilitySettings: { yAxisConfigs: [
type: 'tickLabelsConfig', {
x: true, type: 'yAxisConfig',
yLeft: true, position: 'left',
yRight: true, labelsOrientation: 0,
}, showGridLines: false,
gridlinesVisibilitySettings: { showLabels: true,
type: 'gridlinesConfig', title: '',
x: true, extent: {
yLeft: false, mode: 'full',
yRight: false, type: 'axisExtentConfig',
}, },
labelsOrientation: { },
type: 'labelsOrientationConfig', {
x: 0, type: 'yAxisConfig',
yLeft: 0, position: 'right',
yRight: 0, labelsOrientation: 0,
}, showGridLines: false,
xExtent: { showLabels: true,
mode: 'dataBounds', title: '',
type: 'axisExtentConfig', extent: {
}, mode: 'full',
yLeftExtent: { type: 'axisExtentConfig',
mode: 'full', },
type: 'axisExtentConfig', },
}, ],
yRightExtent: { xAxisConfig: {
mode: 'full', type: 'xAxisConfig',
type: 'axisExtentConfig', id: 'x',
title: '',
showLabels: true,
showGridLines: true,
labelsOrientation: 0,
position: 'bottom',
extent: {
mode: 'dataBounds',
type: 'axisExtentConfig',
},
}, },
markSizeRatio: 1, markSizeRatio: 1,
layers: [ layers: [
@ -2311,6 +2469,8 @@ describe('XYChart component', () => {
type: 'dataLayer', type: 'dataLayer',
layerType: LayerTypes.DATA, layerType: LayerTypes.DATA,
seriesType: 'line', seriesType: 'line',
isStacked: false,
isHorizontal: false,
showLines: true, showLines: true,
xAccessor: 'a', xAccessor: 'a',
accessors: ['c'], accessors: ['c'],
@ -2318,6 +2478,7 @@ describe('XYChart component', () => {
columnToLabel: '', columnToLabel: '',
xScaleType: 'ordinal', xScaleType: 'ordinal',
isHistogram: false, isHistogram: false,
isPercentage: false,
palette: mockPaletteOutput, palette: mockPaletteOutput,
table: data1, table: data1,
}, },
@ -2333,6 +2494,9 @@ describe('XYChart component', () => {
columnToLabel: '', columnToLabel: '',
xScaleType: 'ordinal', xScaleType: 'ordinal',
isHistogram: false, isHistogram: false,
isStacked: false,
isHorizontal: false,
isPercentage: false,
palette: mockPaletteOutput, palette: mockPaletteOutput,
table: data2, table: data2,
}, },
@ -2363,45 +2527,51 @@ describe('XYChart component', () => {
}; };
const args: XYProps = { const args: XYProps = {
xTitle: '',
yTitle: '',
yRightTitle: '',
legend: { type: 'legendConfig', isVisible: false, position: Position.Top }, legend: { type: 'legendConfig', isVisible: false, position: Position.Top },
valueLabels: 'hide', valueLabels: 'hide',
yAxisConfigs: [
{
type: 'yAxisConfig',
position: 'left',
labelsOrientation: 0,
showGridLines: false,
showLabels: false,
title: '',
scaleType: 'linear',
extent: {
mode: 'full',
type: 'axisExtentConfig',
},
},
{
type: 'yAxisConfig',
position: 'right',
labelsOrientation: 0,
showGridLines: false,
showLabels: false,
scaleType: 'linear',
title: '',
extent: {
mode: 'full',
type: 'axisExtentConfig',
},
},
],
xAxisConfig: {
type: 'xAxisConfig',
id: 'x',
title: '',
showLabels: true,
showGridLines: true,
labelsOrientation: 0,
position: 'bottom',
extent: {
mode: 'dataBounds',
type: 'axisExtentConfig',
},
},
showTooltip: true, showTooltip: true,
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,
},
xExtent: {
mode: 'dataBounds',
type: 'axisExtentConfig',
},
yLeftExtent: {
mode: 'full',
type: 'axisExtentConfig',
},
yRightExtent: {
mode: 'full',
type: 'axisExtentConfig',
},
markSizeRatio: 1, markSizeRatio: 1,
yLeftScale: 'linear',
yRightScale: 'linear',
layers: [ layers: [
{ {
layerId: 'first', layerId: 'first',
@ -2415,6 +2585,9 @@ describe('XYChart component', () => {
columnToLabel: '', columnToLabel: '',
xScaleType: 'ordinal', xScaleType: 'ordinal',
isHistogram: false, isHistogram: false,
isStacked: false,
isHorizontal: false,
isPercentage: false,
palette: mockPaletteOutput, palette: mockPaletteOutput,
table: data, table: data,
}, },
@ -2443,45 +2616,51 @@ describe('XYChart component', () => {
}; };
const args: XYProps = { const args: XYProps = {
xTitle: '',
yTitle: '',
yRightTitle: '',
showTooltip: true, showTooltip: true,
legend: { type: 'legendConfig', isVisible: true, position: Position.Top }, legend: { type: 'legendConfig', isVisible: true, position: Position.Top },
valueLabels: 'hide', valueLabels: 'hide',
tickLabelsVisibilitySettings: { yAxisConfigs: [
type: 'tickLabelsConfig', {
x: true, type: 'yAxisConfig',
yLeft: false, position: 'left',
yRight: false, labelsOrientation: 0,
}, showGridLines: false,
gridlinesVisibilitySettings: { showLabels: false,
type: 'gridlinesConfig', title: '',
x: true, scaleType: 'linear',
yLeft: false, extent: {
yRight: false, mode: 'full',
}, type: 'axisExtentConfig',
labelsOrientation: { },
type: 'labelsOrientationConfig', },
x: 0, {
yLeft: 0, type: 'yAxisConfig',
yRight: 0, position: 'right',
}, labelsOrientation: 0,
xExtent: { showGridLines: false,
mode: 'dataBounds', showLabels: false,
type: 'axisExtentConfig', scaleType: 'linear',
}, title: '',
yLeftExtent: { extent: {
mode: 'full', mode: 'full',
type: 'axisExtentConfig', type: 'axisExtentConfig',
}, },
yRightExtent: { },
mode: 'full', ],
type: 'axisExtentConfig', xAxisConfig: {
type: 'xAxisConfig',
id: 'x',
showLabels: true,
showGridLines: true,
labelsOrientation: 0,
title: '',
position: 'bottom',
extent: {
mode: 'dataBounds',
type: 'axisExtentConfig',
},
}, },
markSizeRatio: 1, markSizeRatio: 1,
yLeftScale: 'linear',
yRightScale: 'linear',
layers: [ layers: [
{ {
layerId: 'first', layerId: 'first',
@ -2495,6 +2674,9 @@ describe('XYChart component', () => {
columnToLabel: '', columnToLabel: '',
xScaleType: 'ordinal', xScaleType: 'ordinal',
isHistogram: false, isHistogram: false,
isStacked: false,
isHorizontal: false,
isPercentage: false,
palette: mockPaletteOutput, palette: mockPaletteOutput,
table: data, table: data,
}, },
@ -2631,7 +2813,7 @@ describe('XYChart component', () => {
{ ...sampleLayer, accessors: ['a'], table: data }, { ...sampleLayer, accessors: ['a'], table: data },
{ ...sampleLayer, seriesType: 'bar', accessors: ['a'], table: data }, { ...sampleLayer, seriesType: 'bar', accessors: ['a'], table: data },
{ ...sampleLayer, seriesType: 'area', 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( const component = shallow(
@ -2661,7 +2843,13 @@ describe('XYChart component', () => {
test('it should apply the xTitle if is specified', () => { test('it should apply the xTitle if is specified', () => {
const { args } = sampleArgs(); const { args } = sampleArgs();
args.xTitle = 'My custom x-axis title'; args.xAxisConfig = {
type: 'xAxisConfig',
id: 'x',
showLabels: true,
title: 'My custom x-axis title',
position: 'bottom',
};
const component = shallow(<XYChart {...defaultProps} args={{ ...args }} />); const component = shallow(<XYChart {...defaultProps} args={{ ...args }} />);
@ -2671,11 +2859,26 @@ describe('XYChart component', () => {
test('it should hide the X axis title if the corresponding switch is off', () => { test('it should hide the X axis title if the corresponding switch is off', () => {
const { args } = sampleArgs(); const { args } = sampleArgs();
args.axisTitlesVisibilitySettings = { args.yAxisConfigs = [
x: false, {
yLeft: true, type: 'yAxisConfig',
yRight: true, position: 'left',
type: 'axisTitlesVisibilityConfig', showTitle: true,
},
{
type: 'yAxisConfig',
position: 'right',
showTitle: true,
},
];
args.xAxisConfig = {
type: 'xAxisConfig',
id: 'x',
showLabels: true,
showTitle: false,
title: 'My custom x-axis title',
position: 'bottom',
}; };
const component = shallow(<XYChart {...defaultProps} args={{ ...args }} />); const component = shallow(<XYChart {...defaultProps} args={{ ...args }} />);
@ -2692,11 +2895,26 @@ describe('XYChart component', () => {
test('it should show the X axis gridlines if the setting is on', () => { test('it should show the X axis gridlines if the setting is on', () => {
const { args } = sampleArgs(); const { args } = sampleArgs();
args.gridlinesVisibilitySettings = { args.yAxisConfigs = [
x: true, {
yLeft: false, type: 'yAxisConfig',
yRight: false, position: 'left',
type: 'gridlinesConfig', showGridLines: false,
},
{
type: 'yAxisConfig',
position: 'right',
showGridLines: false,
},
];
args.xAxisConfig = {
type: 'xAxisConfig',
id: 'x',
showLabels: true,
showGridLines: true,
title: 'My custom x-axis title',
position: 'bottom',
}; };
const component = shallow(<XYChart {...defaultProps} args={{ ...args }} />); const component = shallow(<XYChart {...defaultProps} args={{ ...args }} />);
@ -2741,10 +2959,13 @@ describe('XYChart component', () => {
layerType: LayerTypes.DATA, layerType: LayerTypes.DATA,
showLines: true, showLines: true,
seriesType: 'line', seriesType: 'line',
isStacked: false,
isHorizontal: false,
xAccessor: 'c', xAccessor: 'c',
accessors: ['a', 'b'], accessors: ['a', 'b'],
xScaleType: 'ordinal', xScaleType: 'ordinal',
isHistogram: false, isHistogram: false,
isPercentage: false,
palette: mockPaletteOutput, palette: mockPaletteOutput,
table: data, table: data,
}; };

View file

@ -47,20 +47,21 @@ import type { FilterEvent, BrushEvent, FormatFactory } from '../types';
import { isTimeChart } from '../../common/helpers'; import { isTimeChart } from '../../common/helpers';
import type { import type {
CommonXYDataLayerConfig, CommonXYDataLayerConfig,
ExtendedYConfig, ReferenceLineDecorationConfig,
ReferenceLineYConfig, ExtendedReferenceLineDecorationConfig,
SeriesType,
XYChartProps, XYChartProps,
AxisExtentConfigResult,
} from '../../common/types'; } from '../../common/types';
import { import {
isHorizontalChart, isHorizontalChart,
getAnnotationsLayers, getAnnotationsLayers,
getDataLayers, getDataLayers,
Series, AxisConfiguration,
getAxisPosition,
getFormattedTablesByLayers, getFormattedTablesByLayers,
getLayersFormats, getLayersFormats,
getLayersTitles, getLayersTitles,
isReferenceLineYConfig, isReferenceLineDecorationConfig,
getFilteredLayers, getFilteredLayers,
getReferenceLayers, getReferenceLayers,
isDataLayer, isDataLayer,
@ -68,10 +69,16 @@ import {
GroupsConfiguration, GroupsConfiguration,
getLinesCausedPaddings, getLinesCausedPaddings,
validateExtent, validateExtent,
Series,
getOriginalAxisPosition,
} from '../helpers'; } from '../helpers';
import { getXDomain, XyEndzones } from './x_domain'; import { getXDomain, XyEndzones } from './x_domain';
import { getLegendAction } from './legend_action'; import { getLegendAction } from './legend_action';
import { ReferenceLines, computeChartMargins } from './reference_lines'; import {
ReferenceLines,
computeChartMargins,
getAxisGroupForReferenceLine,
} from './reference_lines';
import { visualizationDefinitions } from '../definitions'; import { visualizationDefinitions } from '../definitions';
import { CommonXYLayerConfig } from '../../common/types'; import { CommonXYLayerConfig } from '../../common/types';
import { SplitChart } from './split_chart'; import { SplitChart } from './split_chart';
@ -138,8 +145,16 @@ function getValueLabelsStyling(isHorizontal: boolean): {
}; };
} }
function getIconForSeriesType(seriesType: SeriesType): IconType { function getIconForSeriesType(layer: CommonXYDataLayerConfig): IconType {
return visualizationDefinitions.find((c) => c.id === seriesType)!.icon || 'empty'; 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); export const XYChartReportable = React.memo(XYChart);
@ -166,15 +181,11 @@ export function XYChart({
fittingFunction, fittingFunction,
endValue, endValue,
emphasizeFitting, emphasizeFitting,
gridlinesVisibilitySettings,
valueLabels, valueLabels,
hideEndzones, hideEndzones,
xExtent,
yLeftExtent,
yRightExtent,
valuesInLegend, valuesInLegend,
yLeftScale, yAxisConfigs,
yRightScale, xAxisConfig,
splitColumnAccessor, splitColumnAccessor,
splitRowAccessor, splitRowAccessor,
} = args; } = args;
@ -204,9 +215,7 @@ export function XYChart({
); );
if (dataLayers.length === 0) { if (dataLayers.length === 0) {
const icon: IconType = getIconForSeriesType( const icon: IconType = getIconForSeriesType(getDataLayers(layers)?.[0]);
getDataLayers(layers)?.[0]?.seriesType || SeriesTypes.BAR
);
return <EmptyPlaceholder className="xyChart__empty" icon={icon} />; return <EmptyPlaceholder className="xyChart__empty" icon={icon} />;
} }
@ -236,37 +245,35 @@ export function XYChart({
shouldRotate, shouldRotate,
formatFactory, formatFactory,
fieldFormats, fieldFormats,
yLeftScale, yAxisConfigs
yRightScale
); );
const xTitle = args.xTitle || (xAxisColumn && xAxisColumn.name); const axesConfiguration = getAxesConfiguration(
dataLayers,
shouldRotate,
formatFactory,
fieldFormats,
[...(yAxisConfigs ?? []), ...(xAxisConfig ? [xAxisConfig] : [])]
);
const xTitle = xAxisConfig?.title || (xAxisColumn && xAxisColumn.name);
const yAxesMap = { const yAxesMap = {
left: yAxesConfiguration.find(({ groupId }) => groupId === 'left'), left: yAxesConfiguration.find(
right: yAxesConfiguration.find(({ groupId }) => groupId === 'right'), ({ position }) => position === getAxisPosition(Position.Left, shouldRotate)
),
right: yAxesConfiguration.find(
({ position }) => position === getAxisPosition(Position.Right, shouldRotate)
),
}; };
const titles = getLayersTitles( const titles = getLayersTitles(
dataLayers, dataLayers,
{ splitColumnAccessor, splitRowAccessor }, { splitColumnAccessor, splitRowAccessor },
{ xTitle: args.xTitle, yTitle: args.yTitle, yRightTitle: args.yRightTitle }, { xTitle },
yAxesConfiguration yAxesConfiguration
); );
const axisTitlesVisibilitySettings = args.axisTitlesVisibilitySettings || { const filteredBarLayers = dataLayers.filter(({ seriesType }) => seriesType === SeriesTypes.BAR);
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 filteredBarLayers = dataLayers.filter((layer) => layer.seriesType.includes('bar'));
const chartHasMoreThanOneBarSeries = const chartHasMoreThanOneBarSeries =
filteredBarLayers.length > 1 || filteredBarLayers.length > 1 ||
@ -285,9 +292,18 @@ export function XYChart({
minInterval, minInterval,
isTimeViz, isTimeViz,
isHistogramViz, isHistogramViz,
xExtent xAxisConfig?.extent
); );
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 = (axisSeries: Series[]) => { const getYAxesTitles = (axisSeries: Series[]) => {
return axisSeries return axisSeries
.map(({ layer, accessor }) => titles?.[layer]?.yTitles?.[accessor]) .map(({ layer, accessor }) => titles?.[layer]?.yTitles?.[accessor])
@ -312,45 +328,48 @@ export function XYChart({
const rangeAnnotations = getRangeAnnotations(annotationsLayers); const rangeAnnotations = getRangeAnnotations(annotationsLayers);
const visualConfigs = [ const visualConfigs = [
...referenceLineLayers.flatMap<ExtendedYConfig | ReferenceLineYConfig | undefined>( ...referenceLineLayers
({ yConfig }) => yConfig .flatMap<ReferenceLineDecorationConfig | ExtendedReferenceLineDecorationConfig | undefined>(
), ({ decorations }) => decorations
)
.map((config) => ({
...config,
position: config
? getAxisGroupForReferenceLine(axesConfiguration, config, shouldRotate)?.position ??
Position.Left
: Position.Bottom,
})),
...groupedLineAnnotations, ...groupedLineAnnotations,
].filter(Boolean); ].filter(Boolean);
const shouldHideDetails = annotationsLayers.length > 0 ? annotationsLayers[0].hide : false; const shouldHideDetails = annotationsLayers.length > 0 ? annotationsLayers[0].hide : false;
const linesPaddings = !shouldHideDetails ? getLinesCausedPaddings(visualConfigs, yAxesMap) : {}; const linesPaddings = !shouldHideDetails
? getLinesCausedPaddings(visualConfigs, yAxesMap, shouldRotate)
: {};
const getYAxesStyle = (groupId: 'left' | 'right') => { const getYAxesStyle = (axis: AxisConfiguration) => {
const tickVisible = const tickVisible = axis.showLabels;
groupId === 'right' const position = getOriginalAxisPosition(axis.position, shouldRotate);
? tickLabelsVisibilitySettings?.yRight
: tickLabelsVisibilitySettings?.yLeft;
const style = { const style = {
tickLabel: { tickLabel: {
fill: axis.labelColor,
visible: tickVisible, visible: tickVisible,
rotation: rotation: axis.labelsOrientation,
groupId === 'right'
? args.labelsOrientation?.yRight || 0
: args.labelsOrientation?.yLeft || 0,
padding: padding:
linesPaddings[groupId] != null linesPaddings[position] != null
? { ? {
inner: linesPaddings[groupId], inner: linesPaddings[position],
} }
: undefined, : undefined,
}, },
axisTitle: { axisTitle: {
visible: visible: axis.showTitle,
groupId === 'right'
? axisTitlesVisibilitySettings?.yRight
: axisTitlesVisibilitySettings?.yLeft,
// if labels are not visible add the padding to the title // if labels are not visible add the padding to the title
padding: padding:
!tickVisible && linesPaddings[groupId] != null !tickVisible && linesPaddings[position] != null
? { ? {
inner: linesPaddings[groupId], inner: linesPaddings[position],
} }
: undefined, : undefined,
}, },
@ -359,7 +378,10 @@ export function XYChart({
}; };
const getYAxisDomain = (axis: GroupsConfiguration[number]) => { const getYAxisDomain = (axis: GroupsConfiguration[number]) => {
const extent = axis.groupId === 'left' ? yLeftExtent : yRightExtent; const extent: AxisExtentConfigResult = axis.extent || {
type: 'axisExtentConfig',
mode: 'full',
};
const hasBarOrArea = Boolean( const hasBarOrArea = Boolean(
axis.series.some((series) => { axis.series.some((series) => {
const layer = layersById[series.layer]; const layer = layersById[series.layer];
@ -367,11 +389,12 @@ export function XYChart({
return false; 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 === AxisExtentModes.DATA_BOUNDS; const fit = !hasBarOrArea && extent.mode === AxisExtentModes.DATA_BOUNDS;
const padding = axis.boundsMargin || undefined;
let min: number = NaN; let min: number = NaN;
let max: number = NaN; let max: number = NaN;
@ -387,22 +410,31 @@ export function XYChart({
fit, fit,
min, min,
max, max,
padding,
includeDataFromIds: referenceLineLayers includeDataFromIds: referenceLineLayers
.flatMap((l) => .flatMap(
l.yConfig ? l.yConfig.map((yConfig) => ({ layerId: l.layerId, yConfig })) : [] (l) => l.decorations?.map((decoration) => ({ layerId: l.layerId, decoration })) || []
) )
.filter(({ yConfig }) => yConfig.axisMode === axis.groupId) .filter(({ decoration }) => {
.map(({ layerId, yConfig }) => if (decoration.axisId) {
isReferenceLineYConfig(yConfig) return axis.groupId.includes(decoration.axisId);
? `${layerId}-${yConfig.value}-${yConfig.fill !== 'none' ? 'rect' : 'line'}` }
: `${layerId}-${yConfig.forAccessor}-${yConfig.fill !== 'none' ? 'rect' : 'line'}`
return (
axis.position === getAxisPosition(decoration.position ?? Position.Left, shouldRotate)
);
})
.map(({ layerId, decoration }) =>
isReferenceLineDecorationConfig(decoration)
? `${layerId}-${decoration.value}-${decoration.fill !== 'none' ? 'rect' : 'line'}`
: `${layerId}-${decoration.forAccessor}-${decoration.fill !== 'none' ? 'rect' : 'line'}`
), ),
}; };
}; };
const shouldShowValueLabels = const shouldShowValueLabels =
// No stacked bar charts // No stacked bar charts
dataLayers.every((layer) => !layer.seriesType.includes('stacked')) && dataLayers.every((layer) => !layer.isStacked) &&
// No histogram charts // No histogram charts
!isHistogramViz; !isHistogramViz;
@ -516,18 +548,17 @@ export function XYChart({
}; };
const isHistogramModeEnabled = dataLayers.some( const isHistogramModeEnabled = dataLayers.some(
({ isHistogram, seriesType }) => ({ isHistogram, seriesType, isStacked }) =>
isHistogram && isHistogram && (isStacked || seriesType !== SeriesTypes.BAR || !chartHasMoreThanOneBarSeries)
(seriesType.includes('stacked') ||
!seriesType.includes('bar') ||
!chartHasMoreThanOneBarSeries)
); );
const shouldUseNewTimeAxis = const shouldUseNewTimeAxis =
isTimeViz && isHistogramModeEnabled && !useLegacyTimeAxis && !shouldRotate; isTimeViz && isHistogramModeEnabled && !useLegacyTimeAxis && !shouldRotate;
const defaultXAxisPosition = shouldRotate ? Position.Left : Position.Bottom;
const gridLineStyle = { const gridLineStyle = {
visible: gridlinesVisibilitySettings?.x, visible: xAxisConfig?.showGridLines,
strokeWidth: 1, strokeWidth: 1,
}; };
const xAxisStyle: RecursivePartial<AxisStyle> = shouldUseNewTimeAxis const xAxisStyle: RecursivePartial<AxisStyle> = shouldUseNewTimeAxis
@ -535,26 +566,28 @@ export function XYChart({
...MULTILAYER_TIME_AXIS_STYLE, ...MULTILAYER_TIME_AXIS_STYLE,
tickLabel: { tickLabel: {
...MULTILAYER_TIME_AXIS_STYLE.tickLabel, ...MULTILAYER_TIME_AXIS_STYLE.tickLabel,
visible: Boolean(tickLabelsVisibilitySettings?.x), visible: Boolean(xAxisConfig?.showLabels),
fill: xAxisConfig?.labelColor,
}, },
tickLine: { tickLine: {
...MULTILAYER_TIME_AXIS_STYLE.tickLine, ...MULTILAYER_TIME_AXIS_STYLE.tickLine,
visible: Boolean(tickLabelsVisibilitySettings?.x), visible: Boolean(xAxisConfig?.showLabels),
}, },
axisTitle: { axisTitle: {
visible: axisTitlesVisibilitySettings.x, visible: xAxisConfig?.showTitle,
}, },
} }
: { : {
tickLabel: { tickLabel: {
visible: tickLabelsVisibilitySettings?.x, visible: xAxisConfig?.showLabels,
rotation: labelsOrientation?.x, rotation: xAxisConfig?.labelsOrientation,
padding: linesPaddings.bottom != null ? { inner: linesPaddings.bottom } : undefined, padding: linesPaddings.bottom != null ? { inner: linesPaddings.bottom } : undefined,
fill: xAxisConfig?.labelColor,
}, },
axisTitle: { axisTitle: {
visible: axisTitlesVisibilitySettings.x, visible: xAxisConfig?.showTitle,
padding: padding:
!tickLabelsVisibilitySettings?.x && linesPaddings.bottom != null !xAxisConfig?.showLabels && linesPaddings.bottom != null
? { inner: linesPaddings.bottom } ? { inner: linesPaddings.bottom }
: undefined, : undefined,
}, },
@ -610,8 +643,8 @@ export function XYChart({
...chartTheme.chartPaddings, ...chartTheme.chartPaddings,
...computeChartMargins( ...computeChartMargins(
linesPaddings, linesPaddings,
tickLabelsVisibilitySettings, { ...tickLabelsVisibilitySettings, x: xAxisConfig?.showLabels },
axisTitlesVisibilitySettings, { ...axisTitlesVisibilitySettings, x: xAxisConfig?.showTitle },
yAxesMap, yAxesMap,
shouldRotate shouldRotate
), ),
@ -678,12 +711,24 @@ export function XYChart({
<Axis <Axis
id="x" id="x"
position={shouldRotate ? Position.Left : Position.Bottom} position={
xAxisConfig?.position
? getOriginalAxisPosition(xAxisConfig?.position, shouldRotate)
: defaultXAxisPosition
}
title={xTitle} title={xTitle}
gridLine={gridLineStyle} gridLine={gridLineStyle}
hide={dataLayers[0]?.hide || !dataLayers[0]?.xAccessor} hide={xAxisConfig?.hide || dataLayers[0]?.hide || !dataLayers[0]?.xAccessor}
tickFormat={(d) => safeXAccessorLabelRenderer(d)} tickFormat={(d) => {
let value = safeXAccessorLabelRenderer(d) || '';
if (xAxisConfig?.truncate && value.length > xAxisConfig.truncate) {
value = `${value.slice(0, xAxisConfig.truncate)}...`;
}
return value;
}}
style={xAxisStyle} style={xAxisStyle}
showOverlappingLabels={xAxisConfig?.showOverlappingLabels}
showDuplicatedTicks={xAxisConfig?.showDuplicates}
timeAxisLayerCount={shouldUseNewTimeAxis ? 3 : 0} timeAxisLayerCount={shouldUseNewTimeAxis ? 3 : 0}
/> />
{isSplitChart && splitTable && ( {isSplitChart && splitTable && (
@ -704,15 +749,20 @@ export function XYChart({
position={axis.position} position={axis.position}
title={getYAxesTitles(axis.series)} title={getYAxesTitles(axis.series)}
gridLine={{ gridLine={{
visible: visible: axis.showGridLines,
axis.groupId === 'right'
? gridlinesVisibilitySettings?.yRight
: gridlinesVisibilitySettings?.yLeft,
}} }}
hide={dataLayers[0]?.hide} hide={axis.hide || dataLayers[0]?.hide}
tickFormat={(d) => axis.formatter?.convert(d) || ''} tickFormat={(d) => {
style={getYAxesStyle(axis.groupId)} 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)} domain={getYAxisDomain(axis)}
showOverlappingLabels={axis.showOverlappingLabels}
showDuplicatedTicks={axis.showDuplicates}
ticks={5} ticks={5}
/> />
); );
@ -726,9 +776,9 @@ export function XYChart({
histogramMode={dataLayers.every( histogramMode={dataLayers.every(
(layer) => (layer) =>
layer.isHistogram && layer.isHistogram &&
(layer.seriesType.includes('stacked') || !layer.splitAccessor) && (layer.isStacked || !layer.splitAccessor) &&
(layer.seriesType.includes('stacked') || (layer.isStacked ||
!layer.seriesType.includes('bar') || layer.seriesType !== SeriesTypes.BAR ||
!chartHasMoreThanOneBarSeries) !chartHasMoreThanOneBarSeries)
)} )}
/> />
@ -758,18 +808,12 @@ export function XYChart({
{referenceLineLayers.length ? ( {referenceLineLayers.length ? (
<ReferenceLines <ReferenceLines
layers={referenceLineLayers} layers={referenceLineLayers}
formatters={{ xAxisFormatter={xAxisFormatter}
left: yAxesMap.left?.formatter, axesConfiguration={axesConfiguration}
right: yAxesMap.right?.formatter,
bottom: xAxisFormatter,
}}
axesMap={{
left: Boolean(yAxesMap.left),
right: Boolean(yAxesMap.right),
}}
isHorizontal={shouldRotate} isHorizontal={shouldRotate}
paddingMap={linesPaddings} paddingMap={linesPaddings}
titles={titles} titles={titles}
yAxesMap={yAxesMap}
/> />
) : null} ) : null}
{rangeAnnotations.length || groupedLineAnnotations.length ? ( {rangeAnnotations.length || groupedLineAnnotations.length ? (

View file

@ -22,13 +22,13 @@ import {
export const visualizationDefinitions = [ export const visualizationDefinitions = [
{ id: SeriesTypes.BAR, icon: BarIcon }, { id: SeriesTypes.BAR, icon: BarIcon },
{ id: SeriesTypes.BAR_STACKED, icon: BarStackedIcon }, { id: `${SeriesTypes.BAR}_stacked`, icon: BarStackedIcon },
{ id: SeriesTypes.BAR_HORIZONTAL, icon: BarHorizontalIcon }, { id: `${SeriesTypes.BAR}_horizontal`, icon: BarHorizontalIcon },
{ id: SeriesTypes.BAR_PERCENTAGE_STACKED, icon: BarPercentageIcon }, { id: `${SeriesTypes.BAR}_percentage_stacked`, icon: BarPercentageIcon },
{ id: SeriesTypes.BAR_HORIZONTAL_STACKED, icon: BarHorizontalStackedIcon }, { id: `${SeriesTypes.BAR}_horizontal_stacked`, icon: BarHorizontalStackedIcon },
{ id: SeriesTypes.BAR_HORIZONTAL_PERCENTAGE_STACKED, icon: BarHorizontalPercentageIcon }, { id: `${SeriesTypes.BAR}_horizontal_percentage_stacked`, icon: BarHorizontalPercentageIcon },
{ id: SeriesTypes.LINE, icon: LineIcon }, { id: SeriesTypes.LINE, icon: LineIcon },
{ id: SeriesTypes.AREA, icon: AreaIcon }, { id: SeriesTypes.AREA, icon: AreaIcon },
{ id: SeriesTypes.AREA_STACKED, icon: AreaStackedIcon }, { id: `${SeriesTypes.AREA}_stacked`, icon: AreaStackedIcon },
{ id: SeriesTypes.AREA_PERCENTAGE_STACKED, icon: AreaPercentageIcon }, { id: `${SeriesTypes.AREA}_percentage_stacked`, icon: AreaPercentageIcon },
]; ];

View file

@ -11,31 +11,34 @@ import { EuiFlexGroup, EuiIcon, EuiIconProps, EuiText } from '@elastic/eui';
import classnames from 'classnames'; import classnames from 'classnames';
import type { import type {
IconPosition, IconPosition,
YAxisMode, ReferenceLineDecorationConfig,
ExtendedYConfig,
CollectiveConfig, CollectiveConfig,
} from '../../common/types'; } from '../../common/types';
import { getBaseIconPlacement } from '../components'; import { getBaseIconPlacement } from '../components';
import { hasIcon, iconSet } from './icon'; import { hasIcon, iconSet } from './icon';
import { AxesMap, getOriginalAxisPosition } from './axes_configuration';
export const LINES_MARKER_SIZE = 20; export const LINES_MARKER_SIZE = 20;
type PartialExtendedYConfig = Pick< type PartialReferenceLineDecorationConfig = Pick<
ExtendedYConfig, ReferenceLineDecorationConfig,
'axisMode' | 'icon' | 'iconPosition' | 'textVisibility' 'icon' | 'iconPosition' | 'textVisibility'
>; > & {
position?: Position;
};
type PartialCollectiveConfig = Pick<CollectiveConfig, 'axisMode' | 'icon' | 'textVisibility'>; type PartialCollectiveConfig = Pick<CollectiveConfig, 'position' | 'icon' | 'textVisibility'>;
const isExtendedYConfig = ( const isExtendedDecorationConfig = (
config: PartialExtendedYConfig | PartialCollectiveConfig | undefined config: PartialReferenceLineDecorationConfig | PartialCollectiveConfig | undefined
): config is PartialExtendedYConfig => ): config is PartialReferenceLineDecorationConfig =>
(config as PartialExtendedYConfig)?.iconPosition ? true : false; (config as PartialReferenceLineDecorationConfig)?.iconPosition ? true : false;
// Note: it does not take into consideration whether the reference line is in view or not // Note: it does not take into consideration whether the reference line is in view or not
export const getLinesCausedPaddings = ( export const getLinesCausedPaddings = (
visualConfigs: Array<PartialExtendedYConfig | PartialCollectiveConfig | undefined>, visualConfigs: Array<PartialReferenceLineDecorationConfig | PartialCollectiveConfig | undefined>,
axesMap: Record<'left' | 'right', unknown> axesMap: AxesMap,
shouldRotate: boolean
) => { ) => {
// collect all paddings for the 4 axis: if any text is detected double it. // collect all paddings for the 4 axis: if any text is detected double it.
const paddings: Partial<Record<Position, number>> = {}; const paddings: Partial<Record<Position, number>> = {};
@ -44,11 +47,15 @@ export const getLinesCausedPaddings = (
if (!config) { if (!config) {
return; return;
} }
const { axisMode, icon, textVisibility } = config; const { position, icon, textVisibility } = config;
const iconPosition = isExtendedYConfig(config) ? config.iconPosition : undefined; const iconPosition = isExtendedDecorationConfig(config) ? config.iconPosition : undefined;
if (axisMode && (hasIcon(icon) || textVisibility)) { if (position && (hasIcon(icon) || textVisibility)) {
const placement = getBaseIconPlacement(iconPosition, axesMap, axisMode); const placement = getBaseIconPlacement(
iconPosition,
axesMap,
getOriginalAxisPosition(position, shouldRotate)
);
paddings[placement] = Math.max( paddings[placement] = Math.max(
paddings[placement] || 0, paddings[placement] || 0,
LINES_MARKER_SIZE * (textVisibility ? 2 : 1) // double the padding size if there's text LINES_MARKER_SIZE * (textVisibility ? 2 : 1) // double the padding size if there's text
@ -174,7 +181,7 @@ export const AnnotationIcon = ({
}; };
interface MarkerConfig { interface MarkerConfig {
axisMode?: YAxisMode; position?: Position;
icon?: string; icon?: string;
textVisibility?: boolean; textVisibility?: boolean;
iconPosition?: IconPosition; iconPosition?: IconPosition;

View file

@ -6,7 +6,7 @@
* Side Public License, v 1. * Side Public License, v 1.
*/ */
import { DataLayerConfig } from '../../common'; import { DataLayerConfig, YAxisConfigResult } from '../../common';
import { LayerTypes } from '../../common/constants'; import { LayerTypes } from '../../common/constants';
import { Datatable } from '@kbn/expressions-plugin/public'; import { Datatable } from '@kbn/expressions-plugin/public';
import { getAxesConfiguration } from './axes_configuration'; import { getAxesConfiguration } from './axes_configuration';
@ -221,6 +221,14 @@ describe('axes_configuration', () => {
}, },
}; };
const yAxisConfigs: YAxisConfigResult[] = [
{
id: '1',
position: 'right',
type: 'yAxisConfig',
},
];
const sampleLayer: DataLayerConfig = { const sampleLayer: DataLayerConfig = {
layerId: 'first', layerId: 'first',
type: 'dataLayer', type: 'dataLayer',
@ -233,6 +241,9 @@ describe('axes_configuration', () => {
columnToLabel: '{"a": "Label A", "b": "Label B", "d": "Label D"}', columnToLabel: '{"a": "Label A", "b": "Label B", "d": "Label D"}',
xScaleType: 'ordinal', xScaleType: 'ordinal',
isHistogram: false, isHistogram: false,
isPercentage: false,
isStacked: false,
isHorizontal: false,
palette: { type: 'palette', name: 'default' }, palette: { type: 'palette', name: 'default' },
table: tables.first, table: tables.first,
}; };
@ -253,7 +264,7 @@ describe('axes_configuration', () => {
it('should map auto series to left axis', () => { it('should map auto series to left axis', () => {
const formatFactory = jest.fn(); 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.length).toEqual(1);
expect(groups[0].position).toEqual('left'); expect(groups[0].position).toEqual('left');
expect(groups[0].series[0].accessor).toEqual('yAccessorId'); expect(groups[0].series[0].accessor).toEqual('yAccessorId');
@ -263,7 +274,7 @@ describe('axes_configuration', () => {
it('should map auto series to right axis if formatters do not match', () => { it('should map auto series to right axis if formatters do not match', () => {
const formatFactory = jest.fn(); const formatFactory = jest.fn();
const twoSeriesLayer = { ...sampleLayer, accessors: ['yAccessorId', 'yAccessorId2'] }; 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.length).toEqual(2);
expect(groups[0].position).toEqual('left'); expect(groups[0].position).toEqual('left');
expect(groups[1].position).toEqual('right'); expect(groups[1].position).toEqual('right');
@ -277,7 +288,7 @@ describe('axes_configuration', () => {
...sampleLayer, ...sampleLayer,
accessors: ['yAccessorId', 'yAccessorId2', 'yAccessorId3'], 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.length).toEqual(2);
expect(groups[0].position).toEqual('left'); expect(groups[0].position).toEqual('left');
expect(groups[1].position).toEqual('right'); expect(groups[1].position).toEqual('right');
@ -292,12 +303,13 @@ describe('axes_configuration', () => {
[ [
{ {
...sampleLayer, ...sampleLayer,
yConfig: [{ type: 'yConfig', forAccessor: 'yAccessorId', axisMode: 'right' }], decorations: [{ type: 'dataDecorationConfig', forAccessor: 'yAccessorId', axisId: '1' }],
}, },
], ],
false, false,
formatFactory, formatFactory,
fieldFormats fieldFormats,
yAxisConfigs
); );
expect(groups.length).toEqual(1); expect(groups.length).toEqual(1);
expect(groups[0].position).toEqual('right'); expect(groups[0].position).toEqual('right');
@ -312,19 +324,20 @@ describe('axes_configuration', () => {
{ {
...sampleLayer, ...sampleLayer,
accessors: ['yAccessorId', 'yAccessorId3', 'yAccessorId4'], accessors: ['yAccessorId', 'yAccessorId3', 'yAccessorId4'],
yConfig: [{ type: 'yConfig', forAccessor: 'yAccessorId', axisMode: 'right' }], decorations: [{ type: 'dataDecorationConfig', forAccessor: 'yAccessorId', axisId: '1' }],
}, },
], ],
false, false,
formatFactory, formatFactory,
fieldFormats fieldFormats,
yAxisConfigs
); );
expect(groups.length).toEqual(2); expect(groups.length).toEqual(2);
expect(groups[0].position).toEqual('left'); expect(groups[0].position).toEqual('right');
expect(groups[0].series[0].accessor).toEqual('yAccessorId3'); expect(groups[0].series[0].accessor).toEqual('yAccessorId');
expect(groups[0].series[1].accessor).toEqual('yAccessorId4'); expect(groups[1].position).toEqual('left');
expect(groups[1].position).toEqual('right'); expect(groups[1].series[0].accessor).toEqual('yAccessorId3');
expect(groups[1].series[0].accessor).toEqual('yAccessorId'); expect(groups[1].series[1].accessor).toEqual('yAccessorId4');
expect(formatFactory).toHaveBeenCalledWith({ id: 'number', params: {} }); expect(formatFactory).toHaveBeenCalledWith({ id: 'number', params: {} });
expect(formatFactory).toHaveBeenCalledWith({ id: 'currency', params: {} }); expect(formatFactory).toHaveBeenCalledWith({ id: 'currency', params: {} });
}); });
@ -336,12 +349,13 @@ describe('axes_configuration', () => {
{ {
...sampleLayer, ...sampleLayer,
accessors: ['yAccessorId', 'yAccessorId3', 'yAccessorId4'], accessors: ['yAccessorId', 'yAccessorId3', 'yAccessorId4'],
yConfig: [{ type: 'yConfig', forAccessor: 'yAccessorId', axisMode: 'right' }], decorations: [{ type: 'dataDecorationConfig', forAccessor: 'yAccessorId', axisId: '1' }],
}, },
], ],
false, false,
formatFactory, formatFactory,
fieldFormats fieldFormats,
yAxisConfigs
); );
expect(formatFactory).toHaveBeenCalledTimes(2); expect(formatFactory).toHaveBeenCalledTimes(2);
expect(formatFactory).toHaveBeenCalledWith({ id: 'number', params: {} }); expect(formatFactory).toHaveBeenCalledWith({ id: 'number', params: {} });

View file

@ -6,15 +6,18 @@
* Side Public License, v 1. * Side Public License, v 1.
*/ */
import { Position } from '@elastic/charts';
import type { IFieldFormat, SerializedFieldFormat } from '@kbn/field-formats-plugin/common'; import type { IFieldFormat, SerializedFieldFormat } from '@kbn/field-formats-plugin/common';
import { getAccessorByDimension } from '@kbn/visualizations-plugin/common/utils'; import { getAccessorByDimension } from '@kbn/visualizations-plugin/common/utils';
import { FormatFactory } from '../types'; import { FormatFactory } from '../types';
import { import {
AxisExtentConfig, AxisExtentConfig,
CommonXYDataLayerConfig, CommonXYDataLayerConfig,
ExtendedYConfig, DataDecorationConfig,
YConfig, YAxisConfig,
YScaleType, ReferenceLineDecorationConfig,
YAxisConfigResult,
XAxisConfigResult,
} from '../../common'; } from '../../common';
import { LayersFieldFormats } from './layers'; import { LayersFieldFormats } from './layers';
@ -25,15 +28,26 @@ export interface Series {
interface FormattedMetric extends Series { interface FormattedMetric extends Series {
fieldFormat: SerializedFieldFormat; fieldFormat: SerializedFieldFormat;
axisId?: string;
} }
export type GroupsConfiguration = Array<{ interface AxesSeries {
groupId: 'left' | 'right'; [key: string]: FormattedMetric[];
position: 'left' | 'right' | 'bottom' | 'top'; }
export interface AxisConfiguration extends Omit<YAxisConfig, 'id'> {
/**
* Axis group identificator. Format: `axis-${axis.id}` or just `left`/`right`.
*/
groupId: string;
position: Position;
formatter?: IFieldFormat; formatter?: IFieldFormat;
series: Series[]; series: Series[];
scale?: YScaleType; }
}>;
export type GroupsConfiguration = AxisConfiguration[];
export type AxesMap = Record<'left' | 'right', AxisConfiguration | undefined>;
export function isFormatterCompatible( export function isFormatterCompatible(
formatter1: SerializedFieldFormat, formatter1: SerializedFieldFormat,
@ -42,91 +56,203 @@ export function isFormatterCompatible(
return formatter1?.id === formatter2?.id; 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( export function groupAxesByType(
layers: CommonXYDataLayerConfig[], layers: CommonXYDataLayerConfig[],
fieldFormats: LayersFieldFormats fieldFormats: LayersFieldFormats,
yAxisConfigs?: YAxisConfig[]
) { ) {
const series: { const series: AxesSeries = {
auto: FormattedMetric[];
left: FormattedMetric[];
right: FormattedMetric[];
bottom: FormattedMetric[];
} = {
auto: [], auto: [],
left: [], left: [],
right: [], right: [],
bottom: [],
}; };
const leftSeriesKeys: string[] = [];
const rightSeriesKeys: string[] = [];
layers.forEach((layer) => { layers.forEach((layer) => {
const { layerId, table } = layer; const { layerId, table } = layer;
layer.accessors.forEach((accessor) => { layer.accessors.forEach((accessor) => {
const yConfig: Array<YConfig | ExtendedYConfig> | undefined = layer.yConfig; const dataDecorations:
| Array<DataDecorationConfig | ReferenceLineDecorationConfig>
| undefined = layer.decorations;
const yAccessor = getAccessorByDimension(accessor, table.columns); const yAccessor = getAccessorByDimension(accessor, table.columns);
const mode = const decorationByAccessor = dataDecorations?.find(
yConfig?.find(({ forAccessor }) => forAccessor === yAccessor)?.axisMode || 'auto'; (decorationConfig) => decorationConfig.forAccessor === yAccessor
);
const axisConfigById = yAxisConfigs?.find(
(axis) =>
decorationByAccessor?.axisId && axis.id && axis.id === decorationByAccessor?.axisId
);
const key = axisConfigById?.id ? `axis-${axisConfigById?.id}` : 'auto';
const fieldFormat = fieldFormats[layerId].yAccessors[yAccessor]!; const fieldFormat = fieldFormats[layerId].yAccessors[yAccessor]!;
series[mode].push({ layer: layer.layerId, accessor: yAccessor, fieldFormat }); if (!series[key]) {
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; 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) => { series.auto.forEach((currentSeries) => {
if ( const leftAxisGroupId = tablesExist
series.left.length === 0 || ? leftSeriesKeys.find((leftSeriesKey) =>
(tablesExist && isAxisSeriesAppliedForFormatter(series[leftSeriesKey], currentSeries)
series.left.every((leftSeries) => )
isFormatterCompatible(leftSeries.fieldFormat, currentSeries.fieldFormat) : undefined;
))
) { const rightAxisGroupId = tablesExist
series.left.push(currentSeries); ? rightSeriesKeys.find((rightSeriesKey) =>
} else if ( isAxisSeriesAppliedForFormatter(series[rightSeriesKey], currentSeries)
series.right.length === 0 || )
(tablesExist && : undefined;
series.left.every((leftSeries) =>
isFormatterCompatible(leftSeries.fieldFormat, currentSeries.fieldFormat) let axisGroupId = LEFT_GLOBAL_AXIS_ID;
))
) { if (series[LEFT_GLOBAL_AXIS_ID].length === 0 || leftAxisGroupId) {
series.right.push(currentSeries); axisGroupId = leftAxisGroupId || LEFT_GLOBAL_AXIS_ID;
} else if (series.right.length >= series.left.length) { } else if (series[RIGHT_GLOBAL_AXIS_ID].length === 0 || rightAxisGroupId) {
series.left.push(currentSeries); 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 { } else {
series.right.push(currentSeries); axisGroupId = RIGHT_GLOBAL_AXIS_ID;
} }
series[axisGroupId].push(currentSeries);
}); });
return series; return series;
} }
export function getAxisPosition(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 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) || {};
}
const getXAxisConfig = (axisConfigs: Array<XAxisConfigResult | YAxisConfigResult> = []) =>
axisConfigs.find(({ type }) => type === 'xAxisConfig');
export function getAxesConfiguration( export function getAxesConfiguration(
layers: CommonXYDataLayerConfig[], layers: CommonXYDataLayerConfig[],
shouldRotate: boolean, shouldRotate: boolean,
formatFactory: FormatFactory | undefined, formatFactory: FormatFactory | undefined,
fieldFormats: LayersFieldFormats, fieldFormats: LayersFieldFormats,
yLeftScale?: YScaleType, axisConfigs?: Array<XAxisConfigResult | YAxisConfigResult>
yRightScale?: YScaleType
): GroupsConfiguration { ): GroupsConfiguration {
const series = groupAxesByType(layers, fieldFormats); const series = groupAxesByType(layers, fieldFormats, axisConfigs);
const axisGroups: GroupsConfiguration = []; const axisGroups: GroupsConfiguration = [];
let position: Position;
if (series.left.length > 0) { axisConfigs?.forEach((axis) => {
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,
formatter: formatFactory?.(series[groupId][0].fieldFormat),
series: series[groupId].map(({ fieldFormat, ...currentSeries }) => currentSeries),
...axisGlobalConfig(axis.position || Position.Left, axisConfigs),
...axis,
position,
});
}
});
if (series[LEFT_GLOBAL_AXIS_ID].length > 0) {
position = shouldRotate ? Position.Bottom : Position.Left;
axisGroups.push({ axisGroups.push({
groupId: 'left', groupId: LEFT_GLOBAL_AXIS_ID,
position: shouldRotate ? 'bottom' : 'left',
formatter: formatFactory?.(series.left[0].fieldFormat), formatter: formatFactory?.(series.left[0].fieldFormat),
series: series.left.map(({ fieldFormat, ...currentSeries }) => currentSeries), series: series.left.map(({ fieldFormat, ...currentSeries }) => currentSeries),
scale: yLeftScale, ...axisGlobalConfig(Position.Left, axisConfigs),
position,
}); });
} }
if (series.right.length > 0) { if (series[RIGHT_GLOBAL_AXIS_ID].length > 0) {
position = shouldRotate ? Position.Top : Position.Right;
axisGroups.push({ axisGroups.push({
groupId: 'right', groupId: RIGHT_GLOBAL_AXIS_ID,
position: shouldRotate ? 'top' : 'right',
formatter: formatFactory?.(series.right[0].fieldFormat), formatter: formatFactory?.(series.right[0].fieldFormat),
series: series.right.map(({ fieldFormat, ...currentSeries }) => currentSeries), series: series.right.map(({ fieldFormat, ...currentSeries }) => currentSeries),
scale: yRightScale, ...axisGlobalConfig(Position.Right, axisConfigs),
position,
});
}
const xAxisConfig = getXAxisConfig(axisConfigs);
if (xAxisConfig) {
position = getAxisPosition(xAxisConfig.position || Position.Bottom, shouldRotate);
axisGroups.push({
groupId: 'bottom',
series: [],
...xAxisConfig,
position,
}); });
} }

View file

@ -54,8 +54,11 @@ describe('color_assignment', () => {
type: 'dataLayer', type: 'dataLayer',
showLines: true, showLines: true,
isHistogram: true, isHistogram: true,
isPercentage: false,
xScaleType: 'linear', xScaleType: 'linear',
seriesType: 'bar', seriesType: 'bar',
isStacked: false,
isHorizontal: false,
palette: { type: 'palette', name: 'palette1' }, palette: { type: 'palette', name: 'palette1' },
layerType: LayerTypes.DATA, layerType: LayerTypes.DATA,
splitAccessor: 'split1', splitAccessor: 'split1',
@ -68,7 +71,10 @@ describe('color_assignment', () => {
showLines: true, showLines: true,
xScaleType: 'linear', xScaleType: 'linear',
isHistogram: true, isHistogram: true,
isPercentage: false,
seriesType: 'bar', seriesType: 'bar',
isStacked: false,
isHorizontal: false,
palette: { type: 'palette', name: 'palette2' }, palette: { type: 'palette', name: 'palette2' },
layerType: LayerTypes.DATA, layerType: LayerTypes.DATA,
splitAccessor: 'split2', splitAccessor: 'split2',

View file

@ -27,6 +27,7 @@ import {
import type { ExpressionValueVisDimension } from '@kbn/visualizations-plugin/common/expression_functions'; import type { ExpressionValueVisDimension } from '@kbn/visualizations-plugin/common/expression_functions';
import { PaletteRegistry, SeriesLayer } from '@kbn/coloring'; import { PaletteRegistry, SeriesLayer } from '@kbn/coloring';
import { CommonXYDataLayerConfig, XScaleType } from '../../common'; import { CommonXYDataLayerConfig, XScaleType } from '../../common';
import { AxisModes, SeriesTypes } from '../../common/constants';
import { FormatFactory } from '../types'; import { FormatFactory } from '../types';
import { getSeriesColor } from './state'; import { getSeriesColor } from './state';
import { ColorAssignments } from './color_assignment'; import { ColorAssignments } from './color_assignment';
@ -350,10 +351,14 @@ export const getSeriesProps: GetSeriesPropsFn = ({
formattedDatatableInfo, formattedDatatableInfo,
defaultXScaleType, defaultXScaleType,
}): SeriesSpec => { }): SeriesSpec => {
const { table, markSizeAccessor } = layer; const { table, isStacked, markSizeAccessor } = layer;
const isStacked = layer.seriesType.includes('stacked'); const isPercentage = layer.isPercentage;
const isPercentage = layer.seriesType.includes('percentage'); let stackMode: StackMode | undefined = isPercentage ? AxisModes.PERCENTAGE : undefined;
const isBarChart = layer.seriesType.includes('bar'); if (yAxis?.mode) {
stackMode = yAxis?.mode === AxisModes.NORMAL ? undefined : yAxis?.mode;
}
const scaleType = yAxis?.scaleType || ScaleType.Linear;
const isBarChart = layer.seriesType === SeriesTypes.BAR;
const xColumnId = layer.xAccessor && getAccessorByDimension(layer.xAccessor, table.columns); const xColumnId = layer.xAccessor && getAccessorByDimension(layer.xAccessor, table.columns);
const splitColumnId = const splitColumnId =
layer.splitAccessor && getAccessorByDimension(layer.splitAccessor, table.columns); layer.splitAccessor && getAccessorByDimension(layer.splitAccessor, table.columns);
@ -413,9 +418,9 @@ export const getSeriesProps: GetSeriesPropsFn = ({
data: rows, data: rows,
xScaleType: xColumnId ? layer.xScaleType ?? defaultXScaleType : 'ordinal', xScaleType: xColumnId ? layer.xScaleType ?? defaultXScaleType : 'ordinal',
yScaleType: yScaleType:
formatter?.id === 'bytes' && yAxis?.scale === ScaleType.Linear formatter?.id === 'bytes' && scaleType === ScaleType.Linear
? ScaleType.LinearBinary ? ScaleType.LinearBinary
: yAxis?.scale || ScaleType.Linear, : scaleType,
color: (series) => color: (series) =>
getColor( getColor(
series, series,
@ -431,7 +436,7 @@ export const getSeriesProps: GetSeriesPropsFn = ({
), ),
groupId: yAxis?.groupId, groupId: yAxis?.groupId,
enableHistogramMode, enableHistogramMode,
stackMode: isPercentage ? StackMode.Percentage : undefined, stackMode,
timeZone, timeZone,
areaSeriesStyle: { areaSeriesStyle: {
point: getPointConfig({ point: getPointConfig({

View file

@ -17,7 +17,6 @@ import {
CommonXYDataLayerConfig, CommonXYDataLayerConfig,
CommonXYLayerConfig, CommonXYLayerConfig,
ReferenceLineLayerConfig, ReferenceLineLayerConfig,
SeriesType,
} from '../../common/types'; } from '../../common/types';
import { GroupsConfiguration } from './axes_configuration'; import { GroupsConfiguration } from './axes_configuration';
import { getFormat } from './format'; import { getFormat } from './format';
@ -110,7 +109,7 @@ const getAccessorWithFieldFormat = (
const getYAccessorWithFieldFormat = ( const getYAccessorWithFieldFormat = (
dimension: string | ExpressionValueVisDimension | undefined, dimension: string | ExpressionValueVisDimension | undefined,
columns: Datatable['columns'], columns: Datatable['columns'],
seriesType: SeriesType isPercentage: boolean
) => { ) => {
if (!dimension) { if (!dimension) {
return {}; return {};
@ -118,7 +117,7 @@ const getYAccessorWithFieldFormat = (
const accessor = getAccessorByDimension(dimension, columns); const accessor = getAccessorByDimension(dimension, columns);
let format = getFormat(columns, dimension) ?? { id: 'number' }; 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]%' } }; format = { id: 'percent', params: { pattern: '0.[00]%' } };
} }
@ -126,7 +125,7 @@ const getYAccessorWithFieldFormat = (
}; };
export const getLayerFormats = ( export const getLayerFormats = (
{ xAccessor, accessors, splitAccessor, table, seriesType }: CommonXYDataLayerConfig, { xAccessor, accessors, splitAccessor, table, isPercentage }: CommonXYDataLayerConfig,
{ splitColumnAccessor, splitRowAccessor }: SplitAccessors { splitColumnAccessor, splitRowAccessor }: SplitAccessors
): LayerFieldFormats => { ): LayerFieldFormats => {
const yAccessors: Array<string | ExpressionValueVisDimension> = accessors; const yAccessors: Array<string | ExpressionValueVisDimension> = accessors;
@ -135,7 +134,7 @@ export const getLayerFormats = (
yAccessors: yAccessors.reduce( yAccessors: yAccessors.reduce(
(formatters, a) => ({ (formatters, a) => ({
...formatters, ...formatters,
...getYAccessorWithFieldFormat(a, table.columns, seriesType), ...getYAccessorWithFieldFormat(a, table.columns, isPercentage),
}), }),
{} {}
), ),
@ -160,28 +159,21 @@ export const getLayersFormats = (
const getTitleForYAccessor = ( const getTitleForYAccessor = (
layerId: string, layerId: string,
yAccessor: string | ExpressionValueVisDimension, yAccessor: string | ExpressionValueVisDimension,
{ yTitle, yRightTitle }: Omit<CustomTitles, 'xTitle'>,
groups: GroupsConfiguration, groups: GroupsConfiguration,
columns: Datatable['columns'] columns: Datatable['columns']
) => { ) => {
const column = getColumnByAccessor(yAccessor, columns); const column = getColumnByAccessor(yAccessor, columns);
const isRight = groups.some((group) => const axisGroup = groups.find((group) =>
group.series.some( group.series.some(({ accessor, layer }) => accessor === yAccessor && layer === layerId)
({ accessor, layer }) =>
accessor === yAccessor && layer === layerId && group.groupId === 'right'
)
); );
if (isRight) {
return yRightTitle || column!.name;
}
return yTitle || column!.name; return axisGroup?.title || column!.name;
}; };
export const getLayerTitles = ( export const getLayerTitles = (
{ xAccessor, accessors, splitAccessor, table, layerId }: CommonXYDataLayerConfig, { xAccessor, accessors, splitAccessor, table, layerId }: CommonXYDataLayerConfig,
{ splitColumnAccessor, splitRowAccessor }: SplitAccessors, { splitColumnAccessor, splitRowAccessor }: SplitAccessors,
{ xTitle, yTitle, yRightTitle }: CustomTitles, { xTitle }: CustomTitles,
groups: GroupsConfiguration groups: GroupsConfiguration
): LayerAccessorsTitles => { ): LayerAccessorsTitles => {
const mapTitle = (dimension?: string | ExpressionValueVisDimension) => { const mapTitle = (dimension?: string | ExpressionValueVisDimension) => {
@ -194,13 +186,7 @@ export const getLayerTitles = (
}; };
const getYTitle = (accessor: string) => ({ const getYTitle = (accessor: string) => ({
[accessor]: getTitleForYAccessor( [accessor]: getTitleForYAccessor(layerId, accessor, groups, table.columns),
layerId,
accessor,
{ yTitle, yRightTitle },
groups,
table.columns
),
}); });
const xColumnId = xAccessor && getAccessorByDimension(xAccessor, table.columns); const xColumnId = xAccessor && getAccessorByDimension(xAccessor, table.columns);

View file

@ -6,23 +6,15 @@
* Side Public License, v 1. * Side Public License, v 1.
*/ */
import type { CommonXYLayerConfig, SeriesType, ExtendedYConfig, YConfig } from '../../common'; import type {
CommonXYLayerConfig,
ReferenceLineDecorationConfig,
DataDecorationConfig,
} from '../../common';
import { getDataLayers, isAnnotationsLayer, isDataLayer, isReferenceLine } from './visualization'; import { getDataLayers, isAnnotationsLayer, isDataLayer, isReferenceLine } 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: CommonXYLayerConfig[]) { export function isHorizontalChart(layers: CommonXYLayerConfig[]) {
return getDataLayers(layers).every((l) => isHorizontalSeries(l.seriesType)); return getDataLayers(layers).every((l) => l.isHorizontal);
} }
export const getSeriesColor = (layer: CommonXYLayerConfig, accessor: string) => { export const getSeriesColor = (layer: CommonXYLayerConfig, accessor: string) => {
@ -33,6 +25,10 @@ export const getSeriesColor = (layer: CommonXYLayerConfig, accessor: string) =>
) { ) {
return null; return null;
} }
const yConfig: Array<YConfig | ExtendedYConfig> | undefined = layer?.yConfig; const decorations: Array<DataDecorationConfig | ReferenceLineDecorationConfig> | undefined =
return yConfig?.find((yConf) => yConf.forAccessor === accessor)?.color || null; layer?.decorations;
return (
decorations?.find((decorationConfig) => decorationConfig.forAccessor === accessor)?.color ||
null
);
}; };

View file

@ -10,7 +10,7 @@ import {
LayerTypes, LayerTypes,
REFERENCE_LINE, REFERENCE_LINE,
REFERENCE_LINE_LAYER, REFERENCE_LINE_LAYER,
REFERENCE_LINE_Y_CONFIG, EXTENDED_REFERENCE_LINE_DECORATION_CONFIG,
} from '../../common/constants'; } from '../../common/constants';
import { import {
CommonXYLayerConfig, CommonXYLayerConfig,
@ -19,8 +19,8 @@ import {
CommonXYAnnotationLayerConfig, CommonXYAnnotationLayerConfig,
ReferenceLineLayerConfig, ReferenceLineLayerConfig,
ReferenceLineConfig, ReferenceLineConfig,
ExtendedYConfigResult, ReferenceLineDecorationConfigResult,
ReferenceLineYConfig, ExtendedReferenceLineDecorationConfig,
} from '../../common/types'; } from '../../common/types';
export const isDataLayer = (layer: CommonXYLayerConfig): layer is CommonXYDataLayerConfig => 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 => export const isReferenceLine = (layer: CommonXYLayerConfig): layer is ReferenceLineConfig =>
layer.type === REFERENCE_LINE; layer.type === REFERENCE_LINE;
export const isReferenceLineYConfig = ( export const isReferenceLineDecorationConfig = (
yConfig: ReferenceLineYConfig | ExtendedYConfigResult decoration: ExtendedReferenceLineDecorationConfig | ReferenceLineDecorationConfigResult
): yConfig is ReferenceLineYConfig => yConfig.type === REFERENCE_LINE_Y_CONFIG; ): decoration is ExtendedReferenceLineDecorationConfig =>
decoration.type === EXTENDED_REFERENCE_LINE_DECORATION_CONFIG;
export const isReferenceLineOrLayer = ( export const isReferenceLineOrLayer = (
layer: CommonXYLayerConfig layer: CommonXYLayerConfig

View file

@ -18,18 +18,16 @@ import {
xyVisFunction, xyVisFunction,
layeredXyVisFunction, layeredXyVisFunction,
extendedDataLayerFunction, extendedDataLayerFunction,
dataDecorationConfigFunction,
xAxisConfigFunction,
yAxisConfigFunction, yAxisConfigFunction,
extendedYAxisConfigFunction,
legendConfigFunction, legendConfigFunction,
gridlinesConfigFunction,
axisExtentConfigFunction, axisExtentConfigFunction,
tickLabelsConfigFunction,
referenceLineFunction, referenceLineFunction,
referenceLineLayerFunction, referenceLineLayerFunction,
annotationLayerFunction, annotationLayerFunction,
labelsOrientationConfigFunction,
axisTitlesVisibilityConfigFunction,
extendedAnnotationLayerFunction, extendedAnnotationLayerFunction,
referenceLineDecorationConfigFunction,
} from '../common/expression_functions'; } from '../common/expression_functions';
import { GetStartDepsFn, getXyChartRenderer } from './expression_renderers'; import { GetStartDepsFn, getXyChartRenderer } from './expression_renderers';
@ -55,18 +53,16 @@ export class ExpressionXyPlugin {
{ expressions, charts }: SetupDeps { expressions, charts }: SetupDeps
): ExpressionXyPluginSetup { ): ExpressionXyPluginSetup {
expressions.registerFunction(yAxisConfigFunction); expressions.registerFunction(yAxisConfigFunction);
expressions.registerFunction(extendedYAxisConfigFunction); expressions.registerFunction(dataDecorationConfigFunction);
expressions.registerFunction(referenceLineDecorationConfigFunction);
expressions.registerFunction(legendConfigFunction); expressions.registerFunction(legendConfigFunction);
expressions.registerFunction(gridlinesConfigFunction);
expressions.registerFunction(extendedDataLayerFunction); expressions.registerFunction(extendedDataLayerFunction);
expressions.registerFunction(axisExtentConfigFunction); expressions.registerFunction(axisExtentConfigFunction);
expressions.registerFunction(tickLabelsConfigFunction); expressions.registerFunction(xAxisConfigFunction);
expressions.registerFunction(annotationLayerFunction); expressions.registerFunction(annotationLayerFunction);
expressions.registerFunction(extendedAnnotationLayerFunction); expressions.registerFunction(extendedAnnotationLayerFunction);
expressions.registerFunction(labelsOrientationConfigFunction);
expressions.registerFunction(referenceLineFunction); expressions.registerFunction(referenceLineFunction);
expressions.registerFunction(referenceLineLayerFunction); expressions.registerFunction(referenceLineLayerFunction);
expressions.registerFunction(axisTitlesVisibilityConfigFunction);
expressions.registerFunction(xyVisFunction); expressions.registerFunction(xyVisFunction);
expressions.registerFunction(layeredXyVisFunction); expressions.registerFunction(layeredXyVisFunction);

View file

@ -11,16 +11,14 @@ import { CoreSetup, CoreStart, Plugin } from '@kbn/core/server';
import { ExpressionXyPluginSetup, ExpressionXyPluginStart } from './types'; import { ExpressionXyPluginSetup, ExpressionXyPluginStart } from './types';
import { import {
xyVisFunction, xyVisFunction,
yAxisConfigFunction,
extendedYAxisConfigFunction,
legendConfigFunction, legendConfigFunction,
gridlinesConfigFunction, dataDecorationConfigFunction,
xAxisConfigFunction,
yAxisConfigFunction,
referenceLineDecorationConfigFunction,
axisExtentConfigFunction, axisExtentConfigFunction,
tickLabelsConfigFunction,
annotationLayerFunction, annotationLayerFunction,
labelsOrientationConfigFunction,
referenceLineFunction, referenceLineFunction,
axisTitlesVisibilityConfigFunction,
extendedDataLayerFunction, extendedDataLayerFunction,
referenceLineLayerFunction, referenceLineLayerFunction,
layeredXyVisFunction, layeredXyVisFunction,
@ -33,18 +31,16 @@ export class ExpressionXyPlugin
{ {
public setup(core: CoreSetup, { expressions }: SetupDeps) { public setup(core: CoreSetup, { expressions }: SetupDeps) {
expressions.registerFunction(yAxisConfigFunction); expressions.registerFunction(yAxisConfigFunction);
expressions.registerFunction(extendedYAxisConfigFunction); expressions.registerFunction(dataDecorationConfigFunction);
expressions.registerFunction(xAxisConfigFunction);
expressions.registerFunction(referenceLineDecorationConfigFunction);
expressions.registerFunction(legendConfigFunction); expressions.registerFunction(legendConfigFunction);
expressions.registerFunction(gridlinesConfigFunction);
expressions.registerFunction(extendedDataLayerFunction); expressions.registerFunction(extendedDataLayerFunction);
expressions.registerFunction(axisExtentConfigFunction); expressions.registerFunction(axisExtentConfigFunction);
expressions.registerFunction(tickLabelsConfigFunction);
expressions.registerFunction(annotationLayerFunction); expressions.registerFunction(annotationLayerFunction);
expressions.registerFunction(extendedAnnotationLayerFunction); expressions.registerFunction(extendedAnnotationLayerFunction);
expressions.registerFunction(labelsOrientationConfigFunction);
expressions.registerFunction(referenceLineFunction); expressions.registerFunction(referenceLineFunction);
expressions.registerFunction(referenceLineLayerFunction); expressions.registerFunction(referenceLineLayerFunction);
expressions.registerFunction(axisTitlesVisibilityConfigFunction);
expressions.registerFunction(xyVisFunction); expressions.registerFunction(xyVisFunction);
expressions.registerFunction(layeredXyVisFunction); expressions.registerFunction(layeredXyVisFunction);
} }

View file

@ -18,6 +18,9 @@ export type {
ValidLayer, ValidLayer,
XYDataLayerConfig, XYDataLayerConfig,
XYAnnotationLayerConfig, XYAnnotationLayerConfig,
YAxisMode,
SeriesType,
YConfig,
} from './xy_visualization/types'; } from './xy_visualization/types';
export type { export type {
DatasourcePublicAPI, DatasourcePublicAPI,
@ -74,13 +77,10 @@ export type {
} from './indexpattern_datasource/types'; } from './indexpattern_datasource/types';
export type { export type {
XYArgs, XYArgs,
ExtendedYConfig,
XYRender, XYRender,
LayerType, LayerType,
YAxisMode,
LineStyle, LineStyle,
FillStyle, FillStyle,
SeriesType,
YScaleType, YScaleType,
XScaleType, XScaleType,
AxisConfig, AxisConfig,
@ -88,7 +88,6 @@ export type {
XYChartProps, XYChartProps,
LegendConfig, LegendConfig,
IconPosition, IconPosition,
ExtendedYConfigResult,
DataLayerArgs, DataLayerArgs,
ValueLabelMode, ValueLabelMode,
AxisExtentMode, AxisExtentMode,
@ -97,14 +96,9 @@ export type {
AxisExtentConfig, AxisExtentConfig,
LegendConfigResult, LegendConfigResult,
AxesSettingsConfig, AxesSettingsConfig,
GridlinesConfigResult,
TickLabelsConfigResult,
AxisExtentConfigResult, AxisExtentConfigResult,
ReferenceLineLayerArgs, ReferenceLineLayerArgs,
LabelsOrientationConfig,
ReferenceLineLayerConfig, ReferenceLineLayerConfig,
LabelsOrientationConfigResult,
AxisTitlesVisibilityConfigResult,
} from '@kbn/expression-xy-plugin/common'; } from '@kbn/expression-xy-plugin/common';
export type { LensEmbeddableInput, LensSavedObjectAttributes, Embeddable } from './embeddable'; export type { LensEmbeddableInput, LensSavedObjectAttributes, Embeddable } from './embeddable';

View file

@ -8,7 +8,7 @@
import React, { useCallback, useMemo } from 'react'; import React, { useCallback, useMemo } from 'react';
import { EuiSpacer, EuiFormRow } from '@elastic/eui'; import { EuiSpacer, EuiFormRow } from '@elastic/eui';
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import { AxesSettingsConfig } from '@kbn/expression-xy-plugin/common'; import { AxesSettingsConfig } from '../xy_visualization/types';
import { LabelMode, useDebouncedValue, VisLabel } from '.'; import { LabelMode, useDebouncedValue, VisLabel } from '.';
type AxesSettingsConfigKeys = keyof AxesSettingsConfig; type AxesSettingsConfigKeys = keyof AxesSettingsConfig;

View file

@ -5,28 +5,6 @@ Object {
"chain": Array [ "chain": Array [
Object { Object {
"arguments": Object { "arguments": Object {
"axisTitlesVisibilitySettings": Array [
Object {
"chain": Array [
Object {
"arguments": Object {
"x": Array [
true,
],
"yLeft": Array [
true,
],
"yRight": Array [
true,
],
},
"function": "axisTitlesVisibilityConfig",
"type": "function",
},
],
"type": "expression",
},
],
"curveType": Array [ "curveType": Array [
"LINEAR", "LINEAR",
], ],
@ -42,53 +20,9 @@ Object {
"fittingFunction": Array [ "fittingFunction": Array [
"Carry", "Carry",
], ],
"gridlinesVisibilitySettings": Array [
Object {
"chain": Array [
Object {
"arguments": Object {
"x": Array [
false,
],
"yLeft": Array [
true,
],
"yRight": Array [
true,
],
},
"function": "gridlinesConfig",
"type": "function",
},
],
"type": "expression",
},
],
"hideEndzones": Array [ "hideEndzones": Array [
true, true,
], ],
"labelsOrientation": Array [
Object {
"chain": Array [
Object {
"arguments": Object {
"x": Array [
0,
],
"yLeft": Array [
-90,
],
"yRight": Array [
-45,
],
},
"function": "labelsOrientationConfig",
"type": "function",
},
],
"type": "expression",
},
],
"layers": Array [ "layers": Array [
Object { Object {
"chain": Array [ "chain": Array [
@ -101,12 +35,16 @@ Object {
"columnToLabel": Array [ "columnToLabel": Array [
"{\\"b\\":\\"col_b\\",\\"c\\":\\"col_c\\",\\"d\\":\\"col_d\\"}", "{\\"b\\":\\"col_b\\",\\"c\\":\\"col_c\\",\\"d\\":\\"col_d\\"}",
], ],
"decorations": Array [],
"hide": Array [ "hide": Array [
false, false,
], ],
"isHistogram": Array [ "isHistogram": Array [
false, false,
], ],
"isHorizontal": Array [],
"isPercentage": Array [],
"isStacked": Array [],
"layerId": Array [ "layerId": Array [
"first", "first",
], ],
@ -144,7 +82,6 @@ Object {
"xScaleType": Array [ "xScaleType": Array [
"linear", "linear",
], ],
"yConfig": Array [],
}, },
"function": "extendedDataLayer", "function": "extendedDataLayer",
"type": "function", "type": "function",
@ -182,103 +119,138 @@ Object {
"type": "expression", "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 [ "valueLabels": Array [
"hide", "hide",
], ],
"valuesInLegend": Array [ "valuesInLegend": Array [
false, false,
], ],
"xExtent": Array [ "xAxisConfig": Array [
Object { Object {
"chain": Array [ "chain": Array [
Object { Object {
"arguments": Object { "arguments": Object {
"lowerBound": Array [], "extent": Array [],
"mode": Array [], "id": Array [
"upperBound": Array [], "x",
],
"labelsOrientation": Array [
0,
],
"position": Array [
"bottom",
],
"showGridLines": Array [
false,
],
"showLabels": Array [
false,
],
"showTitle": Array [
true,
],
"title": Array [
"",
],
}, },
"function": "axisExtentConfig", "function": "xAxisConfig",
"type": "function", "type": "function",
}, },
], ],
"type": "expression", "type": "expression",
}, },
], ],
"xTitle": Array [ "yAxisConfigs": Array [
"",
],
"yLeftExtent": Array [
Object { Object {
"chain": Array [ "chain": Array [
Object { Object {
"arguments": Object { "arguments": Object {
"lowerBound": Array [], "extent": Array [],
"mode": Array [], "id": Array [],
"upperBound": Array [], "labelsOrientation": Array [
-90,
],
"position": Array [
"left",
],
"scaleType": Array [
"linear",
],
"showGridLines": Array [
true,
],
"showLabels": Array [
true,
],
"showTitle": Array [
true,
],
"title": Array [
"",
],
}, },
"function": "axisExtentConfig", "function": "yAxisConfig",
"type": "function", "type": "function",
}, },
], ],
"type": "expression", "type": "expression",
}, },
],
"yLeftScale": Array [
"linear",
],
"yRightExtent": Array [
Object { Object {
"chain": Array [ "chain": Array [
Object { Object {
"arguments": Object { "arguments": Object {
"lowerBound": Array [ "extent": Array [
123, Object {
"chain": Array [
Object {
"arguments": Object {
"lowerBound": Array [
123,
],
"mode": Array [
"custom",
],
"upperBound": Array [
456,
],
},
"function": "axisExtentConfig",
"type": "function",
},
],
"type": "expression",
},
], ],
"mode": Array [ "id": Array [],
"custom", "labelsOrientation": Array [
-45,
], ],
"upperBound": Array [ "position": Array [
456, "right",
],
"scaleType": Array [
"linear",
],
"showGridLines": Array [
true,
],
"showLabels": Array [
true,
],
"showTitle": Array [
true,
],
"title": Array [
"",
], ],
}, },
"function": "axisExtentConfig", "function": "yAxisConfig",
"type": "function", "type": "function",
}, },
], ],
"type": "expression", "type": "expression",
}, },
], ],
"yRightScale": Array [
"linear",
],
"yRightTitle": Array [
"",
],
"yTitle": Array [
"",
],
}, },
"function": "layeredXyVis", "function": "layeredXyVis",
"type": "function", "type": "function",

View file

@ -7,13 +7,18 @@
import { groupBy, partition } from 'lodash'; import { groupBy, partition } from 'lodash';
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import type { YAxisMode, ExtendedYConfig } from '@kbn/expression-xy-plugin/common';
import { Datatable } from '@kbn/expressions-plugin/public'; import { Datatable } from '@kbn/expressions-plugin/public';
import { layerTypes } from '../../common'; import { layerTypes } from '../../common';
import type { DatasourceLayers, FramePublicAPI, Visualization } from '../types'; import type { DatasourceLayers, FramePublicAPI, Visualization } from '../types';
import { groupAxesByType } from './axes_configuration'; import { groupAxesByType } from './axes_configuration';
import { isHorizontalChart, isPercentageSeries, isStackedChart } from './state_helpers'; import { isHorizontalChart, isPercentageSeries, isStackedChart } from './state_helpers';
import type { XYState, XYDataLayerConfig, XYReferenceLineLayerConfig } from './types'; import type {
XYState,
XYDataLayerConfig,
XYReferenceLineLayerConfig,
YAxisMode,
YConfig,
} from './types';
import { import {
checkScaleOperation, checkScaleOperation,
getAxisName, getAxisName,
@ -35,7 +40,7 @@ export interface ReferenceLineBase {
* * what groups are current defined in data layers * * what groups are current defined in data layers
* * what existing reference line are currently defined in reference layers * * what existing reference line are currently defined in reference layers
*/ */
export function getGroupsToShow<T extends ReferenceLineBase & { config?: ExtendedYConfig[] }>( export function getGroupsToShow<T extends ReferenceLineBase & { config?: YConfig[] }>(
referenceLayers: T[], referenceLayers: T[],
state: XYState | undefined, state: XYState | undefined,
datasourceLayers: DatasourceLayers, datasourceLayers: DatasourceLayers,

View file

@ -6,13 +6,14 @@
*/ */
import { EuiIconType } from '@elastic/eui/src/components/icon/icon'; 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 type { FramePublicAPI, DatasourcePublicAPI } from '../types';
import { import {
visualizationTypes, visualizationTypes,
XYLayerConfig, XYLayerConfig,
XYDataLayerConfig, XYDataLayerConfig,
XYReferenceLineLayerConfig, XYReferenceLineLayerConfig,
SeriesType,
YConfig,
} from './types'; } from './types';
import { getDataLayers, isAnnotationsLayer, isDataLayer } from './visualization_helpers'; import { getDataLayers, isAnnotationsLayer, isDataLayer } from './visualization_helpers';
@ -58,8 +59,7 @@ export const getSeriesColor = (layer: XYLayerConfig, accessor: string) => {
return null; return null;
} }
return ( return (
layer?.yConfig?.find((yConfig: ExtendedYConfig) => yConfig.forAccessor === accessor)?.color || layer?.yConfig?.find((yConfig: YConfig) => yConfig.forAccessor === accessor)?.color || null
null
); );
}; };

View file

@ -159,13 +159,23 @@ describe('#toExpression', () => {
undefined, undefined,
datasourceExpressionsByLayers datasourceExpressionsByLayers
) as Ast; ) as Ast;
expect( expect((expression.chain[0].arguments.yAxisConfigs[0] as Ast).chain[0].arguments).toEqual(
(expression.chain[0].arguments.axisTitlesVisibilitySettings[0] as Ast).chain[0].arguments expect.objectContaining({
).toEqual({ showTitle: [true],
x: [true], position: ['left'],
yLeft: [true], })
yRight: [true], );
}); expect((expression.chain[0].arguments.yAxisConfigs[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', () => { it('should generate an expression without x accessor', () => {
@ -244,9 +254,6 @@ describe('#toExpression', () => {
expect(mockDatasource.publicAPIMock.getOperationForColumnId).toHaveBeenCalledWith('b'); expect(mockDatasource.publicAPIMock.getOperationForColumnId).toHaveBeenCalledWith('b');
expect(mockDatasource.publicAPIMock.getOperationForColumnId).toHaveBeenCalledWith('c'); expect(mockDatasource.publicAPIMock.getOperationForColumnId).toHaveBeenCalledWith('c');
expect(mockDatasource.publicAPIMock.getOperationForColumnId).toHaveBeenCalledWith('d'); 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( expect(
(expression.chain[0].arguments.layers[0] as Ast).chain[0].arguments.columnToLabel (expression.chain[0].arguments.layers[0] as Ast).chain[0].arguments.columnToLabel
).toEqual([ ).toEqual([
@ -279,13 +286,23 @@ describe('#toExpression', () => {
undefined, undefined,
datasourceExpressionsByLayers datasourceExpressionsByLayers
) as Ast; ) as Ast;
expect( expect((expression.chain[0].arguments.yAxisConfigs[0] as Ast).chain[0].arguments).toEqual(
(expression.chain[0].arguments.tickLabelsVisibilitySettings[0] as Ast).chain[0].arguments expect.objectContaining({
).toEqual({ showLabels: [true],
x: [true], position: ['left'],
yLeft: [true], })
yRight: [true], );
}); expect((expression.chain[0].arguments.yAxisConfigs[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', () => { it('should default the tick labels orientation settings to 0', () => {
@ -309,11 +326,23 @@ describe('#toExpression', () => {
undefined, undefined,
datasourceExpressionsByLayers datasourceExpressionsByLayers
) as Ast; ) as Ast;
expect((expression.chain[0].arguments.labelsOrientation[0] as Ast).chain[0].arguments).toEqual({ expect((expression.chain[0].arguments.yAxisConfigs[0] as Ast).chain[0].arguments).toEqual(
x: [0], expect.objectContaining({
yLeft: [0], labelsOrientation: [0],
yRight: [0], position: ['left'],
}); })
);
expect((expression.chain[0].arguments.yAxisConfigs[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', () => { it('should default the gridlines visibility settings to true', () => {
@ -337,13 +366,23 @@ describe('#toExpression', () => {
undefined, undefined,
datasourceExpressionsByLayers datasourceExpressionsByLayers
) as Ast; ) as Ast;
expect( expect((expression.chain[0].arguments.yAxisConfigs[0] as Ast).chain[0].arguments).toEqual(
(expression.chain[0].arguments.gridlinesVisibilitySettings[0] as Ast).chain[0].arguments expect.objectContaining({
).toEqual({ showGridLines: [true],
x: [true], position: ['left'],
yLeft: [true], })
yRight: [true], );
}); expect((expression.chain[0].arguments.yAxisConfigs[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', () => { it('should correctly report the valueLabels visibility settings', () => {
@ -488,8 +527,9 @@ describe('#toExpression', () => {
) as Ast; ) as Ast;
function getYConfigColorForLayer(ast: Ast, index: number) { function getYConfigColorForLayer(ast: Ast, index: number) {
return ((ast.chain[0].arguments.layers[index] as Ast).chain[0].arguments.yConfig[0] as Ast) return (
.chain[0].arguments.color; (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, 0)).toEqual([]);
expect(getYConfigColorForLayer(expression, 1)).toEqual([defaultReferenceLineColor]); expect(getYConfigColorForLayer(expression, 1)).toEqual([defaultReferenceLineColor]);

View file

@ -8,15 +8,15 @@
import { Ast, AstFunction } from '@kbn/interpreter'; import { Ast, AstFunction } from '@kbn/interpreter';
import { Position, ScaleType } from '@elastic/charts'; import { Position, ScaleType } from '@elastic/charts';
import type { PaletteRegistry } from '@kbn/coloring'; import type { PaletteRegistry } from '@kbn/coloring';
import { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public'; import { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public';
import type { YConfig, ExtendedYConfig } from '@kbn/expression-xy-plugin/common';
import { LegendSize } from '@kbn/visualizations-plugin/public'; import { LegendSize } from '@kbn/visualizations-plugin/public';
import { import {
State, State,
YConfig,
XYDataLayerConfig, XYDataLayerConfig,
XYReferenceLineLayerConfig, XYReferenceLineLayerConfig,
XYAnnotationLayerConfig, XYAnnotationLayerConfig,
AxisConfig,
} from './types'; } from './types';
import type { ValidXYDataLayerConfig } from './types'; import type { ValidXYDataLayerConfig } from './types';
import { OperationMetadata, DatasourcePublicAPI, DatasourceLayers } from '../types'; import { OperationMetadata, DatasourcePublicAPI, DatasourceLayers } from '../types';
@ -191,6 +191,54 @@ export const buildExpression = (
return null; return null;
} }
const isLeftAxis = validDataLayers.some(({ yConfig }) =>
yConfig?.some((config) => config.axisMode === Position.Left)
);
const isRightAxis = validDataLayers.some(({ yConfig }) =>
yConfig?.some((config) => config.axisMode === Position.Right)
);
const yAxisConfigs: AxisConfig[] = [
{
position: Position.Left,
extent: state?.yLeftExtent,
showTitle: state?.axisTitlesVisibilitySettings?.yLeft ?? true,
title: state.yTitle || '',
showLabels: state?.tickLabelsVisibilitySettings?.yLeft ?? true,
showGridLines: state?.gridlinesVisibilitySettings?.yLeft ?? true,
labelsOrientation: state?.labelsOrientation?.yLeft ?? 0,
scaleType: state.yLeftScale || 'linear',
},
{
position: Position.Right,
extent: state?.yRightExtent,
showTitle: state?.axisTitlesVisibilitySettings?.yRight ?? true,
title: state.yRightTitle || '',
showLabels: state?.tickLabelsVisibilitySettings?.yRight ?? true,
showGridLines: state?.gridlinesVisibilitySettings?.yRight ?? true,
labelsOrientation: state?.labelsOrientation?.yRight ?? 0,
scaleType: state.yRightScale || 'linear',
},
];
if (isLeftAxis) {
yAxisConfigs.push({
id: Position.Left,
position: Position.Left,
// we need also settings from global config here so that default's doesn't override it
...yAxisConfigs[0],
});
}
if (isRightAxis) {
yAxisConfigs.push({
id: Position.Right,
position: Position.Right,
// we need also settings from global config here so that default's doesn't override it
...yAxisConfigs[1],
});
}
return { return {
type: 'expression', type: 'expression',
chain: [ chain: [
@ -198,9 +246,6 @@ export const buildExpression = (
type: 'function', type: 'function',
function: 'layeredXyVis', function: 'layeredXyVis',
arguments: { arguments: {
xTitle: [state.xTitle || ''],
yTitle: [state.yTitle || ''],
yRightTitle: [state.yRightTitle || ''],
legend: [ legend: [
{ {
type: 'expression', type: 'expression',
@ -252,82 +297,36 @@ export const buildExpression = (
emphasizeFitting: [state.emphasizeFitting || false], emphasizeFitting: [state.emphasizeFitting || false],
curveType: [state.curveType || 'LINEAR'], curveType: [state.curveType || 'LINEAR'],
fillOpacity: [state.fillOpacity || 0.3], fillOpacity: [state.fillOpacity || 0.3],
xExtent: [axisExtentConfigToExpression(state.xExtent)],
yLeftExtent: [axisExtentConfigToExpression(state.yLeftExtent)],
yRightExtent: [axisExtentConfigToExpression(state.yRightExtent)],
yLeftScale: [state.yLeftScale || 'linear'],
yRightScale: [state.yRightScale || 'linear'],
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: [
{
type: 'expression',
chain: [
{
type: 'function',
function: 'labelsOrientationConfig',
arguments: {
x: [state?.labelsOrientation?.x ?? 0],
yLeft: [state?.labelsOrientation?.yLeft ?? 0],
yRight: [state?.labelsOrientation?.yRight ?? 0],
},
},
],
},
],
valueLabels: [state?.valueLabels || 'hide'], valueLabels: [state?.valueLabels || 'hide'],
hideEndzones: [state?.hideEndzones || false], hideEndzones: [state?.hideEndzones || false],
valuesInLegend: [state?.valuesInLegend || false], valuesInLegend: [state?.valuesInLegend || false],
yAxisConfigs: [...yAxisConfigsToExpression(yAxisConfigs)],
xAxisConfig: [
{
type: 'expression',
chain: [
{
type: 'function',
function: 'xAxisConfig',
arguments: {
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],
extent: state.xExtent ? [axisExtentConfigToExpression(state.xExtent)] : [],
},
},
],
},
],
layers: [ layers: [
...validDataLayers.map((layer) => ...validDataLayers.map((layer) =>
dataLayerToExpression( dataLayerToExpression(
layer, layer,
yAxisConfigs,
datasourceLayers[layer.layerId], datasourceLayers[layer.layerId],
metadata, metadata,
paletteService, paletteService,
@ -351,6 +350,29 @@ export const buildExpression = (
}; };
}; };
const yAxisConfigsToExpression = (yAxisConfigs: AxisConfig[]): Ast[] => {
return yAxisConfigs.map((axis) => ({
type: 'expression',
chain: [
{
type: 'function',
function: 'yAxisConfig',
arguments: {
id: axis.id ? [axis.id] : [],
position: axis.position ? [axis.position] : [],
extent: axis.extent ? [axisExtentConfigToExpression(axis.extent)] : [],
showTitle: [axis.showTitle ?? true],
title: axis.title !== undefined ? [axis.title] : [],
showLabels: [axis.showLabels ?? true],
showGridLines: [axis.showGridLines ?? true],
labelsOrientation: axis.labelsOrientation !== undefined ? [axis.labelsOrientation] : [],
scaleType: axis.scaleType ? [axis.scaleType] : [],
},
},
],
}));
};
const referenceLineLayerToExpression = ( const referenceLineLayerToExpression = (
layer: XYReferenceLineLayerConfig, layer: XYReferenceLineLayerConfig,
datasourceLayer: DatasourcePublicAPI, datasourceLayer: DatasourcePublicAPI,
@ -364,9 +386,9 @@ const referenceLineLayerToExpression = (
function: 'referenceLineLayer', function: 'referenceLineLayer',
arguments: { arguments: {
layerId: [layer.layerId], layerId: [layer.layerId],
yConfig: layer.yConfig decorations: layer.yConfig
? layer.yConfig.map((yConfig) => ? layer.yConfig.map((yConfig) =>
extendedYConfigToExpression(yConfig, defaultReferenceLineColor) extendedYConfigToRLDecorationConfigExpression(yConfig, defaultReferenceLineColor)
) )
: [], : [],
accessors: layer.accessors, accessors: layer.accessors,
@ -402,6 +424,7 @@ const annotationLayerToExpression = (
const dataLayerToExpression = ( const dataLayerToExpression = (
layer: ValidXYDataLayerConfig, layer: ValidXYDataLayerConfig,
yAxisConfigs: AxisConfig[],
datasourceLayer: DatasourcePublicAPI, datasourceLayer: DatasourcePublicAPI,
metadata: Record<string, Record<string, OperationMetadata | null>>, metadata: Record<string, Record<string, OperationMetadata | null>>,
paletteService: PaletteRegistry, paletteService: PaletteRegistry,
@ -418,6 +441,12 @@ const dataLayerToExpression = (
xAxisOperation.scale !== 'ordinal' 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 { return {
type: 'expression', type: 'expression',
chain: [ chain: [
@ -430,11 +459,16 @@ const dataLayerToExpression = (
xAccessor: layer.xAccessor ? [layer.xAccessor] : [], xAccessor: layer.xAccessor ? [layer.xAccessor] : [],
xScaleType: [getScaleType(metadata[layer.layerId][layer.xAccessor], ScaleType.Linear)], xScaleType: [getScaleType(metadata[layer.layerId][layer.xAccessor], ScaleType.Linear)],
isHistogram: [isHistogramDimension], isHistogram: [isHistogramDimension],
isPercentage: isPercentage ? [isPercentage] : [],
isStacked: isStacked ? [isStacked] : [],
isHorizontal: isHorizontal ? [isHorizontal] : [],
splitAccessor: layer.collapseFn || !layer.splitAccessor ? [] : [layer.splitAccessor], splitAccessor: layer.collapseFn || !layer.splitAccessor ? [] : [layer.splitAccessor],
yConfig: layer.yConfig decorations: layer.yConfig
? layer.yConfig.map((yConfig) => yConfigToExpression(yConfig)) ? layer.yConfig.map((yConfig) =>
yConfigToDataDecorationConfigExpression(yConfig, yAxisConfigs)
)
: [], : [],
seriesType: [layer.seriesType], seriesType: [seriesType],
accessors: layer.accessors, accessors: layer.accessors,
columnToLabel: [JSON.stringify(columnToLabel)], columnToLabel: [JSON.stringify(columnToLabel)],
...(datasourceExpression ...(datasourceExpression
@ -493,16 +527,21 @@ const dataLayerToExpression = (
}; };
}; };
const yConfigToExpression = (yConfig: YConfig, defaultColor?: string): Ast => { const yConfigToDataDecorationConfigExpression = (
yConfig: YConfig,
yAxisConfigs: AxisConfig[],
defaultColor?: string
): Ast => {
const axisId = yAxisConfigs.find((axis) => axis.id && axis.position === yConfig.axisMode)?.id;
return { return {
type: 'expression', type: 'expression',
chain: [ chain: [
{ {
type: 'function', type: 'function',
function: 'yConfig', function: 'dataDecorationConfig',
arguments: { arguments: {
axisId: axisId ? [axisId] : [],
forAccessor: [yConfig.forAccessor], forAccessor: [yConfig.forAccessor],
axisMode: yConfig.axisMode ? [yConfig.axisMode] : [],
color: yConfig.color ? [yConfig.color] : defaultColor ? [defaultColor] : [], color: yConfig.color ? [yConfig.color] : defaultColor ? [defaultColor] : [],
}, },
}, },
@ -510,16 +549,19 @@ const yConfigToExpression = (yConfig: YConfig, defaultColor?: string): Ast => {
}; };
}; };
const extendedYConfigToExpression = (yConfig: ExtendedYConfig, defaultColor?: string): Ast => { const extendedYConfigToRLDecorationConfigExpression = (
yConfig: YConfig,
defaultColor?: string
): Ast => {
return { return {
type: 'expression', type: 'expression',
chain: [ chain: [
{ {
type: 'function', type: 'function',
function: 'extendedYConfig', function: 'referenceLineDecorationConfig',
arguments: { arguments: {
forAccessor: [yConfig.forAccessor], forAccessor: [yConfig.forAccessor],
axisMode: yConfig.axisMode ? [yConfig.axisMode] : [], position: yConfig.axisMode ? [yConfig.axisMode] : [],
color: yConfig.color ? [yConfig.color] : defaultColor ? [defaultColor] : [], color: yConfig.color ? [yConfig.color] : defaultColor ? [defaultColor] : [],
lineStyle: [yConfig.lineStyle || 'solid'], lineStyle: [yConfig.lineStyle || 'solid'],
lineWidth: [yConfig.lineWidth || 1], lineWidth: [yConfig.lineWidth || 1],

View file

@ -6,20 +6,20 @@
*/ */
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import { $Values } from '@kbn/utility-types';
import type { PaletteOutput } from '@kbn/coloring'; import type { PaletteOutput } from '@kbn/coloring';
import type { import type {
SeriesType,
LegendConfig, LegendConfig,
AxisExtentConfig, AxisExtentConfig,
XYCurveType, XYCurveType,
AxesSettingsConfig,
FittingFunction, FittingFunction,
LabelsOrientationConfig,
EndValue, EndValue,
ExtendedYConfig,
YConfig,
YScaleType, YScaleType,
XScaleType, XScaleType,
LineStyle,
IconPosition,
FillStyle,
YAxisConfig,
} from '@kbn/expression-xy-plugin/common'; } from '@kbn/expression-xy-plugin/common';
import { EventAnnotationConfig } from '@kbn/event-annotation-plugin/common'; import { EventAnnotationConfig } from '@kbn/event-annotation-plugin/common';
import { LensIconChartArea } from '../assets/chart_area'; import { LensIconChartArea } from '../assets/chart_area';
@ -36,6 +36,56 @@ import { LensIconChartLine } from '../assets/chart_line';
import type { VisualizationType, Suggestion } from '../types'; import type { VisualizationType, Suggestion } from '../types';
import type { ValueLabelConfig } from '../../common/types'; import type { ValueLabelConfig } from '../../common/types';
export const YAxisModes = {
AUTO: 'auto',
LEFT: 'left',
RIGHT: 'right',
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<typeof YAxisModes>;
export type SeriesType = $Values<typeof SeriesTypes>;
export interface AxesSettingsConfig {
x: boolean;
yRight: boolean;
yLeft: boolean;
}
export interface AxisConfig extends Omit<YAxisConfig, 'extent'> {
extent?: AxisExtentConfig;
}
export interface LabelsOrientationConfig {
x: number;
yLeft: number;
yRight: number;
}
export interface YConfig {
forAccessor: string;
color?: string;
icon?: string;
lineWidth?: number;
lineStyle?: LineStyle;
fill?: FillStyle;
iconPosition?: IconPosition;
textVisibility?: boolean;
axisMode?: YAxisMode;
}
export interface XYDataLayerConfig { export interface XYDataLayerConfig {
layerId: string; layerId: string;
accessors: string[]; accessors: string[];
@ -55,7 +105,7 @@ export interface XYDataLayerConfig {
export interface XYReferenceLineLayerConfig { export interface XYReferenceLineLayerConfig {
layerId: string; layerId: string;
accessors: string[]; accessors: string[];
yConfig?: ExtendedYConfig[]; yConfig?: YConfig[];
layerType: 'referenceLine'; layerType: 'referenceLine';
} }

View file

@ -15,9 +15,9 @@ import type {
XYLayerConfig, XYLayerConfig,
XYDataLayerConfig, XYDataLayerConfig,
XYReferenceLineLayerConfig, XYReferenceLineLayerConfig,
SeriesType,
} from './types'; } from './types';
import { createDatatableUtilitiesMock } from '@kbn/data-plugin/common/mocks'; import { createDatatableUtilitiesMock } from '@kbn/data-plugin/common/mocks';
import type { SeriesType } from '@kbn/expression-xy-plugin/common';
import { layerTypes } from '../../common'; import { layerTypes } from '../../common';
import { createMockDatasource, createMockFramePublicAPI } from '../mocks'; import { createMockDatasource, createMockFramePublicAPI } from '../mocks';
import { LensIconChartBar } from '../assets/chart_bar'; import { LensIconChartBar } from '../assets/chart_bar';

View file

@ -17,18 +17,22 @@ import { ThemeServiceStart } from '@kbn/core/public';
import { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public'; import { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public';
import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public';
import { VIS_EVENT_TO_TRIGGER } from '@kbn/visualizations-plugin/public'; import { VIS_EVENT_TO_TRIGGER } from '@kbn/visualizations-plugin/public';
import { import { FillStyle } from '@kbn/expression-xy-plugin/common';
FillStyle,
SeriesType,
YAxisMode,
ExtendedYConfig,
} from '@kbn/expression-xy-plugin/common';
import { getSuggestions } from './xy_suggestions'; import { getSuggestions } from './xy_suggestions';
import { XyToolbar } from './xy_config_panel'; import { XyToolbar } from './xy_config_panel';
import { DimensionEditor } from './xy_config_panel/dimension_editor'; import { DimensionEditor } from './xy_config_panel/dimension_editor';
import { LayerHeader } from './xy_config_panel/layer_header'; import { LayerHeader } from './xy_config_panel/layer_header';
import { Visualization, AccessorConfig, FramePublicAPI } from '../types'; import type { Visualization, AccessorConfig, FramePublicAPI } from '../types';
import { State, visualizationTypes, XYSuggestion, XYLayerConfig, XYDataLayerConfig } from './types'; import {
State,
visualizationTypes,
XYSuggestion,
XYLayerConfig,
XYDataLayerConfig,
YConfig,
YAxisMode,
SeriesType,
} from './types';
import { layerTypes } from '../../common'; import { layerTypes } from '../../common';
import { isHorizontalChart } from './state_helpers'; import { isHorizontalChart } from './state_helpers';
import { toExpression, toPreviewExpression, getSortedAccessors } from './to_expression'; import { toExpression, toPreviewExpression, getSortedAccessors } from './to_expression';
@ -360,7 +364,7 @@ export const getXyVisualization = ({
} }
const isReferenceLine = metrics.some((metric) => metric.agg === 'static_value'); const isReferenceLine = metrics.some((metric) => metric.agg === 'static_value');
const axisMode = axisPosition as YAxisMode; const axisMode = axisPosition as YAxisMode;
const yConfig = metrics.map<ExtendedYConfig>((metric, idx) => { const yConfig = metrics.map<YConfig>((metric, idx) => {
return { return {
color: metric.color, color: metric.color,
forAccessor: metric.accessor ?? foundLayer.accessors[idx], forAccessor: metric.accessor ?? foundLayer.accessors[idx],

View file

@ -7,7 +7,6 @@
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import { uniq } from 'lodash'; import { uniq } from 'lodash';
import { SeriesType } from '@kbn/expression-xy-plugin/common';
import { DatasourceLayers, OperationMetadata, VisualizationType } from '../types'; import { DatasourceLayers, OperationMetadata, VisualizationType } from '../types';
import { import {
State, State,
@ -17,6 +16,7 @@ import {
XYLayerConfig, XYLayerConfig,
XYDataLayerConfig, XYDataLayerConfig,
XYReferenceLineLayerConfig, XYReferenceLineLayerConfig,
SeriesType,
} from './types'; } from './types';
import { isHorizontalChart } from './state_helpers'; import { isHorizontalChart } from './state_helpers';
import { layerTypes } from '..'; import { layerTypes } from '..';

View file

@ -16,9 +16,9 @@ import {
} from '@elastic/eui'; } from '@elastic/eui';
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import { isEqual } from 'lodash'; import { isEqual } from 'lodash';
import { AxesSettingsConfig, AxisExtentConfig, YScaleType } from '@kbn/expression-xy-plugin/common'; import { AxisExtentConfig, YScaleType } from '@kbn/expression-xy-plugin/common';
import { ToolbarButtonProps } from '@kbn/kibana-react-plugin/public'; import { ToolbarButtonProps } from '@kbn/kibana-react-plugin/public';
import { XYLayerConfig } from '../types'; import { XYLayerConfig, AxesSettingsConfig } from '../types';
import { import {
ToolbarPopover, ToolbarPopover,
useDebouncedValue, useDebouncedValue,

View file

@ -10,9 +10,8 @@ import { i18n } from '@kbn/i18n';
import { EuiButtonGroup, EuiFormRow, htmlIdGenerator } from '@elastic/eui'; import { EuiButtonGroup, EuiFormRow, htmlIdGenerator } from '@elastic/eui';
import type { PaletteRegistry } from '@kbn/coloring'; import type { PaletteRegistry } from '@kbn/coloring';
import type { DatatableUtilitiesService } from '@kbn/data-plugin/common'; import type { DatatableUtilitiesService } from '@kbn/data-plugin/common';
import { YAxisMode, ExtendedYConfig } from '@kbn/expression-xy-plugin/common';
import type { VisualizationDimensionEditorProps } from '../../types'; import type { VisualizationDimensionEditorProps } from '../../types';
import { State, XYState, XYDataLayerConfig } from '../types'; import { State, XYState, XYDataLayerConfig, YConfig, YAxisMode } from '../types';
import { FormatFactory } from '../../../common'; import { FormatFactory } from '../../../common';
import { getSeriesColor, isHorizontalChart } from '../state_helpers'; import { getSeriesColor, isHorizontalChart } from '../state_helpers';
import { ColorPicker } from './color_picker'; import { ColorPicker } from './color_picker';
@ -81,7 +80,7 @@ export function DataDimensionEditor(
const axisMode = localYConfig?.axisMode || 'auto'; const axisMode = localYConfig?.axisMode || 'auto';
const setConfig = useCallback( const setConfig = useCallback(
(yConfig: Partial<ExtendedYConfig> | undefined) => { (yConfig: Partial<YConfig> | undefined) => {
if (yConfig == null) { if (yConfig == null) {
return; return;
} }

View file

@ -9,10 +9,10 @@ import React, { memo, useCallback, useMemo, useState } from 'react';
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import { Position, ScaleType } from '@elastic/charts'; import { Position, ScaleType } from '@elastic/charts';
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { AxesSettingsConfig, AxisExtentConfig } from '@kbn/expression-xy-plugin/common'; import { AxisExtentConfig } from '@kbn/expression-xy-plugin/common';
import { LegendSize } from '@kbn/visualizations-plugin/public'; import { LegendSize } from '@kbn/visualizations-plugin/public';
import type { VisualizationToolbarProps, FramePublicAPI } from '../../types'; import type { VisualizationToolbarProps, FramePublicAPI } from '../../types';
import { State, XYState } from '../types'; import { State, XYState, AxesSettingsConfig } from '../types';
import { isHorizontalChart } from '../state_helpers'; import { isHorizontalChart } from '../state_helpers';
import { hasNumericHistogramDimension, LegendSettingsPopover } from '../../shared_components'; import { hasNumericHistogramDimension, LegendSettingsPopover } from '../../shared_components';
import { AxisSettingsPopover } from './axis_settings_popover'; import { AxisSettingsPopover } from './axis_settings_popover';

View file

@ -8,10 +8,9 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import { EuiIcon, EuiPopover, EuiSelectable, EuiText, EuiPopoverTitle } from '@elastic/eui'; import { EuiIcon, EuiPopover, EuiSelectable, EuiText, EuiPopoverTitle } from '@elastic/eui';
import { SeriesType } from '@kbn/expression-xy-plugin/common';
import { ToolbarButton } from '@kbn/kibana-react-plugin/public'; import { ToolbarButton } from '@kbn/kibana-react-plugin/public';
import type { VisualizationLayerWidgetProps, VisualizationType } from '../../types'; import type { VisualizationLayerWidgetProps, VisualizationType } from '../../types';
import { State, visualizationTypes } from '../types'; import { State, visualizationTypes, SeriesType } from '../types';
import { isHorizontalChart, isHorizontalSeries } from '../state_helpers'; import { isHorizontalChart, isHorizontalSeries } from '../state_helpers';
import { trackUiEvent } from '../../lens_ui_telemetry'; import { trackUiEvent } from '../../lens_ui_telemetry';
import { StaticHeader } from '../../shared_components'; import { StaticHeader } from '../../shared_components';

View file

@ -9,9 +9,9 @@ import React, { useCallback } from 'react';
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import { EuiButtonGroup, EuiFormRow } from '@elastic/eui'; import { EuiButtonGroup, EuiFormRow } from '@elastic/eui';
import type { PaletteRegistry } from '@kbn/coloring'; 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 type { VisualizationDimensionEditorProps } from '../../../types';
import { State, XYState, XYReferenceLineLayerConfig } from '../../types'; import { State, XYState, XYReferenceLineLayerConfig, YConfig } from '../../types';
import { FormatFactory } from '../../../../common'; import { FormatFactory } from '../../../../common';
import { ColorPicker } from '../color_picker'; import { ColorPicker } from '../color_picker';
@ -52,7 +52,7 @@ export const ReferenceLinePanel = (
); );
const setConfig = useCallback( const setConfig = useCallback(
(yConfig: Partial<ExtendedYConfig> | undefined) => { (yConfig: Partial<YConfig> | undefined) => {
if (yConfig == null) { if (yConfig == null) {
return; return;
} }
@ -108,7 +108,7 @@ export const ReferenceLinePanel = (
interface LabelConfigurationOptions { interface LabelConfigurationOptions {
isHorizontal: boolean; isHorizontal: boolean;
axisMode: ExtendedYConfig['axisMode']; axisMode: YConfig['axisMode'];
} }
function getFillPositionOptions({ isHorizontal, axisMode }: LabelConfigurationOptions) { function getFillPositionOptions({ isHorizontal, axisMode }: LabelConfigurationOptions) {
@ -154,8 +154,8 @@ export const FillSetting = ({
setConfig, setConfig,
isHorizontal, isHorizontal,
}: { }: {
currentConfig?: ExtendedYConfig; currentConfig?: YConfig;
setConfig: (yConfig: Partial<ExtendedYConfig> | undefined) => void; setConfig: (yConfig: Partial<YConfig> | undefined) => void;
isHorizontal: boolean; isHorizontal: boolean;
}) => { }) => {
return ( return (

View file

@ -8,7 +8,8 @@
import React from 'react'; import React from 'react';
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import { EuiButtonGroup, EuiFormRow } from '@elastic/eui'; import { EuiButtonGroup, EuiFormRow } from '@elastic/eui';
import { IconPosition, YAxisMode } from '@kbn/expression-xy-plugin/common'; import { IconPosition } from '@kbn/expression-xy-plugin/common';
import { YAxisMode } from '../../types';
import { TooltipWrapper } from '../../../shared_components'; import { TooltipWrapper } from '../../../shared_components';
import { hasIcon, IconSelect, IconSet } from './icon_select'; import { hasIcon, IconSelect, IconSet } from './icon_select';

View file

@ -9,7 +9,6 @@ import { i18n } from '@kbn/i18n';
import { partition } from 'lodash'; import { partition } from 'lodash';
import { Position } from '@elastic/charts'; import { Position } from '@elastic/charts';
import type { PaletteOutput } from '@kbn/coloring'; import type { PaletteOutput } from '@kbn/coloring';
import type { SeriesType } from '@kbn/expression-xy-plugin/common';
import { import {
SuggestionRequest, SuggestionRequest,
VisualizationSuggestion, VisualizationSuggestion,
@ -17,7 +16,14 @@ import {
TableSuggestion, TableSuggestion,
TableChangeType, TableChangeType,
} from '../types'; } from '../types';
import { State, XYState, visualizationTypes, XYLayerConfig, XYDataLayerConfig } from './types'; import {
State,
XYState,
visualizationTypes,
XYLayerConfig,
XYDataLayerConfig,
SeriesType,
} from './types';
import { layerTypes } from '../../common'; import { layerTypes } from '../../common';
import { getIconForSeries } from './state_helpers'; import { getIconForSeries } from './state_helpers';
import { getDataLayers, isDataLayer } from './visualization_helpers'; import { getDataLayers, isDataLayer } from './visualization_helpers';

View file

@ -29,6 +29,7 @@ import {
TypedLensByValueInput, TypedLensByValueInput,
XYCurveType, XYCurveType,
XYState, XYState,
YAxisMode,
} from '@kbn/lens-plugin/public'; } from '@kbn/lens-plugin/public';
import type { DataView } from '@kbn/data-views-plugin/common'; import type { DataView } from '@kbn/data-views-plugin/common';
import { PersistableFilter } from '@kbn/lens-plugin/common'; import { PersistableFilter } from '@kbn/lens-plugin/common';
@ -900,8 +901,8 @@ export class LensAttributes {
this.layerConfigs[0].indexPattern.fieldFormatMap[ this.layerConfigs[0].indexPattern.fieldFormatMap[
this.layerConfigs[0].selectedMetricField this.layerConfigs[0].selectedMetricField
]?.id ]?.id
? 'left' ? ('left' as YAxisMode)
: 'right', : ('right' as YAxisMode),
}, },
], ],
xAccessor: `x-axis-column-layer${index}`, xAccessor: `x-axis-column-layer${index}`,

View file

@ -13,7 +13,7 @@ import type {
FieldBasedIndexPatternColumn, FieldBasedIndexPatternColumn,
SeriesType, SeriesType,
OperationType, OperationType,
ExtendedYConfig, YConfig,
} from '@kbn/lens-plugin/public'; } from '@kbn/lens-plugin/public';
import type { PersistableFilter } from '@kbn/lens-plugin/common'; import type { PersistableFilter } from '@kbn/lens-plugin/common';
@ -84,7 +84,7 @@ export interface SeriesConfig {
hasOperationType: boolean; hasOperationType: boolean;
palette?: PaletteOutput; palette?: PaletteOutput;
yTitle?: string; yTitle?: string;
yConfig?: ExtendedYConfig[]; yConfig?: YConfig[];
query?: { query: string; language: 'kuery' }; query?: { query: string; language: 'kuery' };
} }

View file

@ -3402,10 +3402,6 @@
"expressionXY.axisExtentConfig.help": "Configurer les étendues daxe du graphique xy", "expressionXY.axisExtentConfig.help": "Configurer les étendues daxe du graphique xy",
"expressionXY.axisExtentConfig.lowerBound.help": "Limite inférieure", "expressionXY.axisExtentConfig.lowerBound.help": "Limite inférieure",
"expressionXY.axisExtentConfig.upperBound.help": "Limite supérieure", "expressionXY.axisExtentConfig.upperBound.help": "Limite supérieure",
"expressionXY.axisTitlesVisibilityConfig.help": "Configurer laspect des titres daxe 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 laxe y.", "expressionXY.dataLayer.accessors.help": "Les colonnes à afficher sur laxe y.",
"expressionXY.layer.columnToLabel.help": "Paires clé-valeur JSON de lID de colonne pour létiquette", "expressionXY.layer.columnToLabel.help": "Paires clé-valeur JSON de lID de colonne pour létiquette",
"expressionXY.dataLayer.help": "Configurer un calque dans le graphique xy", "expressionXY.dataLayer.help": "Configurer un calque dans le graphique xy",
@ -3417,15 +3413,6 @@
"expressionXY.dataLayer.splitAccessor.help": "Colonne selon laquelle effectuer la division", "expressionXY.dataLayer.splitAccessor.help": "Colonne selon laquelle effectuer la division",
"expressionXY.dataLayer.xAccessor.help": "Axe X", "expressionXY.dataLayer.xAccessor.help": "Axe X",
"expressionXY.dataLayer.xScaleType.help": "Type déchelle de laxe x", "expressionXY.dataLayer.xScaleType.help": "Type déchelle de laxe x",
"expressionXY.dataLayer.yConfig.help": "Configuration supplémentaire pour les axes y",
"expressionXY.gridlinesConfig.help": "Configurer laspect 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 lorientation 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.filterForValueButtonAriaLabel": "Filtrer sur la valeur",
"expressionXY.legend.filterOptionsLegend": "{legendDataLabel}, options de filtre", "expressionXY.legend.filterOptionsLegend": "{legendDataLabel}, options de filtre",
"expressionXY.legend.filterOutValueButtonAriaLabel": "Exclure la valeur", "expressionXY.legend.filterOutValueButtonAriaLabel": "Exclure la valeur",
@ -3442,11 +3429,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.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 laxe y.", "expressionXY.referenceLineLayer.accessors.help": "Les colonnes à afficher sur laxe y.",
"expressionXY.referenceLineLayer.help": "Configurer une ligne de référence dans le graphique xy", "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 laspect 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.emptyXLabel": "(vide)",
"expressionXY.xyChart.iconSelect.alertIconLabel": "Alerte", "expressionXY.xyChart.iconSelect.alertIconLabel": "Alerte",
"expressionXY.xyChart.iconSelect.asteriskIconLabel": "Astérisque", "expressionXY.xyChart.iconSelect.asteriskIconLabel": "Astérisque",
@ -3463,39 +3445,20 @@
"expressionXY.xyChart.iconSelect.tagIconLabel": "Balise", "expressionXY.xyChart.iconSelect.tagIconLabel": "Balise",
"expressionXY.xyChart.iconSelect.triangleIconLabel": "Triangle", "expressionXY.xyChart.iconSelect.triangleIconLabel": "Triangle",
"expressionXY.xyVis.ariaLabel.help": "Spécifie lattribut aria-label du graphique xy", "expressionXY.xyVis.ariaLabel.help": "Spécifie lattribut 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.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.endValue.help": "Valeur de fin",
"expressionXY.xyVis.fillOpacity.help": "Définir l'opacité du remplissage du graphique en aires", "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.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.help": "Graphique X/Y",
"expressionXY.xyVis.hideEndzones.help": "Masquer les marqueurs de zone de fin pour les données partielles", "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.layeredXyVis.layers.help": "Calques de série visuelle",
"expressionXY.xyVis.legend.help": "Configurez la légende du graphique.", "expressionXY.xyVis.legend.help": "Configurez la légende du graphique.",
"expressionXY.xyVis.logDatatable.breakDown": "Répartir par", "expressionXY.xyVis.logDatatable.breakDown": "Répartir par",
"expressionXY.xyVis.logDatatable.metric": "Axe vertical", "expressionXY.xyVis.logDatatable.metric": "Axe vertical",
"expressionXY.xyVis.logDatatable.x": "Axe horizontal", "expressionXY.xyVis.logDatatable.x": "Axe horizontal",
"expressionXY.xyVis.renderer.help": "Outil de rendu de graphique X/Y", "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.valueLabels.help": "Mode des étiquettes de valeur",
"expressionXY.xyVis.valuesInLegend.help": "Afficher les valeurs dans la légende", "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 lindicateur",
"expressionXY.yConfig.color.help": "La couleur des séries",
"expressionXY.yConfig.fill.help": "Remplir",
"expressionXY.yConfig.forAccessor.help": "Laccesseur auquel cette configuration sapplique",
"expressionXY.yConfig.help": "Configurer le comportement de lindicateur daxe y dun 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 licô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.bytesFormat.numeralFormatLinkText": "Format numérique",
"fieldFormats.advancedSettings.format.bytesFormatText": "{numeralFormatLink} par défaut pour le format \"octets\"", "fieldFormats.advancedSettings.format.bytesFormatText": "{numeralFormatLink} par défaut pour le format \"octets\"",
"fieldFormats.advancedSettings.format.bytesFormatTitle": "Format octets", "fieldFormats.advancedSettings.format.bytesFormatTitle": "Format octets",

View file

@ -3501,10 +3501,6 @@
"expressionXY.axisExtentConfig.help": "xyグラフの軸範囲を構成", "expressionXY.axisExtentConfig.help": "xyグラフの軸範囲を構成",
"expressionXY.axisExtentConfig.lowerBound.help": "下界", "expressionXY.axisExtentConfig.lowerBound.help": "下界",
"expressionXY.axisExtentConfig.upperBound.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.dataLayer.accessors.help": "y軸に表示する列。",
"expressionXY.layer.columnToLabel.help": "ラベリングする列IDのJSONキー値のペア", "expressionXY.layer.columnToLabel.help": "ラベリングする列IDのJSONキー値のペア",
"expressionXY.dataLayer.help": "xyグラフでレイヤーを構成", "expressionXY.dataLayer.help": "xyグラフでレイヤーを構成",
@ -3516,15 +3512,6 @@
"expressionXY.dataLayer.splitAccessor.help": "分割の基準となる列", "expressionXY.dataLayer.splitAccessor.help": "分割の基準となる列",
"expressionXY.dataLayer.xAccessor.help": "X 軸", "expressionXY.dataLayer.xAccessor.help": "X 軸",
"expressionXY.dataLayer.xScaleType.help": "x軸の目盛タイプ", "expressionXY.dataLayer.xScaleType.help": "x軸の目盛タイプ",
"expressionXY.dataLayer.yConfig.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.filterForValueButtonAriaLabel": "値でフィルター",
"expressionXY.legend.filterOptionsLegend": "{legendDataLabel}、フィルターオプション", "expressionXY.legend.filterOptionsLegend": "{legendDataLabel}、フィルターオプション",
"expressionXY.legend.filterOutValueButtonAriaLabel": "値を除外", "expressionXY.legend.filterOutValueButtonAriaLabel": "値を除外",
@ -3541,11 +3528,6 @@
"expressionXY.legendConfig.verticalAlignment.help": "凡例がグラフ内に表示されるときに凡例の縦の配置を指定します。", "expressionXY.legendConfig.verticalAlignment.help": "凡例がグラフ内に表示されるときに凡例の縦の配置を指定します。",
"expressionXY.referenceLineLayer.accessors.help": "y軸に表示する列。", "expressionXY.referenceLineLayer.accessors.help": "y軸に表示する列。",
"expressionXY.referenceLineLayer.help": "xyグラフで基準線を構成", "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.emptyXLabel": "(空)",
"expressionXY.xyChart.iconSelect.alertIconLabel": "アラート", "expressionXY.xyChart.iconSelect.alertIconLabel": "アラート",
"expressionXY.xyChart.iconSelect.asteriskIconLabel": "アスタリスク", "expressionXY.xyChart.iconSelect.asteriskIconLabel": "アスタリスク",
@ -3562,39 +3544,20 @@
"expressionXY.xyChart.iconSelect.tagIconLabel": "タグ", "expressionXY.xyChart.iconSelect.tagIconLabel": "タグ",
"expressionXY.xyChart.iconSelect.triangleIconLabel": "三角形", "expressionXY.xyChart.iconSelect.triangleIconLabel": "三角形",
"expressionXY.xyVis.ariaLabel.help": "xyグラフのariaラベルを指定します", "expressionXY.xyVis.ariaLabel.help": "xyグラフのariaラベルを指定します",
"expressionXY.xyVis.axisTitlesVisibilitySettings.help": "xおよびy軸のタイトルを表示",
"expressionXY.xyVis.curveType.help": "折れ線グラフで曲線タイプをレンダリングする方法を定義します", "expressionXY.xyVis.curveType.help": "折れ線グラフで曲線タイプをレンダリングする方法を定義します",
"expressionXY.xyVis.endValue.help": "終了値", "expressionXY.xyVis.endValue.help": "終了値",
"expressionXY.xyVis.fillOpacity.help": "エリアグラフの塗りつぶしの透明度を定義", "expressionXY.xyVis.fillOpacity.help": "エリアグラフの塗りつぶしの透明度を定義",
"expressionXY.xyVis.fittingFunction.help": "欠測値の処理方法を定義", "expressionXY.xyVis.fittingFunction.help": "欠測値の処理方法を定義",
"expressionXY.xyVis.gridlinesVisibilitySettings.help": "xおよびy軸のグリッド線を表示",
"expressionXY.xyVis.help": "X/Y チャート", "expressionXY.xyVis.help": "X/Y チャート",
"expressionXY.xyVis.hideEndzones.help": "部分データの終了ゾーンマーカーを非表示", "expressionXY.xyVis.hideEndzones.help": "部分データの終了ゾーンマーカーを非表示",
"expressionXY.xyVis.labelsOrientation.help": "軸ラベルの回転を定義します",
"expressionXY.layeredXyVis.layers.help": "視覚的な系列のレイヤー", "expressionXY.layeredXyVis.layers.help": "視覚的な系列のレイヤー",
"expressionXY.xyVis.legend.help": "チャートの凡例を構成します。", "expressionXY.xyVis.legend.help": "チャートの凡例を構成します。",
"expressionXY.xyVis.logDatatable.breakDown": "内訳の基準", "expressionXY.xyVis.logDatatable.breakDown": "内訳の基準",
"expressionXY.xyVis.logDatatable.metric": "縦軸", "expressionXY.xyVis.logDatatable.metric": "縦軸",
"expressionXY.xyVis.logDatatable.x": "横軸", "expressionXY.xyVis.logDatatable.x": "横軸",
"expressionXY.xyVis.renderer.help": "X/Y チャートを再レンダリング", "expressionXY.xyVis.renderer.help": "X/Y チャートを再レンダリング",
"expressionXY.xyVis.tickLabelsVisibilitySettings.help": "xおよびy軸の目盛ラベルを表示",
"expressionXY.xyVis.valueLabels.help": "値ラベルモード", "expressionXY.xyVis.valueLabels.help": "値ラベルモード",
"expressionXY.xyVis.valuesInLegend.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": "この構成のアクセサー",
"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.bytesFormat.numeralFormatLinkText": "数字フォーマット",
"fieldFormats.advancedSettings.format.bytesFormatText": "「バイト」フォーマットのデフォルト{numeralFormatLink}です", "fieldFormats.advancedSettings.format.bytesFormatText": "「バイト」フォーマットのデフォルト{numeralFormatLink}です",
"fieldFormats.advancedSettings.format.bytesFormatTitle": "バイトフォーマット", "fieldFormats.advancedSettings.format.bytesFormatTitle": "バイトフォーマット",

View file

@ -3506,10 +3506,6 @@
"expressionXY.axisExtentConfig.help": "配置 xy 图表的轴范围", "expressionXY.axisExtentConfig.help": "配置 xy 图表的轴范围",
"expressionXY.axisExtentConfig.lowerBound.help": "下边界", "expressionXY.axisExtentConfig.lowerBound.help": "下边界",
"expressionXY.axisExtentConfig.upperBound.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.dataLayer.accessors.help": "要在 y 轴上显示的列。",
"expressionXY.layer.columnToLabel.help": "要标记的列 ID 的 JSON 键值对", "expressionXY.layer.columnToLabel.help": "要标记的列 ID 的 JSON 键值对",
"expressionXY.dataLayer.help": "配置 xy 图表中的图层", "expressionXY.dataLayer.help": "配置 xy 图表中的图层",
@ -3521,15 +3517,6 @@
"expressionXY.dataLayer.splitAccessor.help": "拆分要依据的列", "expressionXY.dataLayer.splitAccessor.help": "拆分要依据的列",
"expressionXY.dataLayer.xAccessor.help": "X 轴", "expressionXY.dataLayer.xAccessor.help": "X 轴",
"expressionXY.dataLayer.xScaleType.help": "x 轴的缩放类型", "expressionXY.dataLayer.xScaleType.help": "x 轴的缩放类型",
"expressionXY.dataLayer.yConfig.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.filterForValueButtonAriaLabel": "筛留值",
"expressionXY.legend.filterOptionsLegend": "{legendDataLabel}, 筛选选项", "expressionXY.legend.filterOptionsLegend": "{legendDataLabel}, 筛选选项",
"expressionXY.legend.filterOutValueButtonAriaLabel": "筛除值", "expressionXY.legend.filterOutValueButtonAriaLabel": "筛除值",
@ -3546,11 +3533,6 @@
"expressionXY.legendConfig.verticalAlignment.help": "指定图例显示在图表内时垂直对齐。", "expressionXY.legendConfig.verticalAlignment.help": "指定图例显示在图表内时垂直对齐。",
"expressionXY.referenceLineLayer.accessors.help": "要在 y 轴上显示的列。", "expressionXY.referenceLineLayer.accessors.help": "要在 y 轴上显示的列。",
"expressionXY.referenceLineLayer.help": "配置 xy 图表中的参考线", "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.emptyXLabel": "(空)",
"expressionXY.xyChart.iconSelect.alertIconLabel": "告警", "expressionXY.xyChart.iconSelect.alertIconLabel": "告警",
"expressionXY.xyChart.iconSelect.asteriskIconLabel": "星号", "expressionXY.xyChart.iconSelect.asteriskIconLabel": "星号",
@ -3567,39 +3549,20 @@
"expressionXY.xyChart.iconSelect.tagIconLabel": "标签", "expressionXY.xyChart.iconSelect.tagIconLabel": "标签",
"expressionXY.xyChart.iconSelect.triangleIconLabel": "三角形", "expressionXY.xyChart.iconSelect.triangleIconLabel": "三角形",
"expressionXY.xyVis.ariaLabel.help": "指定 xy 图表的 aria 标签", "expressionXY.xyVis.ariaLabel.help": "指定 xy 图表的 aria 标签",
"expressionXY.xyVis.axisTitlesVisibilitySettings.help": "显示 x 和 y 轴标题",
"expressionXY.xyVis.curveType.help": "定义为折线图渲染曲线类型的方式", "expressionXY.xyVis.curveType.help": "定义为折线图渲染曲线类型的方式",
"expressionXY.xyVis.endValue.help": "结束值", "expressionXY.xyVis.endValue.help": "结束值",
"expressionXY.xyVis.fillOpacity.help": "定义面积图填充透明度", "expressionXY.xyVis.fillOpacity.help": "定义面积图填充透明度",
"expressionXY.xyVis.fittingFunction.help": "定义处理缺失值的方式", "expressionXY.xyVis.fittingFunction.help": "定义处理缺失值的方式",
"expressionXY.xyVis.gridlinesVisibilitySettings.help": "显示 x 和 y 轴网格线",
"expressionXY.xyVis.help": "X/Y 图表", "expressionXY.xyVis.help": "X/Y 图表",
"expressionXY.xyVis.hideEndzones.help": "隐藏部分数据的末日区域标记", "expressionXY.xyVis.hideEndzones.help": "隐藏部分数据的末日区域标记",
"expressionXY.xyVis.labelsOrientation.help": "定义轴标签的旋转",
"expressionXY.layeredXyVis.layers.help": "可视序列的图层", "expressionXY.layeredXyVis.layers.help": "可视序列的图层",
"expressionXY.xyVis.legend.help": "配置图表图例。", "expressionXY.xyVis.legend.help": "配置图表图例。",
"expressionXY.xyVis.logDatatable.breakDown": "细分方式", "expressionXY.xyVis.logDatatable.breakDown": "细分方式",
"expressionXY.xyVis.logDatatable.metric": "垂直轴", "expressionXY.xyVis.logDatatable.metric": "垂直轴",
"expressionXY.xyVis.logDatatable.x": "水平轴", "expressionXY.xyVis.logDatatable.x": "水平轴",
"expressionXY.xyVis.renderer.help": "X/Y 图表呈现器", "expressionXY.xyVis.renderer.help": "X/Y 图表呈现器",
"expressionXY.xyVis.tickLabelsVisibilitySettings.help": "显示 x 和 y 轴刻度标签",
"expressionXY.xyVis.valueLabels.help": "值标签模式", "expressionXY.xyVis.valueLabels.help": "值标签模式",
"expressionXY.xyVis.valuesInLegend.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": "此配置针对的访问器",
"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.bytesFormat.numeralFormatLinkText": "数值格式",
"fieldFormats.advancedSettings.format.bytesFormatText": "“字节”格式的默认{numeralFormatLink}", "fieldFormats.advancedSettings.format.bytesFormatText": "“字节”格式的默认{numeralFormatLink}",
"fieldFormats.advancedSettings.format.bytesFormatTitle": "字节格式", "fieldFormats.advancedSettings.format.bytesFormatTitle": "字节格式",

View file

@ -274,13 +274,13 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
}); });
let data = await PageObjects.lens.getCurrentChartDebugState('xyVisChart'); let data = await PageObjects.lens.getCurrentChartDebugState('xyVisChart');
expect(data?.axes?.y?.[0].title).to.eql(axisTitle); expect(data?.axes?.y?.[1].title).to.eql(axisTitle);
// hide the gridlines // hide the gridlines
await testSubjects.click('lnsshowyLeftAxisGridlines'); await testSubjects.click('lnsshowyLeftAxisGridlines');
data = await PageObjects.lens.getCurrentChartDebugState('xyVisChart'); data = await PageObjects.lens.getCurrentChartDebugState('xyVisChart');
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 () => { it('should transition from a multi-layer stacked bar to donut chart using suggestions', async () => {