"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.TimeseriesChart = TimeseriesChart; var _charts = require("@elastic/charts"); var _eui = require("@elastic/eui"); var _i18n = require("@kbn/i18n"); var _react = _interopRequireDefault(require("react")); var _reactRouterDom = require("react-router-dom"); var _public = require("@kbn/observability-shared-plugin/public"); var _get_comparison_options = require("../time_comparison/get_comparison_options"); var _use_chart_pointer_event_context = require("../../../context/chart_pointer_event/use_chart_pointer_event_context"); var _use_theme = require("../../../hooks/use_theme"); var _style = require("../../../utils/style"); var _chart_container = require("./chart_container"); var _get_chart_anomaly_timeseries = require("./helper/get_chart_anomaly_timeseries"); var _helper = require("./helper/helper"); /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ const END_ZONE_LABEL = _i18n.i18n.translate('xpack.apm.timeseries.endzone', { defaultMessage: 'The selected time range does not include this entire bucket. It might contain partial data.' }); function TimeseriesChart({ id, height = _style.unit * 16, fetchStatus, onToggleLegend, timeseries, yLabelFormat, yTickFormat, showAnnotations = true, yDomain, anomalyTimeseries, customTheme = {}, comparisonEnabled, offset, timeZone, annotations }) { var _anomalyChartTimeseri, _anomalyChartTimeseri2, _anomalyChartTimeseri3, _anomalyChartTimeseri4; const history = (0, _reactRouterDom.useHistory)(); const { chartRef, updatePointerEvent } = (0, _use_chart_pointer_event_context.useChartPointerEventContext)(); const theme = (0, _use_theme.useTheme)(); const chartTheme = (0, _public.useChartTheme)(); const anomalyChartTimeseries = (0, _get_chart_anomaly_timeseries.getChartAnomalyTimeseries)({ anomalyTimeseries, theme, anomalyTimeseriesColor: anomalyTimeseries === null || anomalyTimeseries === void 0 ? void 0 : anomalyTimeseries.color }); const isEmpty = (0, _helper.isTimeseriesEmpty)(timeseries); const isComparingExpectedBounds = comparisonEnabled && (0, _get_comparison_options.isExpectedBoundsComparison)(offset); const allSeries = [...timeseries, ...(isComparingExpectedBounds ? (_anomalyChartTimeseri = anomalyChartTimeseries === null || anomalyChartTimeseries === void 0 ? void 0 : anomalyChartTimeseries.boundaries) !== null && _anomalyChartTimeseri !== void 0 ? _anomalyChartTimeseri : [] : []), ...((_anomalyChartTimeseri2 = anomalyChartTimeseries === null || anomalyChartTimeseries === void 0 ? void 0 : anomalyChartTimeseries.scores) !== null && _anomalyChartTimeseri2 !== void 0 ? _anomalyChartTimeseri2 : [])] // Sorting series so that area type series are before line series // This is a workaround so that the legendSort works correctly // Can be removed when https://github.com/elastic/elastic-charts/issues/1685 is resolved .sort(isComparingExpectedBounds ? (prev, curr) => prev.type.localeCompare(curr.type) : undefined); const xValues = timeseries.flatMap(({ data }) => data.map(({ x }) => x)); const xValuesExpectedBounds = (_anomalyChartTimeseri3 = anomalyChartTimeseries === null || anomalyChartTimeseries === void 0 ? void 0 : (_anomalyChartTimeseri4 = anomalyChartTimeseries.boundaries) === null || _anomalyChartTimeseri4 === void 0 ? void 0 : _anomalyChartTimeseri4.flatMap(({ data }) => data.map(({ x }) => x))) !== null && _anomalyChartTimeseri3 !== void 0 ? _anomalyChartTimeseri3 : []; const min = Math.min(...xValues); const max = Math.max(...xValues, ...xValuesExpectedBounds); const xFormatter = (0, _charts.niceTimeFormatter)([min, max]); const xDomain = isEmpty ? { min: 0, max: 1 } : { min, max }; // Using custom legendSort here when comparing expected bounds // because by default elastic-charts will show legends for expected bounds first // but for consistency, we are making `Expected bounds` last // See https://github.com/elastic/elastic-charts/issues/1685 const legendSort = isComparingExpectedBounds ? (a, b) => { if ((a === null || a === void 0 ? void 0 : a.specId) === _get_chart_anomaly_timeseries.expectedBoundsTitle) return -1; if ((b === null || b === void 0 ? void 0 : b.specId) === _get_chart_anomaly_timeseries.expectedBoundsTitle) return -1; return 1; } : undefined; const endZoneColor = theme.darkMode ? theme.eui.euiColorLightShade : theme.eui.euiColorDarkShade; const endZoneRectAnnotationStyle = { stroke: endZoneColor, fill: endZoneColor, strokeWidth: 0, opacity: theme.darkMode ? 0.6 : 0.2 }; function getChartType(type) { switch (type) { case 'area': return _charts.AreaSeries; case 'bar': return _charts.BarSeries; default: return _charts.LineSeries; } } return /*#__PURE__*/_react.default.createElement(_chart_container.ChartContainer, { hasData: !isEmpty, height: height, status: fetchStatus, id: id }, /*#__PURE__*/_react.default.createElement(_charts.Chart, { ref: chartRef, id: id }, /*#__PURE__*/_react.default.createElement(_charts.Tooltip, { stickTo: "top", showNullValues: false, headerFormatter: ({ value }) => { const formattedValue = xFormatter(value); if (max === value) { return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_eui.EuiFlexGroup, { alignItems: "center", responsive: false, gutterSize: "xs", style: { fontWeight: 'normal' } }, /*#__PURE__*/_react.default.createElement(_eui.EuiFlexItem, { grow: false }, /*#__PURE__*/_react.default.createElement(_eui.EuiIcon, { type: "iInCircle" })), /*#__PURE__*/_react.default.createElement(_eui.EuiFlexItem, null, END_ZONE_LABEL)), /*#__PURE__*/_react.default.createElement(_eui.EuiSpacer, { size: "xs" }), formattedValue); } return formattedValue; } }), /*#__PURE__*/_react.default.createElement(_charts.Settings, { onBrushEnd: event => (0, _helper.onBrushEnd)({ x: event.x, history }), theme: [customTheme, { areaSeriesStyle: { line: { visible: false } } }, ...chartTheme], onPointerUpdate: updatePointerEvent, externalPointerEvents: { tooltip: { visible: true } }, showLegend: true, legendSort: legendSort, legendPosition: _charts.Position.Bottom, xDomain: xDomain, onLegendItemClick: legend => { if (onToggleLegend) { onToggleLegend(legend); } } }), /*#__PURE__*/_react.default.createElement(_charts.Axis, { id: "x-axis", position: _charts.Position.Bottom, showOverlappingTicks: true, tickFormat: xFormatter, gridLine: { visible: false } }), /*#__PURE__*/_react.default.createElement(_charts.Axis, { domain: yDomain, id: "y-axis", ticks: 3, position: _charts.Position.Left, tickFormat: yTickFormat ? yTickFormat : yLabelFormat, labelFormat: yLabelFormat }), showAnnotations && annotations, /*#__PURE__*/_react.default.createElement(_charts.RectAnnotation, { id: "__endzones__", zIndex: 2, dataValues: [{ coordinates: { x0: xValues[xValues.length - 2] }, details: END_ZONE_LABEL }], style: endZoneRectAnnotationStyle }), allSeries.map(serie => { var _serie$yAccessors, _serie$stackAccessors, _serie$fit; const Series = getChartType(serie.type); return /*#__PURE__*/_react.default.createElement(Series, { timeZone: timeZone, key: serie.title, id: serie.id || serie.title, groupId: serie.groupId, xScaleType: _charts.ScaleType.Time, yScaleType: _charts.ScaleType.Linear, xAccessor: "x", yAccessors: (_serie$yAccessors = serie.yAccessors) !== null && _serie$yAccessors !== void 0 ? _serie$yAccessors : ['y'], y0Accessors: serie.y0Accessors, stackAccessors: (_serie$stackAccessors = serie.stackAccessors) !== null && _serie$stackAccessors !== void 0 ? _serie$stackAccessors : undefined, markSizeAccessor: serie.markSizeAccessor, data: isEmpty ? [] : serie.data, color: serie.color, curve: _charts.CurveType.CURVE_MONOTONE_X, hideInLegend: serie.hideLegend, fit: (_serie$fit = serie.fit) !== null && _serie$fit !== void 0 ? _serie$fit : undefined, filterSeriesInTooltip: serie.hideTooltipValue ? () => false : undefined, areaSeriesStyle: serie.areaSeriesStyle, lineSeriesStyle: serie.lineSeriesStyle }); }))); }