From 9e149d5811389554bed6bdd9008a730cd712471c Mon Sep 17 00:00:00 2001 From: Priscila Oliveira Date: Wed, 3 Jul 2024 17:54:30 +0200 Subject: [PATCH] feat(stats): Display subseries in tooltip --- .../organizationStats/usageChart/index.tsx | 17 ++-- .../views/organizationStats/usageStatsOrg.tsx | 90 ++++++++++++++++--- 2 files changed, 83 insertions(+), 24 deletions(-) diff --git a/static/app/views/organizationStats/usageChart/index.tsx b/static/app/views/organizationStats/usageChart/index.tsx index 178c38eead9b9a..4d0bc915737e32 100644 --- a/static/app/views/organizationStats/usageChart/index.tsx +++ b/static/app/views/organizationStats/usageChart/index.tsx @@ -10,6 +10,7 @@ import type { import BaseChart from 'sentry/components/charts/baseChart'; import Legend from 'sentry/components/charts/components/legend'; +import type {TooltipSubLabel} from 'sentry/components/charts/components/tooltip'; import xAxis from 'sentry/components/charts/components/xAxis'; import barSeries from 'sentry/components/charts/series/barSeries'; import {ChartContainer, HeaderTitleLegend} from 'sentry/components/charts/styles'; @@ -151,6 +152,8 @@ export type UsageChartProps = { */ chartSeries?: SeriesOption[]; + chartSubLabels?: TooltipSubLabel[]; + /** * Replace default tooltip */ @@ -348,7 +351,7 @@ function UsageChartBody({ dataCategory, dataTransform, chartSeries, - chartTooltip, + chartSubLabels, categoryColors, isLoading, isError, @@ -506,17 +509,7 @@ function UsageChartBody({ }, }} series={series} - tooltip={ - chartTooltip - ? chartTooltip - : { - // Trigger to axis prevents tooltip from redrawing when hovering - // over individual bars - trigger: 'axis', - valueFormatter: tooltipValueFormatter, - } - } - onLegendSelectChanged={() => {}} + tooltip={{subLabels: chartSubLabels}} legend={Legend({ right: 10, top: 5, diff --git a/static/app/views/organizationStats/usageStatsOrg.tsx b/static/app/views/organizationStats/usageStatsOrg.tsx index 23d4dd25a316d3..2eef98e77e6a9d 100644 --- a/static/app/views/organizationStats/usageStatsOrg.tsx +++ b/static/app/views/organizationStats/usageStatsOrg.tsx @@ -4,9 +4,11 @@ import type {WithRouterProps} from 'react-router'; import styled from '@emotion/styled'; import * as Sentry from '@sentry/react'; import isEqual from 'lodash/isEqual'; +import startCase from 'lodash/startCase'; import moment from 'moment'; import {navigateTo} from 'sentry/actionCreators/navigation'; +import type {TooltipSubLabel} from 'sentry/components/charts/components/tooltip'; import OptionSelector from 'sentry/components/charts/optionSelector'; import {InlineContainer, SectionHeading} from 'sentry/components/charts/styles'; import type {DateTimeObject} from 'sentry/components/charts/utils'; @@ -115,7 +117,7 @@ class UsageStatsOrganization< return { ...queryDatetime, interval: getSeriesApiInterval(dataDatetime), - groupBy: ['category', 'outcome'], + groupBy: ['category', 'outcome', 'reason'], project: projectIds, field: ['sum(quantity)'], }; @@ -136,6 +138,7 @@ class UsageStatsOrganization< chartDateTimezoneDisplay: string; chartDateUtc: boolean; chartStats: ChartStats; + chartSubLabels: TooltipSubLabel[]; chartTransform: ChartDataTransform; dataError?: Error; } { @@ -232,6 +235,7 @@ class UsageStatsOrganization< chartDateEnd, chartDateUtc, chartTransform, + chartSubLabels, } = this.chartData; const hasError = error || !!dataError; @@ -249,6 +253,7 @@ class UsageStatsOrganization< usageDateShowUtc: chartDateUtc, usageDateInterval: chartDateInterval, usageStats: chartStats, + chartSubLabels, } as UsageChartProps; return chartProps; @@ -318,6 +323,7 @@ class UsageStatsOrganization< total?: string; }; chartStats: ChartStats; + chartSubLabels: TooltipSubLabel[]; dataError?: Error; } { const cardStats = { @@ -332,26 +338,84 @@ class UsageStatsOrganization< projected: [], filtered: [], }; + const chartSubLabels: any[] = []; if (!orgStats) { - return {cardStats, chartStats}; + return {cardStats, chartStats, chartSubLabels}; } try { const {dataCategory} = this.props; const {chartDateInterval, chartDateUtc} = this.chartDateRange; - const usageStats: UsageStat[] = orgStats.intervals.map(interval => { - const dateTime = moment(interval); + const usageStats: UsageStat[] = orgStats.intervals.map( + (interval, intervalIndex) => { + const dateTime = moment(interval); + const dateFromMoment = getDateFromMoment( + dateTime, + chartDateInterval, + chartDateUtc + ); + + orgStats.groups.forEach(group => { + const dataObject = { + name: dateFromMoment, + value: group.series['sum(quantity)'][intervalIndex], + }; + + const reason = String(group.by.reason ?? ''); + const label = startCase(reason.replace(/-|_/g, ' ')); + const existingSubLabel = chartSubLabels.find( + subLabel => subLabel.label === label + ); + + if (group.by.outcome === Outcome.FILTERED) { + if (existingSubLabel) { + existingSubLabel.data.push({ + ...dataObject, + value: existingSubLabel.data[0].value + dataObject.value, + }); + return; + } + + chartSubLabels.push({ + parentLabel: t('Filtered'), + label, + data: [dataObject], + }); + } - return { - date: getDateFromMoment(dateTime, chartDateInterval, chartDateUtc), - total: 0, - accepted: 0, - filtered: 0, - dropped: {total: 0}, - }; - }); + if ( + group.by.outcome === Outcome.DROPPED || + group.by.outcome === Outcome.CARDINALITY_LIMITED || + group.by.outcome === Outcome.INVALID || + group.by.outcome === Outcome.RATE_LIMITED + ) { + if (existingSubLabel) { + existingSubLabel.data.push({ + ...dataObject, + value: existingSubLabel.data[0].value + dataObject.value, + }); + return; + } + + chartSubLabels.push({ + parentLabel: t('Dropped'), + label, + data: [dataObject], + }); + } + }); + + return { + date: dateFromMoment, + total: 0, + accepted: 0, + filtered: 0, + dropped: {total: 0}, + }; + } + ); // Tally totals for card data const count: Record<'total' | Outcome, number> = { @@ -441,6 +505,7 @@ class UsageStatsOrganization< ), }, chartStats, + chartSubLabels, }; } catch (err) { Sentry.withScope(scope => { @@ -452,6 +517,7 @@ class UsageStatsOrganization< return { cardStats, chartStats, + chartSubLabels, dataError: new Error('Failed to parse stats data'), }; }