diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line.test.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line.test.ts index 7e3230d075fcf..6dc19f79bd133 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line.test.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line.test.ts @@ -16,6 +16,7 @@ describe('referenceLine', () => { value: 100, fill: 'above', position: 'bottom', + forAccessor: '', }; const result = referenceLineFunction.fn(null, args, createMockExecutionContext()); @@ -47,6 +48,7 @@ describe('referenceLine', () => { color: '#fff', fill: 'below', textVisibility: true, + forAccessor: '', }; const result = referenceLineFunction.fn(null, args, createMockExecutionContext()); @@ -71,6 +73,7 @@ describe('referenceLine', () => { value: 100, fill: 'none', position: 'bottom', + forAccessor: '', }; const result = referenceLineFunction.fn(null, args, createMockExecutionContext()); @@ -96,6 +99,7 @@ describe('referenceLine', () => { textVisibility: true, fill: 'none', position: 'bottom', + forAccessor: '', }; const result = referenceLineFunction.fn(null, args, createMockExecutionContext()); @@ -123,6 +127,7 @@ describe('referenceLine', () => { textVisibility, fill: 'none', position: 'bottom', + forAccessor: '', }; const result = referenceLineFunction.fn(null, args, createMockExecutionContext()); diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line.ts index 0f3a3a4f6b1fb..93832291b9835 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line.ts @@ -105,6 +105,11 @@ export const referenceLineFunction: ReferenceLineFn = { default: FillStyles.NONE, strict: true, }, + forAccessor: { + types: ['string'], + help: '', + default: '', + }, }, fn(table, args) { const textVisibility = diff --git a/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts b/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts index 3a2d135113779..7d5df1a7394bb 100644 --- a/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts +++ b/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts @@ -329,8 +329,7 @@ export type ExtendedAnnotationLayerConfigResult = ExtendedAnnotationLayerArgs & layerType: typeof LayerTypes.ANNOTATIONS; }; -export interface ReferenceLineArgs - extends Omit { +export interface ReferenceLineArgs extends Omit { name?: string; value: number; fill: FillStyle; @@ -365,6 +364,7 @@ export interface ReferenceLineConfigResult { type: typeof REFERENCE_LINE; layerType: typeof LayerTypes.REFERENCELINE; lineLength: number; + columnToLabel?: string; decorations: [ExtendedReferenceLineDecorationConfig]; } diff --git a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_line.tsx b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_line.tsx index 6c58171d35ae4..f12362b2c8c21 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_line.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_line.tsx @@ -35,6 +35,7 @@ export const ReferenceLine: FC = ({ }) => { const { decorations: [decorationConfig], + columnToLabel, } = layer; if (!decorationConfig) { @@ -42,15 +43,19 @@ export const ReferenceLine: FC = ({ } const { value } = decorationConfig; + const columnToLabelMap: Record = columnToLabel ? JSON.parse(columnToLabel) : {}; const axisGroup = getAxisGroupForReferenceLine(axesConfiguration, decorationConfig, isHorizontal); const formatter = axisGroup?.formatter || xAxisFormatter; const id = `${layer.layerId}-${value}`; + const name = decorationConfig.textVisibility + ? columnToLabelMap[decorationConfig.forAccessor] + : undefined; return ( = ({ paddingMap, isHorizontal, }) => { - const { id, axisGroup, iconPosition, name, textVisibility, value, fill, color } = config; + const { id, axisGroup, iconPosition, name, value, fill, color } = config; const defaultColor = euiLightVars.euiColorDarkShade; // get the position for vertical chart @@ -85,18 +85,9 @@ export const ReferenceLineAnnotations: FC = ({ getOriginalAxisPosition(axisGroup?.position ?? Position.Bottom, isHorizontal) ); // the padding map is built for vertical chart - const hasReducedPadding = paddingMap[markerPositionVertical] === LINES_MARKER_SIZE; + const isTextOnlyMarker = paddingMap[markerPositionVertical] === LINES_MARKER_SIZE; - const props = getLineAnnotationProps( - config, - { - markerLabel: name, - markerBodyLabel: textVisibility && !hasReducedPadding ? name : undefined, - }, - axesMap, - paddingMap, - isHorizontal - ); + const props = getLineAnnotationProps(config, name, axesMap, isHorizontal, isTextOnlyMarker); const sharedStyle = getSharedStyle(config); diff --git a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_lines.test.tsx b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_lines.test.tsx index 6aa7319c0ba4c..810705a71fa3e 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_lines.test.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_lines.test.tsx @@ -536,6 +536,7 @@ describe('ReferenceLines', () => { lineStyle: 'solid', fill, value, + forAccessor: '', }), ]} /> @@ -576,6 +577,7 @@ describe('ReferenceLines', () => { lineStyle: 'solid', fill, value, + forAccessor: '', }), ]} /> @@ -617,12 +619,14 @@ describe('ReferenceLines', () => { lineStyle: 'solid', fill, value, + forAccessor: '', }), createReferenceLine(layerPrefix, 10, { position, lineStyle: 'solid', fill, value, + forAccessor: '', }), ]} /> @@ -667,12 +671,14 @@ describe('ReferenceLines', () => { lineStyle: 'solid', fill, value, + forAccessor: '', }), createReferenceLine(layerPrefix, 10, { position: 'bottom', lineStyle: 'solid', fill, value, + forAccessor: '', }), ]} /> @@ -720,12 +726,14 @@ describe('ReferenceLines', () => { lineStyle: 'solid', fill: 'above', value: value1, + forAccessor: '', }), createReferenceLine(layerPrefix, 10, { position, lineStyle: 'solid', fill: 'below', value: value2, + forAccessor: '', }), ]} /> diff --git a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/utils.test.tsx b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/utils.test.tsx index f46f12d2317ab..ba14980ed5a7b 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/utils.test.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/utils.test.tsx @@ -7,8 +7,11 @@ */ import { AxisTypeId, computeInputCombinations, PosType } from './_mocks'; -import { computeChartMargins } from './utils'; -import { AxisConfiguration } from '../../helpers'; +import { computeChartMargins, getLineAnnotationProps } from './utils'; +import { AxesMap, AxisConfiguration, Marker, MarkerBody } from '../../helpers'; +import { ReferenceLineAnnotationConfig } from './reference_line_annotations'; +import { Position } from '@elastic/charts'; +import React from 'react'; describe('reference lines helpers', () => { describe('computeChartMargins', () => { @@ -36,4 +39,122 @@ describe('reference lines helpers', () => { } ); }); + + describe('getLineAnnotationProps', () => { + function getAxesMap({ left, right }: Partial = {}): AxesMap { + return { + left: { groupId: 'yLeft', position: Position.Left, series: [], ...left }, + right: { groupId: 'yRight', position: Position.Right, series: [], ...right }, + }; + } + function getConfig( + customPartialConfig: Partial = {} + ): ReferenceLineAnnotationConfig { + return { + id: 'id', + value: 3, + lineWidth: 5, + textVisibility: false, + iconPosition: 'auto', + axisGroup: getAxesMap().left, + ...customPartialConfig, + }; + } + + it('should render only the line with no marker', () => { + const config = getConfig(); + expect(getLineAnnotationProps(config, 'myLabel', getAxesMap(), false, false)).toEqual( + expect.objectContaining({ + groupId: 'yLeft', + markerPosition: Position.Left, + marker: ( + + ), + markerBody: , + }) + ); + }); + it('should render an icon marker', () => { + const config = getConfig({ icon: 'triangle' }); + expect(getLineAnnotationProps(config, 'myLabel', getAxesMap(), false, false)).toEqual( + expect.objectContaining({ + groupId: 'yLeft', + markerPosition: Position.Left, + marker: ( + + ), + markerBody: , + }) + ); + }); + it('should render only the label', () => { + const config = getConfig({ textVisibility: true }); + expect(getLineAnnotationProps(config, 'myLabel', getAxesMap(), false, true)).toEqual( + expect.objectContaining({ + groupId: 'yLeft', + markerPosition: Position.Left, + marker: ( + + ), + markerBody: , + }) + ); + }); + it('should render both icon and text', () => { + const config = getConfig({ icon: 'triangle', textVisibility: true }); + expect(getLineAnnotationProps(config, 'myLabel', getAxesMap(), false, false)).toEqual( + expect.objectContaining({ + groupId: 'yLeft', + markerPosition: Position.Left, + marker: ( + + ), + markerBody: , + }) + ); + }); + + it('should handle marker rotated label', () => { + const config = getConfig({ + icon: 'triangle', + textVisibility: true, + axisGroup: { groupId: 'x', position: Position.Bottom, series: [] }, + }); + expect(getLineAnnotationProps(config, 'myLabel', getAxesMap(), false, false)).toEqual( + expect.objectContaining({ + groupId: 'x', + markerPosition: Position.Top, + marker: ( + + ), + markerBody: , + }) + ); + }); + }); }); diff --git a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/utils.tsx b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/utils.tsx index 5c56bd8cc6955..33c513a42c603 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/utils.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/utils.tsx @@ -21,7 +21,6 @@ import { import { FillStyles } from '../../../common/constants'; import { GroupsConfiguration, - LINES_MARKER_SIZE, mapVerticalToHorizontalPlacement, Marker, MarkerBody, @@ -29,7 +28,7 @@ import { getOriginalAxisPosition, AxesMap, } from '../../helpers'; -import { ReferenceLineAnnotationConfig } from './reference_line_annotations'; +import type { ReferenceLineAnnotationConfig } from './reference_line_annotations'; // if there's just one axis, put it on the other one // otherwise use the same axis @@ -84,10 +83,10 @@ export const getSharedStyle = (config: ReferenceLineAnnotationConfig) => ({ export const getLineAnnotationProps = ( config: ReferenceLineAnnotationConfig, - labels: { markerLabel?: string; markerBodyLabel?: string }, + label: string | undefined, axesMap: AxesMap, - paddingMap: Partial>, - isHorizontal: boolean + isHorizontal: boolean, + isTextOnlyMarker: boolean ) => { // get the position for vertical chart const markerPositionVertical = getBaseIconPlacement( @@ -96,27 +95,27 @@ export const getLineAnnotationProps = ( getOriginalAxisPosition(config.axisGroup?.position ?? Position.Bottom, isHorizontal) ); - // the padding map is built for vertical chart - const hasReducedPadding = paddingMap[markerPositionVertical] === LINES_MARKER_SIZE; - const markerPosition = isHorizontal ? mapVerticalToHorizontalPlacement(markerPositionVertical) : markerPositionVertical; + const isMarkerLabelHorizontal = + markerPosition === Position.Bottom || markerPosition === Position.Top; + return { groupId: config.axisGroup?.groupId || 'bottom', marker: ( ), markerBody: ( ), // rotate the position if required diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/annotations.tsx b/src/plugins/chart_expressions/expression_xy/public/helpers/annotations.tsx index c972b335a1245..93cf570e86bc1 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/annotations.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/annotations.tsx @@ -29,7 +29,7 @@ type PartialReferenceLineDecorationConfig = Pick< type PartialMergedAnnotation = Pick< MergedAnnotation, - 'position' | 'icon' | 'textVisibility' | 'label' + 'position' | 'icon' | 'textVisibility' | 'label' | 'isGrouped' >; const isExtendedDecorationConfig = ( @@ -37,6 +37,12 @@ const isExtendedDecorationConfig = ( ): config is PartialReferenceLineDecorationConfig => (config as PartialReferenceLineDecorationConfig)?.iconPosition ? true : false; +export const isAnnotationConfig = ( + config: PartialReferenceLineDecorationConfig | PartialMergedAnnotation +): config is PartialMergedAnnotation => { + return 'isGrouped' in config; +}; + // Note: it does not take into consideration whether the reference line is in view or not export const getLinesCausedPaddings = ( visualConfigs: Array, @@ -52,8 +58,9 @@ export const getLinesCausedPaddings = ( } const { position, icon, textVisibility } = config; const iconPosition = isExtendedDecorationConfig(config) ? config.iconPosition : undefined; + const isLabelVisible = textVisibility && (isAnnotationConfig(config) ? config.label : true); - if (position && (hasIcon(icon) || (textVisibility && 'label' in config))) { + if (position && (hasIcon(icon) || isLabelVisible)) { const placement = getBaseIconPlacement( iconPosition, axesMap, @@ -61,7 +68,7 @@ export const getLinesCausedPaddings = ( ); paddings[placement] = Math.max( paddings[placement] || 0, - LINES_MARKER_SIZE * (textVisibility && 'label' in config && config.label ? 2 : 1) // double the padding size if there's text + LINES_MARKER_SIZE * (isLabelVisible ? 2 : 1) // double the padding size if there's text ); icons[placement] = (icons[placement] || 0) + (hasIcon(icon) ? 1 : 0); }