export function XYChart()

in src/platform/plugins/shared/chart_expressions/expression_xy/public/components/xy_chart.tsx [197:1065]


export function XYChart({
  args,
  data,
  formatFactory,
  timeZone,
  chartsThemeService,
  chartsActiveCursorService,
  paletteService,
  minInterval,
  onClickValue,
  onClickMultiValue,
  onCreateAlertRule,
  layerCellValueActions,
  onSelectRange,
  setChartSize,
  interactive = true,
  syncColors,
  syncTooltips,
  syncCursor,
  useLegacyTimeAxis,
  renderComplete,
  uiState,
  timeFormat,
  overrides,
}: XYChartRenderProps) {
  const {
    legend,
    layers,
    fittingFunction,
    endValue,
    emphasizeFitting,
    valueLabels,
    hideEndzones,
    yAxisConfigs,
    xAxisConfig,
    splitColumnAccessor,
    splitRowAccessor,
    singleTable,
    annotations,
  } = args;

  const chartRef = useRef<Chart>(null);
  const chartBaseTheme = chartsThemeService.useChartsBaseTheme();
  const darkMode = chartsThemeService.useDarkMode();
  const palettes = useKbnPalettes();
  const appFixedViewport = useAppFixedViewport();
  const filteredLayers = getFilteredLayers(layers);
  const layersById = filteredLayers.reduce<Record<string, CommonXYLayerConfig>>(
    (hashMap, layer) => ({ ...hashMap, [layer.layerId]: layer }),
    {}
  );
  const chartHasMoreThanOneSeries =
    filteredLayers.length > 1 ||
    filteredLayers.some((layer) => layer.accessors.length > 1) ||
    filteredLayers.some(
      (layer) => isDataLayer(layer) && layer.splitAccessors && layer.splitAccessors.length
    );

  const getShowLegendDefault = useCallback(() => {
    const legendStateDefault =
      legend.isVisible && !legend.showSingleSeries ? chartHasMoreThanOneSeries : legend.isVisible;
    return uiState?.get('vis.legendOpen', legendStateDefault) ?? legendStateDefault;
  }, [chartHasMoreThanOneSeries, legend.isVisible, legend.showSingleSeries, uiState]);

  const [showLegend, setShowLegend] = useState<boolean>(() => getShowLegendDefault());

  useEffect(() => {
    const legendShow = getShowLegendDefault();
    setShowLegend(legendShow);
  }, [getShowLegendDefault]);

  const toggleLegend = useCallback(() => {
    setShowLegend((value) => {
      const newValue = !value;
      uiState?.set?.('vis.legendOpen', newValue);
      return newValue;
    });
  }, [uiState]);

  const setColor = useCallback(
    (newColor: string | null, seriesLabel: string | number) => {
      const colors = uiState?.get('vis.colors') || {};
      if (colors[seriesLabel] === newColor || !newColor) {
        delete colors[seriesLabel];
      } else {
        colors[seriesLabel] = newColor;
      }
      uiState?.setSilent('vis.colors', null);
      uiState?.set('vis.colors', colors);
      uiState?.emit('reload');
      uiState?.emit('colorChanged');
    },
    [uiState]
  );

  // Exclude the reference layers from the cursor update
  const cursorSyncLayers = filteredLayers.filter(isDataLayer);
  const handleCursorUpdate = useActiveCursor(chartsActiveCursorService, chartRef, {
    datatables: cursorSyncLayers.map(({ table }) => table),
  });

  const onRenderChange = useCallback(
    (isRendered: boolean = true) => {
      if (isRendered) {
        renderComplete();
      }
    },
    [renderComplete]
  );

  const dataLayers: CommonXYDataLayerConfig[] = filteredLayers.filter(isDataLayer);

  const isTimeViz = isTimeChart(dataLayers);

  useEffect(() => {
    const chartSizeSpec: ChartSizeSpec =
      isTimeViz && !isHorizontalChart(dataLayers)
        ? {
            aspectRatio: {
              x: 16,
              y: 9,
            },
            minDimensions: {
              y: { value: 300, unit: 'pixels' },
              x: { value: 100, unit: 'percentage' },
            },
          }
        : {
            maxDimensions: {
              x: { value: 100, unit: 'percentage' },
              y: { value: 100, unit: 'percentage' },
            },
          };

    setChartSize(chartSizeSpec);
  }, [dataLayers, isTimeViz, setChartSize]);

  const formattedDatatables = useMemo(
    () =>
      getFormattedTablesByLayers(dataLayers, formatFactory, splitColumnAccessor, splitRowAccessor),
    [dataLayers, formatFactory, splitColumnAccessor, splitRowAccessor]
  );

  const fieldFormats = useMemo(
    () => getLayersFormats(dataLayers, { splitColumnAccessor, splitRowAccessor }, formatFactory),
    [dataLayers, splitColumnAccessor, splitRowAccessor, formatFactory]
  );

  const icon: IconType = getIconForSeriesType(getDataLayers(layers)?.[0]);

  if (dataLayers.length === 0) {
    return (
      <EmptyPlaceholder icon={icon} renderComplete={onRenderChange} css={xyChartEmptyStyles} />
    );
  }

  // use formatting hint of first x axis column to format ticks
  const xAxisColumn = dataLayers[0].xAccessor
    ? getColumnByAccessor(dataLayers[0].xAccessor, dataLayers[0]?.table.columns)
    : undefined;

  const xAxisFormatter = formatFactory(
    xAxisColumn?.id ? fieldFormats[dataLayers[0].layerId].xAccessors[xAxisColumn?.id] : undefined
  );

  // This is a safe formatter for the xAccessor that abstracts the knowledge of already formatted layers
  const safeXAccessorLabelRenderer = (value: unknown): string =>
    xAxisColumn && formattedDatatables[dataLayers[0]?.layerId]?.formattedColumns[xAxisColumn.id]
      ? String(value)
      : String(xAxisFormatter.convert(value));

  const shouldRotate = isHorizontalChart(dataLayers);

  const yAxesConfiguration = getAxesConfiguration(
    dataLayers,
    shouldRotate,
    formatFactory,
    fieldFormats,
    yAxisConfigs
  );

  const axesConfiguration = getAxesConfiguration(
    dataLayers,
    shouldRotate,
    formatFactory,
    fieldFormats,
    [...(yAxisConfigs ?? []), ...(xAxisConfig ? [xAxisConfig] : [])]
  );

  const xTitle = xAxisConfig?.title || (xAxisColumn && xAxisColumn.name) || undefined;
  const yAxesMap = {
    left: yAxesConfiguration.find(
      ({ position }) => position === getAxisPosition(Position.Left, shouldRotate)
    ),
    right: yAxesConfiguration.find(
      ({ position }) => position === getAxisPosition(Position.Right, shouldRotate)
    ),
  };

  const titles = getLayersTitles(
    dataLayers,
    { splitColumnAccessor, splitRowAccessor },
    { xTitle },
    yAxesConfiguration
  );

  const filteredBarLayers = dataLayers.filter(({ seriesType }) => seriesType === SeriesTypes.BAR);

  const chartHasMoreThanOneBarSeries =
    filteredBarLayers.length > 1 ||
    filteredBarLayers.some((layer) => layer.accessors.length > 1) ||
    filteredBarLayers.some(
      (layer) => isDataLayer(layer) && layer.splitAccessors && layer.splitAccessors.length
    );

  const defaultXScaleType = isTimeViz ? XScaleTypes.TIME : XScaleTypes.ORDINAL;

  const isHistogramViz = dataLayers.every((l) => l.isHistogram);
  const isEsqlMode = dataLayers.some((l) => l.table?.meta?.type === ESQL_TABLE_TYPE);
  const hasBars = dataLayers.some((l) => l.seriesType === SeriesTypes.BAR);

  const { baseDomain: rawXDomain, extendedDomain: xDomain } = getXDomain(
    data.datatableUtilities,
    dataLayers,
    minInterval,
    isTimeViz,
    isHistogramViz,
    hasBars,
    timeZone,
    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[]) => {
    return axisSeries
      .map(({ layer, accessor }) => titles?.[layer]?.yTitles?.[accessor])
      .find((name) => Boolean(name));
  };

  const referenceLineLayers = getReferenceLayers(layers);
  const [rangeAnnotations, lineAnnotations] = isTimeViz
    ? partition(annotations?.datatable.rows, isRangeAnnotation)
    : [[], []];

  const groupedLineAnnotations = getAnnotationsGroupedByInterval(
    lineAnnotations as PointEventAnnotationRow[],
    annotations?.layers.flatMap((l) => l.annotations),
    annotations?.datatable.columns,
    formatFactory,
    timeFormat
  );

  const visualConfigs = [
    ...referenceLineLayers
      .flatMap<ReferenceLineDecorationConfig | ExtendedReferenceLineDecorationConfig | undefined>(
        ({ decorations }) => decorations
      )
      .map((config) => ({
        ...config,
        position: config
          ? getAxisGroupForReferenceLine(axesConfiguration, config, shouldRotate)?.position ??
            Position.Left
          : Position.Bottom,
      })),
    ...groupedLineAnnotations,
  ].filter(nonNullable);

  const shouldHideDetails =
    annotations?.layers && annotations.layers.length > 0
      ? annotations?.layers[0].simpleView
      : false;
  const linesPaddings = !shouldHideDetails
    ? getLinesCausedPaddings(visualConfigs, yAxesMap, shouldRotate)
    : {};

  const getYAxesStyle = (axis: AxisConfiguration) => {
    const tickVisible = axis.showLabels;
    const position = getOriginalAxisPosition(axis.position, shouldRotate);

    const style = {
      tickLabel: {
        fill: axis.labelColor,
        visible: tickVisible,
        rotation: axis.labelsOrientation,
        padding:
          linesPaddings[position] != null
            ? {
                inner: linesPaddings[position],
              }
            : undefined,
      },
      axisTitle: {
        visible: axis.showTitle,
        // if labels are not visible add the padding to the title
        padding:
          !tickVisible && linesPaddings[position] != null
            ? {
                inner: linesPaddings[position],
              }
            : undefined,
      },
    };
    return style;
  };

  const getYAxisDomain = (axis: GroupsConfiguration[number]) => {
    const extent: AxisExtentConfigResult = axis.extent || {
      type: 'axisExtentConfig',
      mode: 'full',
    };
    const hasBarOrArea = Boolean(
      axis.series.some((series) => {
        const layer = layersById[series.layer];
        if (!(layer && isDataLayer(layer))) {
          return false;
        }

        return layer.seriesType === SeriesTypes.BAR || layer.seriesType === SeriesTypes.AREA;
      })
    );

    const fit = Boolean(
      (!hasBarOrArea || axis.extent?.enforce) && extent.mode === AxisExtentModes.DATA_BOUNDS
    );
    const padding = axis.boundsMargin || undefined;

    let min: number = NaN;
    let max: number = NaN;
    if (extent.mode === 'custom') {
      const validExtent = validateExtent(hasBarOrArea, extent);
      if (validExtent || extent.enforce) {
        min = extent.lowerBound ?? NaN;
        max = extent.upperBound ?? NaN;
      }
    }

    return {
      fit,
      min,
      max,
      padding,
      includeDataFromIds: referenceLineLayers
        .flatMap(
          (l) => l.decorations?.map((decoration) => ({ layerId: l.layerId, decoration })) || []
        )
        .filter(({ decoration }) => {
          if (decoration.axisId) {
            return axis.groupId.includes(decoration.axisId);
          }

          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 = !uiState || valueLabels !== ValueLabelModes.HIDE;

  const valueLabelsStyling =
    shouldShowValueLabels &&
    valueLabels !== ValueLabelModes.HIDE &&
    getValueLabelsStyling(shouldRotate);

  const clickHandler: ElementClickListener = ([elementEvent]) => {
    // this cast is safe because we are rendering a cartesian chart
    const [xyGeometry, xySeries] = elementEvent as XYChartElementEvent;

    const layer = dataLayers.find((l) =>
      xySeries.seriesKeys.some((key: string | number) =>
        l.accessors.some(
          (accessor) => getAccessorByDimension(accessor, l.table.columns) === key.toString()
        )
      )
    );

    if (!layer) {
      return;
    }
    const { table } = layer;

    const xSeriesPoint = getXSeriesPoint(
      layer,
      xyGeometry.x,
      fieldFormats,
      formattedDatatables,
      xAxisFormatter,
      formatFactory
    );

    const splitPoints: FilterEvent['data']['data'] = [];

    if (xySeries.seriesKeys.length > 1) {
      xySeries.splitAccessors.forEach((value, accessor) => {
        const point = createSplitPoint(
          accessor,
          value,
          formattedDatatables[layer.layerId].table.rows,
          table
        );
        if (point) {
          splitPoints.push(point);
        }
      });
    }

    if (xySeries.smHorizontalAccessorValue && splitColumnAccessor) {
      const accessor = getAccessorByDimension(splitColumnAccessor, table.columns);
      const point = createSplitPoint(
        accessor,
        xySeries.smHorizontalAccessorValue,
        formattedDatatables[layer.layerId].table.rows,
        table
      );
      if (point) {
        splitPoints.push(point);
      }
    }

    if (xySeries.smVerticalAccessorValue && splitRowAccessor) {
      const accessor = getAccessorByDimension(splitRowAccessor, table.columns);
      const point = createSplitPoint(
        accessor,
        xySeries.smVerticalAccessorValue,
        formattedDatatables[layer.layerId].table.rows,
        table
      );
      if (point) {
        splitPoints.push(point);
      }
    }

    const context: FilterEvent['data'] = {
      data: [xSeriesPoint, ...splitPoints],
    };
    onClickValue(context);
  };

  const brushHandler = ({ x }: XYBrushEvent) => {
    if (!x) {
      return;
    }
    const [min, max] = x;
    if (!xAxisColumn || !isHistogramViz) {
      return;
    }

    const { table } = dataLayers[0];
    const xAccessor =
      dataLayers[0].xAccessor !== undefined
        ? getAccessorByDimension(dataLayers[0].xAccessor, table.columns)
        : undefined;
    const xAxisColumnIndex = table.columns.findIndex((el) => el.id === xAccessor);

    const context: BrushEvent['data'] = {
      range: [min, max],
      table,
      column: xAxisColumnIndex,
      ...(isEsqlMode ? { timeFieldName: table.columns[xAxisColumnIndex].name } : {}),
    };
    onSelectRange(context);
  };

  const legendInsideParams: LegendPositionConfig = {
    vAlign: legend.verticalAlignment ?? VerticalAlignment.Top,
    hAlign: legend?.horizontalAlignment ?? HorizontalAlignment.Right,
    direction: LayoutDirection.Vertical,
    floating: true,
    floatingColumns: legend?.floatingColumns ?? 1,
  };

  const isHistogramModeEnabled = dataLayers.some(
    ({ isHistogram, seriesType, isStacked }) =>
      isHistogram && (isStacked || seriesType !== SeriesTypes.BAR || !chartHasMoreThanOneBarSeries)
  );

  const shouldUseNewTimeAxis =
    isTimeViz && isHistogramModeEnabled && !useLegacyTimeAxis && !shouldRotate;

  const defaultXAxisPosition = shouldRotate ? Position.Left : Position.Bottom;

  const gridLineStyle = {
    visible: xAxisConfig?.showGridLines,
    strokeWidth: 1,
  };
  const xAxisStyle: RecursivePartial<AxisStyle> = shouldUseNewTimeAxis
    ? {
        ...MULTILAYER_TIME_AXIS_STYLE,
        tickLabel: {
          ...MULTILAYER_TIME_AXIS_STYLE.tickLabel,
          visible: Boolean(xAxisConfig?.showLabels),
          fill: xAxisConfig?.labelColor,
        },
        tickLine: {
          ...MULTILAYER_TIME_AXIS_STYLE.tickLine,
          visible: Boolean(xAxisConfig?.showLabels),
        },
        axisTitle: {
          visible: xAxisConfig?.showTitle,
        },
      }
    : {
        tickLabel: {
          visible: xAxisConfig?.showLabels,
          rotation: xAxisConfig?.labelsOrientation,
          padding: linesPaddings.bottom != null ? { inner: linesPaddings.bottom } : undefined,
          fill: xAxisConfig?.labelColor,
        },
        axisTitle: {
          visible: xAxisConfig?.showTitle,
          padding:
            !xAxisConfig?.showLabels && linesPaddings.bottom != null
              ? { inner: linesPaddings.bottom }
              : undefined,
        },
      };
  const isSplitChart = splitColumnAccessor || splitRowAccessor;
  const splitTable = isSplitChart ? dataLayers[0].table : undefined;
  const splitColumnId =
    splitColumnAccessor && splitTable
      ? getAccessorByDimension(splitColumnAccessor, splitTable?.columns)
      : undefined;

  const splitRowId =
    splitRowAccessor && splitTable
      ? getAccessorByDimension(splitRowAccessor, splitTable?.columns)
      : undefined;

  const chartContainerStyle = css({
    width: '100%',
    height: '100%',
    overflowX: 'hidden',
    position: uiState ? 'absolute' : 'relative',
  });

  const { theme: settingsThemeOverrides = {}, ...settingsOverrides } = getOverridesFor(
    overrides,
    'settings'
  ) as Partial<SettingsProps>;

  const referenceLinesFormatters = getReferenceLinesFormattersMap(
    referenceLineLayers,
    formatFactory
  );

  // ES|QL charts are allowed to create filters only when the unified search bar query is ES|QL (e.g. in Discover)
  const applicationQuery = data.query.queryString.getQuery();
  const canCreateFilters =
    !isEsqlMode || (isEsqlMode && applicationQuery && isOfAggregateQueryType(applicationQuery));
  // ES|QL charts are allowed to create alert rules only in dashboards
  const canCreateAlerts =
    isEsqlMode && applicationQuery && !isOfAggregateQueryType(applicationQuery);

  return (
    <>
      <GlobalXYChartStyles />
      <div css={chartContainerStyle}>
        {showLegend !== undefined && uiState && (
          <LegendToggle
            onClick={toggleLegend}
            showLegend={showLegend}
            legendPosition={legend.position}
          />
        )}
        <LegendColorPickerWrapperContext.Provider
          value={{
            uiState,
            setColor,
            legendPosition: legend.position,
            dataLayers,
            formattedDatatables,
            titles,
            fieldFormats,
            singleTable,
          }}
        >
          <Chart ref={chartRef} {...getOverridesFor(overrides, 'chart')}>
            <Tooltip<Record<string, string | number>, XYChartSeriesIdentifier>
              boundary={appFixedViewport}
              headerFormatter={
                !args.detailedTooltip && xAxisColumn
                  ? ({ value }) => (
                      <TooltipHeader
                        value={value}
                        formatter={safeXAccessorLabelRenderer}
                        xDomain={rawXDomain}
                      />
                    )
                  : undefined
              }
              actions={getTooltipActions(
                dataLayers,
                onClickMultiValue,
                onCreateAlertRule,
                fieldFormats,
                formattedDatatables,
                xAxisFormatter,
                formatFactory,
                isEsqlMode,
                canCreateAlerts,
                interactive && !args.detailedTooltip
              )}
              customTooltip={
                args.detailedTooltip
                  ? ({ header, values }) => (
                      <CustomTooltip
                        header={header}
                        values={values}
                        titles={titles}
                        fieldFormats={fieldFormats}
                        formatFactory={formatFactory}
                        formattedDatatables={formattedDatatables}
                        splitAccessors={{
                          splitColumnAccessor: splitColumnId,
                          splitRowAccessor: splitRowId,
                        }}
                        layers={dataLayers}
                        xDomain={isTimeViz ? rawXDomain : undefined}
                      />
                    )
                  : undefined
              }
              type={args.showTooltip ? TooltipType.VerticalCursor : TooltipType.None}
            />
            <Settings
              noResults={
                <EmptyPlaceholder
                  icon={icon}
                  renderComplete={onRenderChange}
                  css={xyChartEmptyStyles}
                />
              }
              onRenderChange={onRenderChange}
              pointerUpdateDebounce={0} // use the `handleCursorUpdate` debounce time
              onPointerUpdate={syncCursor ? handleCursorUpdate : undefined}
              externalPointerEvents={{
                tooltip: { visible: syncTooltips, placement: Placement.Right },
              }}
              legendColorPicker={uiState ? LegendColorPickerWrapper : undefined}
              debugState={window._echDebugStateFlag ?? false}
              showLegend={showLegend}
              legendPosition={legend?.isInside ? legendInsideParams : legend.position}
              legendSize={LegendSizeToPixels[legend.legendSize ?? DEFAULT_LEGEND_SIZE]}
              legendValues={isHistogramViz ? legend.legendStats : []}
              legendTitle={getLegendTitle(legend.title, dataLayers[0], legend.isTitleVisible)}
              theme={[
                {
                  barSeriesStyle: {
                    ...valueLabelsStyling,
                  },
                  background: {
                    color: undefined, // removes background for embeddables
                  },
                  legend: {
                    labelOptions: { maxLines: legend.shouldTruncate ? legend?.maxLines ?? 1 : 0 },
                  },
                  // if not title or labels are shown for axes, add some padding if required by reference line markers
                  chartMargins: {
                    // Temporary margin defaults
                    ...LEGACY_LIGHT_THEME.chartMargins,
                    ...computeChartMargins(
                      linesPaddings,
                      { ...tickLabelsVisibilitySettings, x: xAxisConfig?.showLabels },
                      { ...axisTitlesVisibilitySettings, x: xAxisConfig?.showTitle },
                      yAxesMap,
                      shouldRotate
                    ),
                  },
                  markSizeRatio: args.markSizeRatio,
                },
                ...(Array.isArray(settingsThemeOverrides)
                  ? settingsThemeOverrides
                  : [settingsThemeOverrides]),
              ]}
              baseTheme={chartBaseTheme}
              allowBrushingLastHistogramBin={isTimeViz}
              rotation={shouldRotate ? 90 : 0}
              xDomain={xDomain}
              // enable brushing only for time charts, for both ES|QL and DSL queries
              onBrushEnd={interactive ? (brushHandler as BrushEndListener) : undefined}
              onElementClick={interactive ? clickHandler : undefined}
              legendAction={
                interactive && canCreateFilters
                  ? getLegendAction(
                      dataLayers,
                      onClickValue,
                      layerCellValueActions,
                      fieldFormats,
                      formattedDatatables,
                      titles,
                      singleTable
                    )
                  : undefined
              }
              ariaLabel={args.ariaLabel}
              ariaUseDefaultSummary={!args.ariaLabel}
              orderOrdinalBinsBy={
                args.orderBucketsBySum
                  ? {
                      direction: Direction.Descending,
                    }
                  : undefined
              }
              locale={i18n.getLocale()}
              {...settingsOverrides}
            />
            <XYCurrentTime
              enabled={Boolean(args.addTimeMarker && isTimeViz)}
              isDarkMode={darkMode}
              domain={rawXDomain}
            />

            <Axis
              id="x"
              position={
                xAxisConfig?.position
                  ? getOriginalAxisPosition(xAxisConfig?.position, shouldRotate)
                  : defaultXAxisPosition
              }
              title={xTitle}
              gridLine={gridLineStyle}
              hide={xAxisConfig?.hide || dataLayers[0]?.simpleView || !dataLayers[0]?.xAccessor}
              tickFormat={(d) => {
                let value = safeXAccessorLabelRenderer(d) || '';
                if (xAxisConfig?.truncate && value.length > xAxisConfig.truncate) {
                  value = `${value.slice(0, xAxisConfig.truncate)}...`;
                }
                return value;
              }}
              style={xAxisStyle}
              showOverlappingLabels={xAxisConfig?.showOverlappingLabels}
              showDuplicatedTicks={xAxisConfig?.showDuplicates}
              timeAxisLayerCount={shouldUseNewTimeAxis ? 2 : 0}
              {...getOverridesFor(overrides, 'axisX')}
            />
            {isSplitChart && splitTable && (
              <SplitChart
                splitColumnAccessor={splitColumnAccessor}
                splitRowAccessor={splitRowAccessor}
                columns={splitTable.columns}
              />
            )}
            {yAxesConfiguration.map((axis) => {
              return (
                <Axis
                  key={axis.groupId}
                  id={axis.groupId}
                  groupId={axis.groupId}
                  position={axis.position}
                  title={axis.title || getYAxesTitles(axis.series)}
                  gridLine={{
                    visible: axis.showGridLines,
                  }}
                  hide={axis.hide || dataLayers[0]?.simpleView}
                  tickFormat={(d) => {
                    let value = axis.formatter?.convert(d) || '';
                    if (axis.truncate && value.length > axis.truncate) {
                      value = `${value.slice(0, axis.truncate)}...`;
                    }
                    return value;
                  }}
                  style={getYAxesStyle(axis)}
                  domain={getYAxisDomain(axis)}
                  showOverlappingLabels={axis.showOverlappingLabels}
                  showDuplicatedTicks={axis.showDuplicates}
                  {...getOverridesFor(
                    overrides,
                    /left/i.test(axis.groupId) ? 'axisLeft' : 'axisRight'
                  )}
                />
              );
            })}

            {!hideEndzones && (
              <XyEndzones
                baseDomain={rawXDomain}
                extendedDomain={xDomain}
                darkMode={darkMode}
                histogramMode={dataLayers.every(
                  (layer) =>
                    layer.isHistogram &&
                    (layer.isStacked || !layer.splitAccessors || !layer.splitAccessors.length) &&
                    (layer.isStacked ||
                      layer.seriesType !== SeriesTypes.BAR ||
                      !chartHasMoreThanOneBarSeries)
                )}
              />
            )}

            {dataLayers.length && (
              <DataLayers
                titles={titles}
                layers={dataLayers}
                endValue={endValue}
                timeZone={timeZone}
                syncColors={syncColors}
                valueLabels={valueLabels}
                fillOpacity={args.fillOpacity}
                minBarHeight={args.minBarHeight}
                formatFactory={formatFactory}
                paletteService={paletteService}
                palettes={palettes}
                fittingFunction={fittingFunction}
                emphasizeFitting={emphasizeFitting}
                yAxesConfiguration={yAxesConfiguration}
                xAxisConfiguration={
                  xAxisConfig ? axesConfiguration[axesConfiguration.length - 1] : undefined
                }
                shouldShowValueLabels={shouldShowValueLabels}
                formattedDatatables={formattedDatatables}
                chartHasMoreThanOneBarSeries={chartHasMoreThanOneBarSeries}
                defaultXScaleType={defaultXScaleType}
                fieldFormats={fieldFormats}
                uiState={uiState}
                singleTable={singleTable}
                isDarkMode={darkMode}
              />
            )}
            {referenceLineLayers.length ? (
              <ReferenceLines
                layers={referenceLineLayers}
                xAxisFormatter={xAxisFormatter}
                axesConfiguration={axesConfiguration}
                isHorizontal={shouldRotate}
                paddingMap={linesPaddings}
                titles={titles}
                yAxesMap={yAxesMap}
                formatters={referenceLinesFormatters}
              />
            ) : null}
            {(rangeAnnotations.length || lineAnnotations.length) && isTimeViz ? (
              <Annotations
                rangeAnnotations={rangeAnnotations}
                groupedLineAnnotations={groupedLineAnnotations}
                timeFormat={timeFormat}
                isHorizontal={shouldRotate}
                paddingMap={linesPaddings}
                isBarChart={filteredBarLayers.length > 0}
                minInterval={minInterval}
                simpleView={shouldHideDetails}
                outsideDimension={
                  rangeAnnotations.length && shouldHideDetails
                    ? OUTSIDE_RECT_ANNOTATION_WIDTH_SUGGESTION
                    : shouldUseNewTimeAxis
                    ? Number(MULTILAYER_TIME_AXIS_STYLE.tickLine?.padding ?? 0) +
                      chartBaseTheme.axes.tickLabel.fontSize
                    : Math.max(chartBaseTheme.axes.tickLine.size, OUTSIDE_RECT_ANNOTATION_WIDTH)
                }
              />
            ) : null}
          </Chart>
        </LegendColorPickerWrapperContext.Provider>
      </div>
    </>
  );
}