Skip to content

Commit 0157ee2

Browse files
authored
feat(issues): Place current event markline on bar (#91914)
Instead of using the current event's datetime, place the markline directly on a bar in the graph since echarts can sometimes render the current event off the chart due to mixing line charts and bar charts on a single xAxis. before ![image](https://github.com/user-attachments/assets/9fefc64e-4c71-465b-8a13-8fa0ab735c79) after ![image](https://github.com/user-attachments/assets/9497642e-2d70-4475-828f-4be9d43cf697) fixes https://linear.app/getsentry/issue/RTC-855/issue-details-recommended-and-last-event-doesnt-show-on-timeline
1 parent 1764d6d commit 0157ee2

File tree

2 files changed

+71
-96
lines changed

2 files changed

+71
-96
lines changed

static/app/views/issueDetails/streamline/eventGraph.tsx

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import {Button, type ButtonProps} from 'sentry/components/core/button';
2222
import Placeholder from 'sentry/components/placeholder';
2323
import {t, tct, tn} from 'sentry/locale';
2424
import {space} from 'sentry/styles/space';
25-
import type {ReactEchartsRef, SeriesDataUnit} from 'sentry/types/echarts';
25+
import type {ReactEchartsRef} from 'sentry/types/echarts';
2626
import type {Event} from 'sentry/types/event';
2727
import type {Group} from 'sentry/types/group';
2828
import type {EventsStats, MultiSeriesEventsStats} from 'sentry/types/organization';
@@ -86,7 +86,10 @@ interface EventGraphProps {
8686
}
8787

8888
function createSeriesAndCount(stats: EventsStats) {
89-
return stats?.data?.reduce(
89+
return stats?.data?.reduce<{
90+
count: number;
91+
series: Array<{name: number; value: number}>;
92+
}>(
9093
(result, [timestamp, countData]) => {
9194
const count = countData?.[0]?.count ?? 0;
9295
return {
@@ -100,7 +103,7 @@ function createSeriesAndCount(stats: EventsStats) {
100103
count: result.count + count,
101104
};
102105
},
103-
{series: [] as SeriesDataUnit[], count: 0}
106+
{series: [], count: 0}
104107
);
105108
}
106109

@@ -240,6 +243,7 @@ export function EventGraph({
240243
const currentEventSeries = useCurrentEventMarklineSeries({
241244
event,
242245
group,
246+
eventSeries,
243247
});
244248

245249
const [legendSelected, setLegendSelected] = useLocalStorageState(
@@ -287,9 +291,8 @@ export function EventGraph({
287291
// Do some manipulation to make sure the release buckets match up to `eventSeries`
288292
const lastEventSeries = eventSeries.at(-1);
289293
const penultEventSeries = eventSeries.at(-2);
290-
const lastEventSeriesTimestamp = lastEventSeries && (lastEventSeries.name as number);
291-
const penultEventSeriesTimestamp =
292-
penultEventSeries && (penultEventSeries.name as number);
294+
const lastEventSeriesTimestamp = lastEventSeries?.name;
295+
const penultEventSeriesTimestamp = penultEventSeries?.name;
293296
const eventSeriesInterval =
294297
lastEventSeriesTimestamp &&
295298
penultEventSeriesTimestamp &&
@@ -396,7 +399,7 @@ export function EventGraph({
396399
}
397400

398401
// Only display the current event mark line if on the issue details tab
399-
if (currentEventSeries.markLine && currentTab === Tab.DETAILS) {
402+
if (currentEventSeries?.markLine && currentTab === Tab.DETAILS) {
400403
seriesData.push(currentEventSeries as BarChartSeries);
401404
}
402405

Lines changed: 61 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -1,119 +1,91 @@
1+
import {useMemo} from 'react';
12
import {useTheme} from '@emotion/react';
2-
import type {LineSeriesOption} from 'echarts';
33

44
import MarkLine from 'sentry/components/charts/components/markLine';
55
import {t} from 'sentry/locale';
66
import type {Event} from 'sentry/types/event';
77
import type {Group} from 'sentry/types/group';
88
import {getFormattedDate} from 'sentry/utils/dates';
9-
import {getShortEventId} from 'sentry/utils/events';
10-
import {useNavigate} from 'sentry/utils/useNavigate';
11-
import useOrganization from 'sentry/utils/useOrganization';
129
import {useIssueDetailsEventView} from 'sentry/views/issueDetails/streamline/hooks/useIssueDetailsDiscoverQuery';
1310

14-
function useEventMarklineSeries({
15-
events,
16-
group,
17-
markLineProps = {},
18-
}: {
19-
events: Event[];
11+
interface UseEventMarklineSeriesProps {
12+
event: Event | undefined;
13+
/**
14+
* The event series is used to place the mark line on the nearest bar
15+
* This is to ensure the mark line is always visisble.
16+
*/
17+
eventSeries: Array<{name: number; value: number}>;
2018
group: Group;
21-
markLineProps?: Partial<LineSeriesOption['markLine']>;
22-
}) {
23-
const theme = useTheme();
24-
const navigate = useNavigate();
25-
const organization = useOrganization();
26-
const eventView = useIssueDetailsEventView({group});
27-
const baseEventsPath = `/organizations/${organization.slug}/issues/${group.id}/events/`;
28-
29-
const markLine = events.length
30-
? MarkLine({
31-
animation: false,
32-
lineStyle: {
33-
color: theme.isChonk ? theme.tokens.graphics.promotion : theme.pink200,
34-
type: 'solid',
35-
},
36-
label: {
37-
show: false,
38-
},
39-
data: events.map(event => ({
40-
xAxis: +new Date(event.dateCreated ?? event.dateReceived),
41-
name: getShortEventId(event.id),
42-
value: getShortEventId(event.id),
43-
onClick: () => {
44-
navigate({
45-
pathname: `${baseEventsPath}${event.id}/`,
46-
});
47-
},
48-
label: {
49-
formatter: () => getShortEventId(event.id),
50-
},
51-
})),
52-
tooltip: {
53-
trigger: 'item',
54-
formatter: ({data}: any) => {
55-
const time = getFormattedDate(data.value, 'MMM D, YYYY LT', {
56-
local: !eventView.utc,
57-
});
58-
return [
59-
'<div class="tooltip-series">',
60-
`<div><span class="tooltip-label"><strong>${data.name}</strong></span></div>`,
61-
'</div>',
62-
`<div class="tooltip-footer">${time}</div>`,
63-
'<div class="tooltip-arrow"></div>',
64-
].join('');
65-
},
66-
},
67-
...markLineProps,
68-
})
69-
: undefined;
70-
71-
return {
72-
seriesName: t('Specific Events'),
73-
data: [],
74-
markLine,
75-
color: theme.pink200,
76-
type: 'line',
77-
};
7819
}
7920

8021
export function useCurrentEventMarklineSeries({
8122
event,
8223
group,
83-
markLineProps = {},
84-
}: {
85-
group: Group;
86-
event?: Event;
87-
markLineProps?: Partial<LineSeriesOption['markLine']>;
88-
}) {
24+
eventSeries,
25+
}: UseEventMarklineSeriesProps) {
26+
const theme = useTheme();
8927
const eventView = useIssueDetailsEventView({group});
9028

91-
const result = useEventMarklineSeries({
92-
events: event ? [event] : [],
93-
group,
94-
markLineProps: {
29+
return useMemo(() => {
30+
if (!event) {
31+
return undefined;
32+
}
33+
34+
const eventDateCreated = new Date(event.dateCreated!).getTime();
35+
const closestEventSeries = eventSeries.reduce<
36+
{name: number; value: number} | undefined
37+
>((acc, curr) => {
38+
// Find the first bar that would contain the current event
39+
if (curr.value && curr.name <= eventDateCreated) {
40+
if (!acc || curr.name > acc.name) {
41+
return curr;
42+
}
43+
}
44+
return acc;
45+
}, undefined);
46+
47+
if (!closestEventSeries) {
48+
return undefined;
49+
}
50+
51+
const markLine = MarkLine({
52+
animation: false,
53+
lineStyle: {
54+
color: theme.isChonk ? theme.tokens.graphics.promotion : theme.pink200,
55+
type: 'solid',
56+
},
57+
label: {
58+
show: false,
59+
},
60+
data: [
61+
{
62+
xAxis: closestEventSeries.name,
63+
name: event.id,
64+
},
65+
],
9566
tooltip: {
9667
trigger: 'item',
97-
formatter: ({data}: any) => {
98-
const time = getFormattedDate(data.value, 'MMM D, YYYY LT', {
68+
formatter: () => {
69+
// Do not use date from xAxis here since we've placed it on the nearest bar
70+
const time = getFormattedDate(event.dateCreated, 'MMM D, YYYY LT', {
9971
local: !eventView.utc,
10072
});
10173
return [
10274
'<div class="tooltip-series">',
103-
`<div><span class="tooltip-label"><strong>${t(
104-
'Current Event'
105-
)}</strong></span></div>`,
75+
`<div><span class="tooltip-label"><strong>${t('Current Event')}</strong></span></div>`,
10676
'</div>',
10777
`<div class="tooltip-footer">${time}</div>`,
10878
'<div class="tooltip-arrow"></div>',
10979
].join('');
11080
},
11181
},
112-
...markLineProps,
113-
},
114-
});
115-
return {
116-
...result,
117-
seriesName: t('Current Event'),
118-
};
82+
});
83+
84+
return {
85+
seriesName: 'Current Event',
86+
data: [],
87+
markLine,
88+
type: 'line',
89+
};
90+
}, [event, theme, eventView.utc, eventSeries]);
11991
}

0 commit comments

Comments
 (0)