Skip to content

Commit

Permalink
[APM] Transaction duration histogram buckets without samples are clic…
Browse files Browse the repository at this point in the history
…kable (#98540)

* adding no samples label on distribution chart

* adding custom tooltip

* addressing PR comments
  • Loading branch information
cauemarcondes committed Apr 29, 2021
1 parent 0f15a12 commit d111c27
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 14 deletions.
Original file line number Diff line number Diff line change
@@ -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 (
<div className="echTooltip">
<>
<div className="echTooltip__header">{headerTitle}</div>
<div className="echTooltip__list">
<div className="echTooltip__item">
<div
className="echTooltip__item--backgroundColor"
style={{ backgroundColor: 'transparent' }}
>
<div
className="echTooltip__item--color"
style={{ backgroundColor: color }}
/>
</div>
<div className="echTooltip__item--container">
<span className="echTooltip__label">{formatYLong(value)}</span>
<span className="echTooltip__value">{value}</span>
</div>
</div>
</div>
</>
{isSamplesEmpty && (
<div style={{ padding: theme.eui.paddingSizes.xs, display: 'flex' }}>
<EuiIcon type="iInCircle" />
<EuiText size="xs">
{i18n.translate(
'xpack.apm.transactionDetails.transactionsDurationDistributionChart.noSamplesAvailable',
{ defaultMessage: 'No samples available' }
)}
</EuiText>
</div>
)}
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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;
Expand Down Expand Up @@ -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',
{
Expand Down Expand Up @@ -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 (
<CustomTooltip
{...props}
isSamplesEmpty={isEmpty(selectedDistribution?.samples)}
serie={serie}
timeFormatter={timeFormatter}
/>
);
},
};

Expand Down Expand Up @@ -192,7 +200,7 @@ export function TransactionDistribution({
<Chart>
<Settings
xDomain={{ min: xMin, max: xMax }}
tooltip={tooltipProps}
tooltip={tooltip}
onProjectionClick={onBarClick}
/>
{selectedBucket && (
Expand Down

0 comments on commit d111c27

Please sign in to comment.