From d111c27808e16f9615e8ffd91f315f14339a0b00 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Cau=C3=AA=20Marcondes?=
<55978943+cauemarcondes@users.noreply.github.com>
Date: Thu, 29 Apr 2021 16:57:06 -0400
Subject: [PATCH] [APM] Transaction duration histogram buckets without samples
are clickable (#98540)
* adding no samples label on distribution chart
* adding custom tooltip
* addressing PR comments
---
.../Distribution/custom_tooltip.tsx | 68 +++++++++++++++++++
.../Distribution/index.tsx | 36 ++++++----
2 files changed, 90 insertions(+), 14 deletions(-)
create mode 100644 x-pack/plugins/apm/public/components/app/transaction_details/Distribution/custom_tooltip.tsx
diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/Distribution/custom_tooltip.tsx b/x-pack/plugins/apm/public/components/app/transaction_details/Distribution/custom_tooltip.tsx
new file mode 100644
index 00000000000000..ba007015b25f86
--- /dev/null
+++ b/x-pack/plugins/apm/public/components/app/transaction_details/Distribution/custom_tooltip.tsx
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+import { TooltipInfo } from '@elastic/charts';
+import { EuiIcon, EuiText } from '@elastic/eui';
+import { i18n } from '@kbn/i18n';
+import React from 'react';
+import { TimeFormatter } from '../../../../../common/utils/formatters';
+import { useTheme } from '../../../../hooks/use_theme';
+import { formatYLong, IChartPoint } from './';
+
+export function CustomTooltip(
+ props: TooltipInfo & {
+ serie?: IChartPoint;
+ isSamplesEmpty: boolean;
+ timeFormatter: TimeFormatter;
+ }
+) {
+ const theme = useTheme();
+ const { values, header, serie, isSamplesEmpty, timeFormatter } = props;
+ const { color, value } = values[0];
+
+ let headerTitle = `${timeFormatter(header?.value)}`;
+ if (serie) {
+ const xFormatted = timeFormatter(serie.x);
+ const x0Formatted = timeFormatter(serie.x0);
+ headerTitle = `${x0Formatted.value} - ${xFormatted.value} ${xFormatted.unit}`;
+ }
+
+ return (
+
+ <>
+
{headerTitle}
+
+
+
+
+ {formatYLong(value)}
+ {value}
+
+
+
+ >
+ {isSamplesEmpty && (
+
+
+
+ {i18n.translate(
+ 'xpack.apm.transactionDetails.transactionsDurationDistributionChart.noSamplesAvailable',
+ { defaultMessage: 'No samples available' }
+ )}
+
+
+ )}
+
+ );
+}
diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/Distribution/index.tsx b/x-pack/plugins/apm/public/components/app/transaction_details/Distribution/index.tsx
index 6d621afc99e53c..c7dae6ce3d1d43 100644
--- a/x-pack/plugins/apm/public/components/app/transaction_details/Distribution/index.tsx
+++ b/x-pack/plugins/apm/public/components/app/transaction_details/Distribution/index.tsx
@@ -15,13 +15,13 @@ import {
ScaleType,
Settings,
SettingsSpec,
- TooltipValue,
+ TooltipInfo,
XYChartSeriesIdentifier,
} from '@elastic/charts';
import { EuiIconTip, EuiTitle } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import d3 from 'd3';
-import { isEmpty } from 'lodash';
+import { isEmpty, keyBy } from 'lodash';
import React from 'react';
import { ValuesType } from 'utility-types';
import { getDurationFormatter } from '../../../../../common/utils/formatters';
@@ -32,12 +32,13 @@ import { APIReturnType } from '../../../../services/rest/createCallApmApi';
import { unit } from '../../../../style/variables';
import { ChartContainer } from '../../../shared/charts/chart_container';
import { EmptyMessage } from '../../../shared/EmptyMessage';
+import { CustomTooltip } from './custom_tooltip';
type TransactionDistributionAPIResponse = APIReturnType<'GET /api/apm/services/{serviceName}/transactions/charts/distribution'>;
type DistributionBucket = TransactionDistributionAPIResponse['buckets'][0];
-interface IChartPoint {
+export interface IChartPoint {
x0: number;
x: number;
y: number;
@@ -78,7 +79,7 @@ const formatYShort = (t: number) => {
);
};
-const formatYLong = (t: number) => {
+export const formatYLong = (t: number) => {
return i18n.translate(
'xpack.apm.transactionDetails.transactionsDurationDistributionChart.transactionTypeUnitLongLabel',
{
@@ -133,15 +134,22 @@ export function TransactionDistribution({
const xMax = d3.max(buckets, (d) => d.x0) || 0;
const timeFormatter = getDurationFormatter(xMax);
- const tooltipProps: SettingsSpec['tooltip'] = {
- headerFormatter: (tooltip: TooltipValue) => {
- const serie = buckets.find((bucket) => bucket.x0 === tooltip.value);
- if (serie) {
- const xFormatted = timeFormatter(serie.x);
- const x0Formatted = timeFormatter(serie.x0);
- return `${x0Formatted.value} - ${xFormatted.value} ${xFormatted.unit}`;
- }
- return `${timeFormatter(tooltip.value)}`;
+ const distributionMap = keyBy(distribution?.buckets, 'key');
+ const bucketsMap = keyBy(buckets, 'x0');
+
+ const tooltip: SettingsSpec['tooltip'] = {
+ customTooltip: (props: TooltipInfo) => {
+ const datum = props.header?.datum as IChartPoint;
+ const selectedDistribution = distributionMap[datum?.x0];
+ const serie = bucketsMap[datum?.x0];
+ return (
+
+ );
},
};
@@ -192,7 +200,7 @@ export function TransactionDistribution({
{selectedBucket && (