Skip to content

Commit f6c23b3

Browse files
[APM] Chart units don't update when toggling the chart legends (#74931) (#76273)
* changing unit when legend is disabled * changing unit when legend is disabled * show individual units in the tooltip * addressing PR comment * increasing duration threshold * change formatter based on available legends * addressing PR comment Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com> Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
1 parent 6f622d9 commit f6c23b3

File tree

11 files changed

+457
-205
lines changed

11 files changed

+457
-205
lines changed

x-pack/plugins/apm/public/components/shared/charts/CustomPlot/index.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@ export class InnerCustomPlot extends PureComponent {
7979
return i === _i ? !disabledValue : !!disabledValue;
8080
});
8181

82+
if (typeof this.props.onToggleLegend === 'function') {
83+
this.props.onToggleLegend(nextSeriesEnabledState);
84+
}
85+
8286
return {
8387
seriesEnabledState: nextSeriesEnabledState,
8488
};
@@ -235,6 +239,7 @@ InnerCustomPlot.propTypes = {
235239
})
236240
),
237241
noHits: PropTypes.bool,
242+
onToggleLegend: PropTypes.func,
238243
};
239244

240245
InnerCustomPlot.defaultProps = {

x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/BrowserLineChart.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,17 @@
44
* you may not use this file except in compliance with the Elastic License.
55
*/
66

7-
import React from 'react';
8-
import { i18n } from '@kbn/i18n';
97
import { EuiTitle } from '@elastic/eui';
10-
import { TransactionLineChart } from './TransactionLineChart';
8+
import { i18n } from '@kbn/i18n';
9+
import React from 'react';
10+
import { useAvgDurationByBrowser } from '../../../../hooks/useAvgDurationByBrowser';
11+
import { getDurationFormatter } from '../../../../utils/formatters';
1112
import {
12-
getMaxY,
1313
getResponseTimeTickFormatter,
1414
getResponseTimeTooltipFormatter,
15-
} from '.';
16-
import { getDurationFormatter } from '../../../../utils/formatters';
17-
import { useAvgDurationByBrowser } from '../../../../hooks/useAvgDurationByBrowser';
15+
getMaxY,
16+
} from './helper';
17+
import { TransactionLineChart } from './TransactionLineChart';
1818

