From ed4a2926c40a6ba9bf5c411316597e7af32cbc06 Mon Sep 17 00:00:00 2001 From: Brittany Joiner Date: Tue, 11 Aug 2020 09:20:15 -0500 Subject: [PATCH] pluralized for occurrences vs occurrence (#74564) Co-authored-by: Elastic Machine # Conflicts: # x-pack/plugins/apm/public/components/app/ErrorGroupDetails/Distribution/index.tsx --- .../ErrorGroupDetails/Distribution/index.tsx | 129 ++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 x-pack/plugins/apm/public/components/app/ErrorGroupDetails/Distribution/index.tsx diff --git a/x-pack/plugins/apm/public/components/app/ErrorGroupDetails/Distribution/index.tsx b/x-pack/plugins/apm/public/components/app/ErrorGroupDetails/Distribution/index.tsx new file mode 100644 index 0000000000000..ecdd52e31730c --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/ErrorGroupDetails/Distribution/index.tsx @@ -0,0 +1,129 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { EuiTitle } from '@elastic/eui'; +import theme from '@elastic/eui/dist/eui_theme_light.json'; +import numeral from '@elastic/numeral'; +import { i18n } from '@kbn/i18n'; +import d3 from 'd3'; +import { scaleUtc } from 'd3-scale'; +import { mean } from 'lodash'; +import React from 'react'; +import { asRelativeDateTimeRange } from '../../../../utils/formatters'; +import { getTimezoneOffsetInMs } from '../../../shared/charts/CustomPlot/getTimezoneOffsetInMs'; +// @ts-ignore +import Histogram from '../../../shared/charts/Histogram'; +import { EmptyMessage } from '../../../shared/EmptyMessage'; + +interface IBucket { + key: number; + count: number | undefined; +} + +// TODO: cleanup duplication of this in distribution/get_distribution.ts (ErrorDistributionAPIResponse) and transactions/distribution/index.ts (TransactionDistributionAPIResponse) +interface IDistribution { + noHits: boolean; + buckets: IBucket[]; + bucketSize: number; +} + +interface FormattedBucket { + x0: number; + x: number; + y: number | undefined; +} + +export function getFormattedBuckets( + buckets: IBucket[], + bucketSize: number +): FormattedBucket[] | null { + if (!buckets) { + return null; + } + + return buckets.map(({ count, key }) => { + return { + x0: key, + x: key + bucketSize, + y: count, + }; + }); +} + +interface Props { + distribution: IDistribution; + title: React.ReactNode; +} + +const tooltipHeader = (bucket: FormattedBucket) => + asRelativeDateTimeRange(bucket.x0, bucket.x); + +export function ErrorDistribution({ distribution, title }: Props) { + const buckets = getFormattedBuckets( + distribution.buckets, + distribution.bucketSize + ); + + if (!buckets) { + return ( + + ); + } + + const averageValue = mean(buckets.map((bucket) => bucket.y)) || 0; + const xMin = d3.min(buckets, (d) => d.x0); + const xMax = d3.max(buckets, (d) => d.x); + const tickFormat = scaleUtc().domain([xMin, xMax]).tickFormat(); + + return ( +
+ + {title} + + bucket.x} + xType="time-utc" + formatX={(value: Date) => { + const time = value.getTime(); + return tickFormat(new Date(time - getTimezoneOffsetInMs(time))); + }} + buckets={buckets} + bucketSize={distribution.bucketSize} + formatYShort={(value: number) => + i18n.translate('xpack.apm.errorGroupDetails.occurrencesShortLabel', { + defaultMessage: '{occCount} occ.', + values: { occCount: value }, + }) + } + formatYLong={(value: number) => + i18n.translate('xpack.apm.errorGroupDetails.occurrencesLongLabel', { + defaultMessage: + '{occCount} {occCount, plural, one {occurrence} other {occurrences}}', + values: { occCount: value }, + }) + } + legends={[ + { + color: theme.euiColorVis1, + // 0a abbreviates large whole numbers with metric prefixes like: 1000 = 1k, 32000 = 32k, 1000000 = 1m + legendValue: numeral(averageValue).format('0a'), + title: i18n.translate('xpack.apm.errorGroupDetails.avgLabel', { + defaultMessage: 'Avg.', + }), + legendClickDisabled: true, + }, + ]} + /> +
+ ); +}