@@ -20,6 +20,10 @@ import {
2020 GeometryValue ,
2121 XYChartSeriesIdentifier ,
2222 StackMode ,
23+ RecursivePartial ,
24+ Theme ,
25+ VerticalAlignment ,
26+ HorizontalAlignment ,
2327} from '@elastic/charts' ;
2428import { I18nProvider } from '@kbn/i18n/react' ;
2529import {
@@ -131,6 +135,11 @@ export const xyChart: ExpressionFunctionDefinition<
131135 defaultMessage : 'Define how missing values are treated' ,
132136 } ) ,
133137 } ,
138+ valueLabels : {
139+ types : [ 'string' ] ,
140+ options : [ 'hide' , 'inside' ] ,
141+ help : '' ,
142+ } ,
134143 tickLabelsVisibilitySettings : {
135144 types : [ 'lens_xy_tickLabelsConfig' ] ,
136145 help : i18n . translate ( 'xpack.lens.xyChart.tickLabelsSettings.help' , {
@@ -214,6 +223,40 @@ export const getXyChartRenderer = (dependencies: {
214223 } ,
215224} ) ;
216225
226+ function mergeThemeWithValueLabelsStyling (
227+ theme : RecursivePartial < Theme > ,
228+ valuesLabelMode : string = 'hide' ,
229+ isHorizontal : boolean
230+ ) {
231+ const VALUE_LABELS_MAX_FONTSIZE = 15 ;
232+ const VALUE_LABELS_MIN_FONTSIZE = 10 ;
233+ const VALUE_LABELS_VERTICAL_OFFSET = - 10 ;
234+ const VALUE_LABELS_HORIZONTAL_OFFSET = 10 ;
235+
236+ if ( valuesLabelMode === 'hide' ) {
237+ return theme ;
238+ }
239+ return {
240+ ...theme ,
241+ ...{
242+ barSeriesStyle : {
243+ ...theme . barSeriesStyle ,
244+ displayValue : {
245+ fontSize : { min : VALUE_LABELS_MIN_FONTSIZE , max : VALUE_LABELS_MAX_FONTSIZE } ,
246+ fill : { textInverted : true , textBorder : 2 } ,
247+ alignment : isHorizontal
248+ ? {
249+ vertical : VerticalAlignment . Middle ,
250+ }
251+ : { horizontal : HorizontalAlignment . Center } ,
252+ offsetX : isHorizontal ? VALUE_LABELS_HORIZONTAL_OFFSET : 0 ,
253+ offsetY : isHorizontal ? 0 : VALUE_LABELS_VERTICAL_OFFSET ,
254+ } ,
255+ } ,
256+ } ,
257+ } ;
258+ }
259+
217260function getIconForSeriesType ( seriesType : SeriesType ) : IconType {
218261 return visualizationTypes . find ( ( c ) => c . id === seriesType ) ! . icon || 'empty' ;
219262}
@@ -254,7 +297,7 @@ export function XYChart({
254297 onClickValue,
255298 onSelectRange,
256299} : XYChartRenderProps ) {
257- const { legend, layers, fittingFunction, gridlinesVisibilitySettings } = args ;
300+ const { legend, layers, fittingFunction, gridlinesVisibilitySettings, valueLabels } = args ;
258301 const chartTheme = chartsThemeService . useChartsTheme ( ) ;
259302 const chartBaseTheme = chartsThemeService . useChartsBaseTheme ( ) ;
260303
@@ -396,6 +439,16 @@ export function XYChart({
396439 return style ;
397440 } ;
398441
442+ const shouldShowValueLabels =
443+ // No stacked bar charts
444+ filteredLayers . every ( ( layer ) => ! layer . seriesType . includes ( 'stacked' ) ) &&
445+ // No histogram charts
446+ ! isHistogramViz ;
447+
448+ const baseThemeWithMaybeValueLabels = ! shouldShowValueLabels
449+ ? chartTheme
450+ : mergeThemeWithValueLabelsStyling ( chartTheme , valueLabels , shouldRotate ) ;
451+
399452 const colorAssignments = getColorAssignments ( args . layers , data , formatFactory ) ;
400453
401454 return (
@@ -408,7 +461,7 @@ export function XYChart({
408461 }
409462 legendPosition = { legend . position }
410463 showLegendExtra = { false }
411- theme = { chartTheme }
464+ theme = { baseThemeWithMaybeValueLabels }
412465 baseTheme = { chartBaseTheme }
413466 tooltip = { {
414467 headerFormatter : ( d ) => safeXAccessorLabelRenderer ( d . value ) ,
@@ -613,6 +666,10 @@ export function XYChart({
613666 } ) ;
614667 }
615668
669+ const yAxis = yAxesConfiguration . find ( ( axisConfiguration ) =>
670+ axisConfiguration . series . find ( ( currentSeries ) => currentSeries . accessor === accessor )
671+ ) ;
672+
616673 const seriesProps : SeriesSpec = {
617674 splitSeriesAccessors : splitAccessor ? [ splitAccessor ] : [ ] ,
618675 stackAccessors : seriesType . includes ( 'stacked' ) ? [ xAccessor as string ] : [ ] ,
@@ -649,9 +706,7 @@ export function XYChart({
649706 palette . params
650707 ) ;
651708 } ,
652- groupId : yAxesConfiguration . find ( ( axisConfiguration ) =>
653- axisConfiguration . series . find ( ( currentSeries ) => currentSeries . accessor === accessor )
654- ) ?. groupId ,
709+ groupId : yAxis ?. groupId ,
655710 enableHistogramMode :
656711 isHistogram &&
657712 ( seriesType . includes ( 'stacked' ) || ! splitAccessor ) &&
@@ -723,7 +778,19 @@ export function XYChart({
723778 case 'bar_horizontal' :
724779 case 'bar_horizontal_stacked' :
725780 case 'bar_horizontal_percentage_stacked' :
726- return < BarSeries key = { index } { ...seriesProps } /> ;
781+ const valueLabelsSettings = {
782+ displayValueSettings : {
783+ // This format double fixes two issues in elastic-chart
784+ // * when rotating the chart, the formatter is not correctly picked
785+ // * in some scenarios value labels are not strings, and this breaks the elastic-chart lib
786+ valueFormatter : ( d : unknown ) => yAxis ?. formatter ?. convert ( d ) || '' ,
787+ showValueLabel : shouldShowValueLabels && valueLabels !== 'hide' ,
788+ isAlternatingValueLabel : false ,
789+ isValueContainedInElement : true ,
790+ hideClippedValue : true ,
791+ } ,
792+ } ;
793+ return < BarSeries key = { index } { ...seriesProps } { ...valueLabelsSettings } /> ;
727794 case 'area_stacked' :
728795 case 'area_percentage_stacked' :
729796 return (
0 commit comments