1919
export function BrowserLineChart() {
2020
const { data } = useAvgDurationByBrowser();

x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/TransactionLineChart/index.tsx

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,29 +5,21 @@
55
*/
66

77
import React, { useCallback } from 'react';
8-
import {
9-
Coordinate,
10-
RectCoordinate,
11-
} from '../../../../../../typings/timeseries';
8+
import { Coordinate, TimeSeries } from '../../../../../../typings/timeseries';
129
import { useChartsSync } from '../../../../../hooks/useChartsSync';
1310
// @ts-ignore
1411
import CustomPlot from '../../CustomPlot';
1512

1613
interface Props {
17-
series: Array<{
18-
color: string;
19-
title: React.ReactNode;
20-
titleShort?: React.ReactNode;
21-
data: Array<Coordinate | RectCoordinate>;
22-
type: string;
23-
}>;
14+
series: TimeSeries[];
2415
truncateLegends?: boolean;
2516
tickFormatY: (y: number) => React.ReactNode;
2617
formatTooltipValue: (c: Coordinate) => React.ReactNode;
2718
yMax?: string | number;
2819
height?: number;
2920
stacked?: boolean;
3021
onHover?: () => void;
22+
onToggleLegend?: (disabledSeriesState: boolean[]) => void;
3123
}
3224

3325
function TransactionLineChart(props: Props) {
@@ -40,6 +32,7 @@ function TransactionLineChart(props: Props) {
4032
truncateLegends,
4133
stacked = false,
4234
onHover,
35+
onToggleLegend,
4336
} = props;
4437

4538
const syncedChartsProps = useChartsSync();
@@ -66,6 +59,7 @@ function TransactionLineChart(props: Props) {
6659
height={height}
6760
truncateLegends={truncateLegends}
6861
{...(stacked ? { stackBy: 'y' } : {})}
62+
onToggleLegend={onToggleLegend}
6963
/>
7064
);
7165
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
import {
8+
getResponseTimeTickFormatter,
9+
getResponseTimeTooltipFormatter,
10+
getMaxY,
11+
} from './helper';
12+
import {
13+
getDurationFormatter,
14+
toMicroseconds,
15+
} from '../../../../utils/formatters';
16+
import { TimeSeries } from '../../../../../typings/timeseries';
17+
18+
describe('transaction chart helper', () => {
19+
describe('getResponseTimeTickFormatter', () => {
20+
it('formattes time tick in minutes', () => {
21+
const formatter = getDurationFormatter(toMicroseconds(11, 'minutes'));
22+
const timeTickFormatter = getResponseTimeTickFormatter(formatter);
23+
expect(timeTickFormatter(toMicroseconds(60, 'seconds'))).toEqual(
24+
'1.0 min'
25+
);
26+
});
27+
it('formattes time tick in seconds', () => {
28+
const formatter = getDurationFormatter(toMicroseconds(11, 'seconds'));
29+
const timeTickFormatter = getResponseTimeTickFormatter(formatter);
30+
expect(timeTickFormatter(toMicroseconds(6, 'seconds'))).toEqual('6.0 s');
31+
});
32+
});
33+
describe('getResponseTimeTooltipFormatter', () => {
34+
const formatter = getDurationFormatter(toMicroseconds(11, 'minutes'));
35+
const tooltipFormatter = getResponseTimeTooltipFormatter(formatter);
36+
it("doesn't format invalid y coordinate", () => {
37+
expect(tooltipFormatter({ x: 1, y: undefined })).toEqual('N/A');
38+
expect(tooltipFormatter({ x: 1, y: null })).toEqual('N/A');
39+
});
40+
it('formattes tooltip in minutes', () => {
41+
expect(
42+
tooltipFormatter({ x: 1, y: toMicroseconds(60, 'seconds') })
43+
).toEqual('1.0 min');
44+
});
45+
});
46+
describe('getMaxY', () => {
47+
it('returns zero when empty time series', () => {
48+
expect(getMaxY([])).toEqual(0);
49+
});
50+
it('returns zero for invalid y coordinate', () => {
51+
const timeSeries = ([
52+
{ data: [{ x: 1 }, { x: 2 }, { x: 3, y: -1 }] },
53+
] as unknown) as TimeSeries[];
54+
expect(getMaxY(timeSeries)).toEqual(0);
55+
});
56+
it('returns the max y coordinate', () => {
57+
const timeSeries = ([
58+
{
59+
data: [
60+
{ x: 1, y: 10 },
61+
{ x: 2, y: 5 },
62+
{ x: 3, y: 1 },
63+
],
64+
},
65+
] as unknown) as TimeSeries[];
66+
expect(getMaxY(timeSeries)).toEqual(10);
67+
});
68+
});
69+
});
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
import { flatten } from 'lodash';
8+
import { NOT_AVAILABLE_LABEL } from '../../../../../common/i18n';
9+
import { isValidCoordinateValue } from '../../../../utils/isValidCoordinateValue';
10+
import { TimeSeries, Coordinate } from '../../../../../typings/timeseries';
11+
import { TimeFormatter } from '../../../../utils/formatters';
12+
13+
export function getResponseTimeTickFormatter(formatter: TimeFormatter) {
14+
return (t: number) => {
15+
return formatter(t).formatted;
16+
};
17+
}
18+
19+
export function getResponseTimeTooltipFormatter(formatter: TimeFormatter) {
20+
return (coordinate: Coordinate) => {
21+
return isValidCoordinateValue(coordinate.y)
22+
? formatter(coordinate.y).formatted
23+
: NOT_AVAILABLE_LABEL;
24+
};
25+
}
26+
27+
export function getMaxY(timeSeries: TimeSeries[]) {
28+
const coordinates = flatten(
29+
timeSeries.map((serie: TimeSeries) => serie.data as Coordinate[])
30+
);
31+
32+
const numbers: number[] = coordinates.map((c: Coordinate) => (c.y ? c.y : 0));
33+
34+
return Math.max(...numbers, 0);
35+
}

0 commit comments

Comments
 (0)