Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ref(trace) break up the rendering logic #77953

Merged
merged 13 commits into from
Sep 27, 2024
1,191 changes: 91 additions & 1,100 deletions static/app/views/performance/newTraceDetails/trace.tsx

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import {t} from 'sentry/locale';
import {TraceIcons} from 'sentry/views/performance/newTraceDetails/icons';
import {
makeTraceNodeBarColor,
type ParentAutogroupNode,
type SiblingAutogroupNode,
} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree';
import {AutogroupedTraceBar} from 'sentry/views/performance/newTraceDetails/traceRow/traceBar';
import {
maybeFocusTraceRow,
TRACE_COUNT_FORMATTER,
TraceChildrenButton,
TraceRowConnectors,
type TraceRowProps,
} from 'sentry/views/performance/newTraceDetails/traceRow/traceRow';

export function TraceAutogroupedRow(
props: TraceRowProps<ParentAutogroupNode | SiblingAutogroupNode>
) {
return (
<div
key={props.index}
ref={r =>
props.tabIndex === 0 && !props.isEmbedded
? maybeFocusTraceRow(r, props.node, props.previouslyFocusedNodeRef)
: null
}
tabIndex={props.tabIndex}
className={`Autogrouped TraceRow ${props.rowSearchClassName} ${props.node.has_errors ? props.node.max_severity : ''}`}
onClick={props.onRowClick}
onKeyDown={props.onRowKeyDown}
style={props.style}
>
<div className="TraceLeftColumn" ref={props.registerListColumnRef}>
<div
className="TraceLeftColumnInner"
style={props.listColumnStyle}
onDoubleClick={props.onRowDoubleClick}
>
<div className="TraceChildrenCountWrapper">
<TraceRowConnectors node={props.node} manager={props.manager} />
<TraceChildrenButton
icon={
<TraceIcons.Chevron direction={props.node.expanded ? 'up' : 'down'} />
}
status={props.node.fetchStatus}
expanded={!props.node.expanded}
onClick={props.onExpand}
onDoubleClick={props.onExpandDoubleClick}
>
{TRACE_COUNT_FORMATTER.format(props.node.groupCount)}
</TraceChildrenButton>
</div>

<span className="TraceOperation">{t('Autogrouped')}</span>
<strong className="TraceEmDash"> — </strong>
<span className="TraceDescription">{props.node.value.autogrouped_by.op}</span>
</div>
</div>
<div
className={props.spanColumnClassName}
ref={props.registerSpanColumnRef}
onDoubleClick={props.onRowDoubleClick}
>
<AutogroupedTraceBar
manager={props.manager}
entire_space={props.node.space}
errors={props.node.errors}
virtualized_index={props.virtualized_index}
color={makeTraceNodeBarColor(props.theme, props.node)}
node_spaces={props.node.autogroupedSegments}
performance_issues={props.node.performance_issues}
profiles={props.node.profiles}
/>
<button
ref={props.registerSpanArrowRef}
className="TraceArrow"
onClick={props.onSpanArrowClick}
>
<TraceIcons.Chevron direction="left" />
</button>
</div>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import {Fragment, useMemo} from 'react';
import clamp from 'lodash/clamp';

import type {
TraceTree,
TraceTreeNode,
} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree';
import type {VirtualizedViewManager} from 'sentry/views/performance/newTraceDetails/traceRenderers/virtualizedViewManager';

function getMaxErrorSeverity(errors: TraceTree.TraceError[]) {
return errors.reduce((acc, error) => {
if (error.level === 'fatal') {
return 'fatal';
}
if (error.level === 'error') {
return acc === 'fatal' ? 'fatal' : 'error';
}
if (error.level === 'warning') {
return acc === 'fatal' || acc === 'error' ? acc : 'warning';
}
return acc;
}, 'default');
}

interface BackgroundPatternsProps {
errors: TraceTreeNode<TraceTree.Transaction>['errors'];
manager: VirtualizedViewManager;
node_space: [number, number] | null;
performance_issues: TraceTreeNode<TraceTree.Transaction>['performance_issues'];
}

export function TraceBackgroundPatterns(props: BackgroundPatternsProps) {
const performance_issues = useMemo(() => {
if (!props.performance_issues.size) return [];
return [...props.performance_issues];
}, [props.performance_issues]);

const errors = useMemo(() => {
if (!props.errors.size) return [];
return [...props.errors];
}, [props.errors]);

const severity = useMemo(() => {
return getMaxErrorSeverity(errors);
}, [errors]);

if (!props.performance_issues.size && !props.errors.size) {
return null;
}

// If there is an error, render the error pattern across the entire width.
// Else if there is a performance issue, render the performance issue pattern
// for the duration of the performance issue. If there is a profile, render
// the profile pattern for entire duration (we do not have profile durations here)
return (
<Fragment>
{errors.length > 0 ? (
<div
className="TracePatternContainer"
style={{
left: 0,
width: '100%',
}}
>
<div className={`TracePattern ${severity}`} />
</div>
) : performance_issues.length > 0 ? (
<Fragment>
{performance_issues.map((issue, i) => {
const timestamp = issue.start * 1e3;
// Clamp the issue timestamp to the span's timestamp
const left = props.manager.computeRelativeLeftPositionFromOrigin(
clamp(
timestamp,
props.node_space![0],
props.node_space![0] + props.node_space![1]
),
props.node_space!
);

return (
<div
key={i}
className="TracePatternContainer"
style={{
left: left * 100 + '%',
width: (1 - left) * 100 + '%',
}}
>
<div className="TracePattern performance_issue" />
</div>
);
})}
</Fragment>
) : null}
</Fragment>
);
}
Loading
Loading