Skip to content

Commit c482b2c

Browse files
authored
feat(replays): Implement isHovered states for DOM and Network tabs (#42558)
This change adds logic to render isCurrent and isHovered lines in the DOM and Network tabs on the Replay Details page. There's also some cleanup of internal variables and things to make each tab more similar to each other. The DOM tab has subtle ui changes, when you're hovering something the gray200 line appears. The network tab has some other changes to match what's in the latest figma: https://www.figma.com/file/OPFnT04z0uy8BEUms9OU4f/Specs%3A-Details-v3?node-id=604%3A17755&t=372r9SfA2ONS8RXR-0 | Before | After | | --- | --- | | <img width="766" alt="SCR-20221221-ikg" src="https://user-images.githubusercontent.com/187460/208976581-5a999467-e298-453b-8913-972dbca46766.png"> | <img width="769" alt="SCR-20221221-ijy" src="https://user-images.githubusercontent.com/187460/208976589-f0afe18d-1945-4b69-aca9-ef472b8642a7.png"> | | <img width="564" alt="SCR-20221221-in2" src="https://user-images.githubusercontent.com/187460/208977051-ce0e1231-3ae2-41a8-983f-7a159f8f1052.png"> | <img width="563" alt="SCR-20221221-imy" src="https://user-images.githubusercontent.com/187460/208977075-2146dc3e-4952-4a58-8334-e4eb12753fab.png"> | Relates to #38948
1 parent 7911f08 commit c482b2c

File tree

4 files changed

+115
-99
lines changed

4 files changed

+115
-99
lines changed

static/app/utils/replays/hooks/useCrumbHandlers.tsx

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {useReplayContext} from 'sentry/components/replays/replayContext';
44
import {relativeTimeInMs} from 'sentry/components/replays/utils';
55
import {BreadcrumbType, Crumb} from 'sentry/types/breadcrumbs';
66
import useActiveReplayTab from 'sentry/utils/replays/hooks/useActiveReplayTab';
7+
import type {NetworkSpan} from 'sentry/views/replays/types';
78

89
function useCrumbHandlers(startTimestampMs: number = 0) {
910
const {
@@ -16,7 +17,7 @@ function useCrumbHandlers(startTimestampMs: number = 0) {
1617
const {setActiveTab} = useActiveReplayTab();
1718

1819
const handleMouseEnter = useCallback(
19-
(item: Crumb) => {
20+
(item: Crumb | NetworkSpan) => {
2021
if (startTimestampMs) {
2122
setCurrentHoverTime(relativeTimeInMs(item.timestamp ?? '', startTimestampMs));
2223
}
@@ -32,7 +33,7 @@ function useCrumbHandlers(startTimestampMs: number = 0) {
3233
);
3334

3435
const handleMouseLeave = useCallback(
35-
(item: Crumb) => {
36+
(item: Crumb | NetworkSpan) => {
3637
setCurrentHoverTime(undefined);
3738

3839
if (item.data && 'nodeId' in item.data) {
@@ -43,21 +44,23 @@ function useCrumbHandlers(startTimestampMs: number = 0) {
4344
);
4445

4546
const handleClick = useCallback(
46-
(crumb: Crumb) => {
47+
(crumb: Crumb | NetworkSpan) => {
4748
if (crumb.timestamp !== undefined) {
4849
setCurrentTime(relativeTimeInMs(crumb.timestamp, startTimestampMs));
4950
}
5051

51-
switch (crumb.type) {
52-
case BreadcrumbType.NAVIGATION:
53-
setActiveTab('network');
54-
break;
55-
case BreadcrumbType.UI:
56-
setActiveTab('dom');
57-
break;
58-
default:
59-
setActiveTab('console');
60-
break;
52+
if ('type' in crumb) {
53+
switch (crumb.type) {
54+
case BreadcrumbType.NAVIGATION:
55+
setActiveTab('network');
56+
break;
57+
case BreadcrumbType.UI:
58+
setActiveTab('dom');
59+
break;
60+
default:
61+
setActiveTab('console');
62+
break;
63+
}
6164
}
6265
},
6366
[setCurrentTime, startTimestampMs, setActiveTab]

static/app/views/replays/detail/domMutations/index.tsx

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,26 +29,36 @@ import useVirtualizedList from 'sentry/views/replays/detail/useVirtualizedList';
2929

3030
type Props = {
3131
replay: null | ReplayReader;
32+
startTimestampMs: number;
3233
};
3334

34-
function DomMutations({replay}: Props) {
35-
const startTimestampMs = replay?.getReplay()?.startedAt?.getTime() || 0;
36-
const {currentTime} = useReplayContext();
35+
function DomMutations({replay, startTimestampMs}: Props) {
36+
const {currentTime, currentHoverTime} = useReplayContext();
3737
const {isLoading, actions} = useExtractedCrumbHtml({replay});
3838

3939
const filterProps = useDomFilters({actions: actions || []});
4040
const {items, setSearchTerm} = filterProps;
4141
const clearSearchTerm = () => setSearchTerm('');
4242

43-
const currentDomMutation = getPrevReplayEvent({
44-
items: items.map(mutation => mutation.crumb),
43+
const {handleMouseEnter, handleMouseLeave, handleClick} =
44+
useCrumbHandlers(startTimestampMs);
45+
46+
const crumbs = items.map(mutation => mutation.crumb);
47+
const current = getPrevReplayEvent({
48+
items: crumbs,
4549
targetTimestampMs: startTimestampMs + currentTime,
4650
allowEqual: true,
4751
allowExact: true,
4852
});
4953

50-
const {handleMouseEnter, handleMouseLeave, handleClick} =
51-
useCrumbHandlers(startTimestampMs);
54+
const hovered = currentHoverTime
55+
? getPrevReplayEvent({
56+
items: crumbs,
57+
targetTimestampMs: startTimestampMs + currentHoverTime,
58+
allowEqual: true,
59+
allowExact: true,
60+
})
61+
: null;
5262

5363
const listRef = useRef<ReactVirtualizedList>(null);
5464
const {cache} = useVirtualizedList({
@@ -80,7 +90,8 @@ function DomMutations({replay}: Props) {
8090
onMouseEnter={() => handleMouseEnter(crumb)}
8191
onMouseLeave={() => handleMouseLeave(crumb)}
8292
style={style}
83-
isCurrent={crumb.id === currentDomMutation?.id}
93+
isCurrent={crumb.id === current?.id}
94+
isHovered={crumb.id === hovered?.id}
8495
>
8596
<IconWrapper color={crumb.color} hasOccurred={hasOccurred}>
8697
<BreadcrumbIcon type={crumb.type} />
@@ -193,13 +204,16 @@ const IconWrapper = styled('div')<
193204
z-index: 2;
194205
`;
195206

196-
const MutationListItem = styled('li')<{isCurrent?: boolean}>`
207+
const MutationListItem = styled('li')<{isCurrent: boolean; isHovered: boolean}>`
197208
display: flex;
198209
gap: ${space(1)};
199210
flex-grow: 1;
200211
padding: ${space(1)} ${space(1.5)};
201212
position: relative;
202-
border-bottom: 1px solid ${p => (p.isCurrent ? p.theme.purple300 : 'transparent')};
213+
border-bottom: 1px solid
214+
${p =>
215+
p.isCurrent ? p.theme.purple300 : p.isHovered ? p.theme.purple200 : 'transparent'};
216+
203217
&:hover {
204218
background-color: ${p => p.theme.backgroundSecondary};
205219
}

static/app/views/replays/detail/focusArea.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ function FocusArea({}: Props) {
2828
case 'network':
2929
return (
3030
<NetworkList
31-
replayRecord={replay?.getReplay()}
3231
networkSpans={replay?.getNetworkSpans()}
32+
startTimestampMs={replay?.getReplay()?.startedAt?.getTime() || 0}
3333
/>
3434
);
3535
case 'trace':
@@ -48,7 +48,12 @@ function FocusArea({}: Props) {
4848
/>
4949
);
5050
case 'dom':
51-
return <DomMutations replay={replay} />;
51+
return (
52+
<DomMutations
53+
replay={replay}
54+
startTimestampMs={replay?.getReplay()?.startedAt?.getTime() || 0}
55+
/>
56+
);
5257
case 'memory':
5358
return (
5459
<MemoryChart

0 commit comments

Comments
 (0)