123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127 |
- import {Fragment, useRef} from 'react';
- import styled from '@emotion/styled';
- import ErrorBoundary from 'sentry/components/errorBoundary';
- import QuestionTooltip from 'sentry/components/questionTooltip';
- import {t} from 'sentry/locale';
- import {space} from 'sentry/styles/space';
- import type {Event} from 'sentry/types/event';
- import useRouteAnalyticsParams from 'sentry/utils/routeAnalytics/useRouteAnalyticsParams';
- import {useDimensions} from 'sentry/utils/useDimensions';
- import useOrganization from 'sentry/utils/useOrganization';
- import {TraceIssueEvent} from './traceIssue';
- import {TraceTimelineEvents} from './traceTimelineEvents';
- import {useTraceTimelineEvents} from './useTraceTimelineEvents';
- interface TraceTimelineProps {
- event: Event;
- }
- export function TraceTimeline({event}: TraceTimelineProps) {
- const organization = useOrganization();
- const timelineRef = useRef<HTMLDivElement>(null);
- const {width} = useDimensions({elementRef: timelineRef});
- const {isError, isLoading, traceEvents, oneOtherIssueEvent} = useTraceTimelineEvents({
- event,
- });
- const hasTraceId = !!event.contexts?.trace?.trace_id;
- let timelineStatus: string | undefined = 'empty';
- if (hasTraceId && !isLoading) {
- if (!organization.features.includes('related-issues-issue-details-page')) {
- timelineStatus = traceEvents.length > 1 ? 'shown' : 'empty';
- } else {
- // When we have another issue we skip the timeline
- timelineStatus = oneOtherIssueEvent ? 'empty' : 'shown';
- }
- } else if (!hasTraceId) {
- timelineStatus = 'no_trace_id';
- }
- const showTraceRelatedIssue =
- timelineStatus !== 'shown' &&
- organization.features.includes('related-issues-issue-details-page') &&
- oneOtherIssueEvent !== undefined;
- useRouteAnalyticsParams({
- trace_timeline_status: timelineStatus,
- has_related_trace_issue: showTraceRelatedIssue,
- });
- if (!hasTraceId) {
- return null;
- }
- const noEvents = !isLoading && traceEvents.length === 0;
- // Timelines with only the current event are not useful
- const onlySelfEvent =
- !isLoading &&
- traceEvents.length > 0 &&
- traceEvents.every(item => item.id === event.id);
- if (isError || noEvents || onlySelfEvent || isLoading) {
- // display empty placeholder to reduce layout shift
- return null;
- }
- return (
- <ErrorBoundary mini>
- {timelineStatus === 'shown' && (
- <Fragment>
- <TimelineWrapper>
- <div ref={timelineRef}>
- <TimelineEventsContainer>
- <TimelineOutline />
- {/* Sets a min width of 200 for testing */}
- <TraceTimelineEvents event={event} width={Math.max(width, 200)} />
- </TimelineEventsContainer>
- </div>
- <QuestionTooltipWrapper>
- <QuestionTooltip
- size="sm"
- title={t(
- 'This is a trace timeline showing all related events happening upstream and downstream of this event'
- )}
- position="bottom"
- />
- </QuestionTooltipWrapper>
- </TimelineWrapper>
- </Fragment>
- )}
- {showTraceRelatedIssue && <TraceIssueEvent event={oneOtherIssueEvent} />}
- </ErrorBoundary>
- );
- }
- const TimelineWrapper = styled('div')`
- display: grid;
- grid-template-columns: 1fr auto;
- align-items: start;
- gap: ${space(2)};
- margin-top: ${space(0.25)};
- `;
- const QuestionTooltipWrapper = styled('div')`
- margin-top: ${space(0.25)};
- `;
- /**
- * Displays the container the dots appear inside of
- */
- const TimelineOutline = styled('div')`
- position: absolute;
- left: 0;
- top: 3.5px;
- width: 100%;
- height: 10px;
- border: 1px solid ${p => p.theme.innerBorder};
- border-radius: ${p => p.theme.borderRadius};
- background-color: ${p => p.theme.backgroundSecondary};
- `;
- const TimelineEventsContainer = styled('div')`
- position: relative;
- height: 34px;
- padding-top: 10px;
- `;
|