Skip to content

Commit f4611cb

Browse files
DominikB2014claude
andcommitted
feat(chartcuterie): Add explore line chart render descriptor
Add SLACK_EXPLORE_LINE chartcuterie type that accepts timeseries data directly from the /events-timeseries/ endpoint, avoiding the unnecessary conversion to EventsStats format. Uses the shared TimeSeries type, timeSeriesItemToEChartsDataPoint, and formatTimeSeriesName utilities. Supports both single series and grouped (top N) data. Refs DAIN-1483 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 5d3818e commit f4611cb

File tree

3 files changed

+99
-0
lines changed

3 files changed

+99
-0
lines changed

static/app/chartcuterie/config.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import {lightTheme} from 'sentry/utils/theme/theme';
1313

1414
import {makeDiscoverCharts} from './discover';
15+
import {makeExploreCharts} from './explore';
1516
import {makeMetricAlertCharts} from './metricAlert';
1617
import {makeMetricDetectorCharts} from './metricDetector';
1718
import {makePerformanceCharts} from './performance';
@@ -42,6 +43,7 @@ const register = (renderDescriptor: RenderDescriptor<ChartType>) =>
4243
renderConfig.set(renderDescriptor.key, renderDescriptor);
4344

4445
makeDiscoverCharts(lightTheme).forEach(register);
46+
makeExploreCharts(lightTheme).forEach(register);
4547
makeMetricAlertCharts(lightTheme).forEach(register);
4648
makeMetricDetectorCharts(lightTheme).forEach(register);
4749
makePerformanceCharts(lightTheme).forEach(register);
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import type {Theme} from '@emotion/react';
2+
3+
import {XAxis} from 'sentry/components/charts/components/xAxis';
4+
import {LineSeries} from 'sentry/components/charts/series/lineSeries';
5+
import {timeSeriesItemToEChartsDataPoint} from 'sentry/utils/timeSeries/timeSeriesItemToEChartsDataPoint';
6+
import type {TimeSeries} from 'sentry/views/dashboards/widgets/common/types';
7+
import {formatTimeSeriesName} from 'sentry/views/dashboards/widgets/timeSeriesWidget/formatters/formatTimeSeriesName';
8+
9+
import {DEFAULT_FONT_FAMILY, makeSlackChartDefaults, slackChartSize} from './slack';
10+
import type {RenderDescriptor} from './types';
11+
import {ChartType} from './types';
12+
13+
type ExploreChartData = {
14+
seriesName: string;
15+
timeSeries: TimeSeries[];
16+
};
17+
18+
export const makeExploreCharts = (theme: Theme): Array<RenderDescriptor<ChartType>> => {
19+
const exploreXAxis = XAxis({
20+
theme,
21+
splitNumber: 3,
22+
isGroupedByDate: true,
23+
axisLabel: {fontSize: 11, fontFamily: DEFAULT_FONT_FAMILY},
24+
});
25+
26+
const slackChartDefaults = makeSlackChartDefaults(theme);
27+
const exploreCharts: Array<RenderDescriptor<ChartType>> = [];
28+
29+
exploreCharts.push({
30+
key: ChartType.SLACK_EXPLORE_LINE,
31+
getOption: (data: ExploreChartData) => {
32+
const matching = data.timeSeries.filter(ts => ts.yAxis === data.seriesName);
33+
34+
if (matching.length === 0) {
35+
return {
36+
...slackChartDefaults,
37+
xAxis: exploreXAxis,
38+
useUTC: true,
39+
series: [],
40+
};
41+
}
42+
43+
const hasGroups = matching.some(ts => ts.groupBy && ts.groupBy.length > 0);
44+
45+
if (!hasGroups) {
46+
const ts = matching[0]!;
47+
const color = theme.chart.getColorPalette(0);
48+
const lineSeries = LineSeries({
49+
name: data.seriesName,
50+
data: ts.values.map(timeSeriesItemToEChartsDataPoint),
51+
lineStyle: {color: color?.[0], opacity: 1},
52+
itemStyle: {color: color?.[0]},
53+
});
54+
55+
return {
56+
...slackChartDefaults,
57+
xAxis: exploreXAxis,
58+
useUTC: true,
59+
color,
60+
series: [lineSeries],
61+
};
62+
}
63+
64+
const sorted = matching
65+
.slice()
66+
.sort((a, b) => (a.meta?.order ?? 0) - (b.meta?.order ?? 0));
67+
const hasOther = sorted.some(ts => ts.meta?.isOther);
68+
const color = theme.chart
69+
.getColorPalette(sorted.length - 1 - (hasOther ? 1 : 0))
70+
?.slice() as string[];
71+
if (hasOther) {
72+
color.push(theme.tokens.content.secondary);
73+
}
74+
75+
const series = sorted.map((ts, i) => {
76+
return LineSeries({
77+
name: formatTimeSeriesName(ts),
78+
data: ts.values.map(timeSeriesItemToEChartsDataPoint),
79+
lineStyle: {color: color?.[i], opacity: 1},
80+
itemStyle: {color: color?.[i]},
81+
});
82+
});
83+
84+
return {
85+
...slackChartDefaults,
86+
xAxis: exploreXAxis,
87+
useUTC: true,
88+
color,
89+
series,
90+
};
91+
},
92+
...slackChartSize,
93+
});
94+
95+
return exploreCharts;
96+
};

static/app/chartcuterie/types.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export enum ChartType {
2020
SLACK_METRIC_DETECTOR_SESSIONS = 'slack:metricDetector.sessions',
2121
SLACK_PERFORMANCE_ENDPOINT_REGRESSION = 'slack:performance.endpointRegression',
2222
SLACK_PERFORMANCE_FUNCTION_REGRESSION = 'slack:performance.functionRegression',
23+
SLACK_EXPLORE_LINE = 'slack:explore.line',
2324
}
2425

2526
/**

0 commit comments

Comments
 (0)