Skip to content
Draft
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,9 @@ describe('EventDetailsHeader', () => {

expect(screen.getByRole('button', {name: 'All Envs'})).toBeInTheDocument();
// Date selection is based on first seen unless selected by the user
expect(screen.getByRole('button', {name: 'Since First Seen'})).toBeInTheDocument();
expect(
screen.getByRole('button', {name: 'Since First Seen (19d)'})
).toBeInTheDocument();
expect(screen.getByPlaceholderText('Filter events\u2026')).toBeInTheDocument();
expect(
screen.getByRole('button', {
Expand Down
24 changes: 18 additions & 6 deletions static/app/views/issueDetails/streamline/eventDetailsHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ import {
TimeRangeSelector,
TimeRangeSelectTrigger,
} from 'sentry/components/timeRangeSelector';
import {getRelativeSummary} from 'sentry/components/timeRangeSelector/utils';
import {getRelativeDate} from 'sentry/components/timeSince';
import {TourElement} from 'sentry/components/tours/components';
import {t} from 'sentry/locale';
import {t, tct} from 'sentry/locale';
import type {Event} from 'sentry/types/event';
import type {Group} from 'sentry/types/group';
import type {Project} from 'sentry/types/project';
Expand All @@ -38,7 +38,10 @@ import {IssueUptimeCheckTimeline} from 'sentry/views/issueDetails/streamline/iss
import {OccurrenceSummary} from 'sentry/views/issueDetails/streamline/occurrenceSummary';
import {getDetectorDetails} from 'sentry/views/issueDetails/streamline/sidebar/detectorSection';
import {ToggleSidebar} from 'sentry/views/issueDetails/streamline/sidebar/toggleSidebar';
import {useGroupDefaultStatsPeriod} from 'sentry/views/issueDetails/useGroupDefaultStatsPeriod';
import {
shortenRelativeDate,
useGroupDefaultStatsPeriod,
} from 'sentry/views/issueDetails/useGroupDefaultStatsPeriod';
import {
getGroupReprocessingStatus,
ReprocessingStatus,
Comment thread
sentry[bot] marked this conversation as resolved.
Expand Down Expand Up @@ -135,6 +138,9 @@ export function EventDetailsHeader({group, event, project}: EventDetailsHeaderPr
/>
<TimeRangeSelector
menuTitle={t('Filter Time Range')}
menuWidth={
shouldShowSinceFirstSeenOption ? 'fit-content' : undefined
}
start={period?.start}
end={period?.end}
utc={location.query.utc === 'true'}
Expand All @@ -147,8 +153,10 @@ export function EventDetailsHeader({group, event, project}: EventDetailsHeaderPr
shouldShowSinceFirstSeenOption
? {
[defaultStatsPeriod.statsPeriod]: t(
'%s (since first seen)',
getRelativeSummary(defaultStatsPeriod.statsPeriod)
'Last %s (since first seen)',
getRelativeDate(group.firstSeen)
.replace(/^a (?=\w+$)/, '1 ')
.replace(/^an (?=\w+$)/, '1 ')
),
Comment thread
sentry[bot] marked this conversation as resolved.
}
: {}),
Comment on lines 150 to 159
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: The custom "since first seen" dropdown label is overwritten by default options when an issue's age matches a standard period like 7d, 14d, or 30d.
Severity: LOW

Suggested Fix

To fix the label overwrite, move the ...props.defaultOptions spread before the custom "since first seen" option is defined within the relativeOptions callback. This will ensure that the custom label takes precedence over the default one for matching time periods, while still allowing other default options to be available.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location: static/app/views/issueDetails/streamline/eventDetailsHeader.tsx#L153-L162

Potential issue: When an issue's age aligns with a standard relative period (e.g., 7,
14, or 30 days), the custom "Since First Seen" label in the stats period dropdown is
overwritten. This happens because the `...props.defaultOptions` spread, containing
generic labels like "Last 7 days", is applied after the custom option is defined,
replacing it. This results in a UI inconsistency where the dropdown's trigger button
correctly shows "Since First Seen", but the selected option within the dropdown displays
the generic label, creating a confusing user experience.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

known issue, will need another ticket to address this

Expand Down Expand Up @@ -181,7 +189,11 @@ export function EventDetailsHeader({group, event, project}: EventDetailsHeaderPr
{period === defaultStatsPeriod &&
!defaultStatsPeriod.isMaxRetention &&
shouldShowSinceFirstSeenOption
? t('Since First Seen')
? tct('Since First Seen ([period])', {
Comment thread
shashjar marked this conversation as resolved.
period: shortenRelativeDate(
getRelativeDate(group.firstSeen)
),
})
: triggerProps.children}
</TimeRangeSelectTrigger>
)}
Expand Down
37 changes: 37 additions & 0 deletions static/app/views/issueDetails/useGroupDefaultStatsPeriod.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,40 @@ export function useGroupDefaultStatsPeriod(

return {statsPeriod: `${clampedRetentionDays}d`, isMaxRetention};
}

/**
* Converts a getRelativeDate/moment.fromNow(true) string into short form.
* e.g. "19 days" → "19d", "a month" → "1mo", "3 hours" → "3h"
*/
export function shortenRelativeDate(relativeDate: string): string {
const match = relativeDate.match(/^(\d+|an?)\s+(\w+)$/);
if (!match) {
return '1m';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fallback produces misleading "1m" for very recent issues

Low Severity

shortenRelativeDate can't parse moment's "a few seconds" output (returned by fromNow(true) for issues less than ~45 seconds old) because the regex /^(\d+|an?)\s+(\w+)$/ only matches two-word strings. The fallback returns '1m' (1 minute), making the trigger display "Since First Seen (1m)" for an issue that's only seconds old. Similarly, the dropdown label's .replace(/^a (?=\w+$)/, '1 ') lookahead won't match "a few seconds" either, resulting in the awkward "Last a few seconds (since first seen)" text.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit f5cbc5a. Configure here.

}
Comment thread
cursor[bot] marked this conversation as resolved.
Outdated
const num = match[1] === 'a' || match[1] === 'an' ? '1' : match[1];
const unit = match[2];

if (!unit) {
return '1m';
}

if (unit.startsWith('month')) {
return `${num}mo`;
}
if (unit.startsWith('year')) {
return `${num}yr`;
}
if (unit.startsWith('day')) {
return `${num}d`;
}
if (unit.startsWith('hour')) {
return `${num}h`;
}
if (unit.startsWith('minute')) {
return `${num}m`;
}
if (unit.startsWith('second')) {
return `${num}s`;
}
return '1m';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shortenRelativeDate breaks for non-English locale users

Medium Severity

shortenRelativeDate parses English unit names (startsWith('month'), startsWith('day'), etc.), but getRelativeDate uses moment.fromNow(true) which returns locale-dependent strings. Since Sentry sets moment.locale(languageCode) for non-English users, the output will be e.g. "19 jours" (French) or "19 Tage" (German). All startsWith checks fail for these, so the function always returns the misleading fallback '1m'. The trigger would display "Since First Seen (1m)" for every non-English user regardless of actual duration. The same issue affects the .replace(/^a (?=\w+$)/, '1 ') regex in the dropdown label, which is also English-only.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit f5cbc5a. Configure here.

}
Comment thread
cursor[bot] marked this conversation as resolved.
Outdated
Loading