Skip to content

Commit 38dd44f

Browse files
committed
WIP - errors table uses frames
1 parent 8ba41ef commit 38dd44f

File tree

8 files changed

+60
-63
lines changed

8 files changed

+60
-63
lines changed

static/app/utils/replays/hydrateErrors.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export default function hydrateErrors(
3030
(Array.isArray(error['error.type'])
3131
? error['error.type'][0]
3232
: error['error.type']) ?? '',
33+
labels: error['error.type'],
3334
projectSlug: error['project.name'],
3435
},
3536
message: error.title,

static/app/utils/replays/types.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -180,11 +180,12 @@ export type ErrorFrame = Overwrite<
180180
{
181181
category: 'issue';
182182
data: {
183-
eventId: string; // error['id']
184-
groupId: number; // error['issue.id']
185-
groupShortId: string; // error['issue']
186-
label: string; // error['error.type'].join('')
187-
projectSlug: string; // error['project.name']
183+
eventId: string;
184+
groupId: number;
185+
groupShortId: string;
186+
label: string;
187+
labels: string[];
188+
projectSlug: string;
188189
};
189190
message: string;
190191
}

static/app/views/replays/detail/errorList/errorFilters.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
import {CompactSelect, SelectOption} from 'sentry/components/compactSelect';
22
import SearchBar from 'sentry/components/searchBar';
33
import {t} from 'sentry/locale';
4-
import type {Crumb} from 'sentry/types/breadcrumbs';
4+
import type {ErrorFrame} from 'sentry/utils/replays/types';
55
import useErrorFilters from 'sentry/views/replays/detail/errorList/useErrorFilters';
66
import FiltersGrid from 'sentry/views/replays/detail/filtersGrid';
77

88
type Props = {
9-
errorCrumbs: undefined | Crumb[];
9+
errorFrames: undefined | ErrorFrame[];
1010
} & ReturnType<typeof useErrorFilters>;
1111

1212
function ErrorFilters({
1313
getProjectOptions,
14-
errorCrumbs,
14+
errorFrames,
1515
searchTerm,
1616
selectValue,
1717
setFilters,
@@ -36,7 +36,7 @@ function ErrorFilters({
3636
onChange={setSearchTerm}
3737
placeholder={t('Search Errors')}
3838
query={searchTerm}
39-
disabled={!errorCrumbs || !errorCrumbs.length}
39+
disabled={!errorFrames || !errorFrames.length}
4040
/>
4141
</FiltersGrid>
4242
);

static/app/views/replays/detail/errorList/errorTableCell.tsx

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,14 @@ import classNames from 'classnames';
44

55
import Avatar from 'sentry/components/avatar';
66
import Link from 'sentry/components/links/link';
7-
import {relativeTimeInMs} from 'sentry/components/replays/utils';
87
import {
98
AvatarWrapper,
109
Cell,
1110
StyledTimestampButton,
1211
Text,
1312
} from 'sentry/components/replays/virtualizedGrid/bodyCell';
14-
import type {Crumb} from 'sentry/types/breadcrumbs';
1513
import {getShortEventId} from 'sentry/utils/events';
14+
import type {ErrorFrame} from 'sentry/utils/replays/types';
1615
import useOrganization from 'sentry/utils/useOrganization';
1716
import useProjects from 'sentry/utils/useProjects';
1817
import {normalizeUrl} from 'sentry/utils/withDomainRequired';
@@ -24,12 +23,12 @@ const EMPTY_CELL = '--';
2423

2524
type Props = {
2625
columnIndex: number;
27-
crumb: Crumb;
2826
currentHoverTime: number | undefined;
2927
currentTime: number;
30-
onClickTimestamp: (crumb: Crumb) => void;
31-
onMouseEnter: (crumb: Crumb) => void;
32-
onMouseLeave: (crumb: Crumb) => void;
28+
frame: ErrorFrame;
29+
onClickTimestamp: (frame: ErrorFrame) => void;
30+
onMouseEnter: (frame: ErrorFrame) => void;
31+
onMouseLeave: (frame: ErrorFrame) => void;
3332
rowIndex: number;
3433
sortConfig: ReturnType<typeof useSortErrors>['sortConfig'];
3534
startTimestampMs: number;
@@ -42,21 +41,20 @@ const ErrorTableCell = forwardRef<HTMLDivElement, Props>(
4241
columnIndex,
4342
currentHoverTime,
4443
currentTime,
44+
frame,
45+
onClickTimestamp,
4546
onMouseEnter,
4647
onMouseLeave,
47-
onClickTimestamp,
4848
sortConfig,
49-
crumb,
5049
startTimestampMs,
5150
style,
5251
}: Props,
5352
ref
5453
) => {
5554
const organization = useOrganization();
5655

57-
// @ts-expect-error
58-
const {eventId, groupId, groupShortId, project: projectSlug} = crumb.data;
59-
const title = crumb.message;
56+
const {eventId, groupId, groupShortId, projectSlug} = frame.data;
57+
const title = frame.message;
6058
const {projects} = useProjects();
6159
const project = useMemo(
6260
() => projects.find(p => p.slug === projectSlug),
@@ -75,13 +73,9 @@ const ErrorTableCell = forwardRef<HTMLDivElement, Props>(
7573
}
7674
: null;
7775

78-
const crumbTime = useMemo(
79-
// @ts-expect-error
80-
() => relativeTimeInMs(new Date(crumb.timestamp).getTime(), startTimestampMs),
81-
[crumb.timestamp, startTimestampMs]
82-
);
83-
const hasOccurred = currentTime >= crumbTime;
84-
const isBeforeHover = currentHoverTime === undefined || currentHoverTime >= crumbTime;
76+
const hasOccurred = currentTime >= frame.offsetMs;
77+
const isBeforeHover =
78+
currentHoverTime === undefined || currentHoverTime >= frame.offsetMs;
8579

8680
const isByTimestamp = sortConfig.by === 'timestamp';
8781
const isAsc = isByTimestamp ? sortConfig.asc : undefined;
@@ -111,8 +105,8 @@ const ErrorTableCell = forwardRef<HTMLDivElement, Props>(
111105
: undefined,
112106
}),
113107
hasOccurred: isByTimestamp ? hasOccurred : undefined,
114-
onMouseEnter: () => onMouseEnter(crumb),
115-
onMouseLeave: () => onMouseLeave(crumb),
108+
onMouseEnter: () => onMouseEnter(frame),
109+
onMouseLeave: () => onMouseLeave(frame),
116110
ref,
117111
style,
118112
} as ComponentProps<typeof Cell>;
@@ -176,10 +170,10 @@ const ErrorTableCell = forwardRef<HTMLDivElement, Props>(
176170
<StyledTimestampButton
177171
format="mm:ss.SSS"
178172
onClick={() => {
179-
onClickTimestamp(crumb);
173+
onClickTimestamp(frame);
180174
}}
181175
startTimestampMs={startTimestampMs}
182-
timestampMs={crumb.timestamp || ''}
176+
timestampMs={frame.timestampMs}
183177
/>
184178
</Cell>
185179
),

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

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import styled from '@emotion/styled';
55
import Placeholder from 'sentry/components/placeholder';
66
import {useReplayContext} from 'sentry/components/replays/replayContext';
77
import {t} from 'sentry/locale';
8-
import type {Crumb} from 'sentry/types/breadcrumbs';
98
import useCrumbHandlers from 'sentry/utils/replays/hooks/useCrumbHandlers';
9+
import type {ErrorFrame} from 'sentry/utils/replays/types';
1010
import ErrorFilters from 'sentry/views/replays/detail/errorList/errorFilters';
1111
import ErrorHeaderCell, {
1212
COLUMN_COUNT,
@@ -22,7 +22,7 @@ const HEADER_HEIGHT = 25;
2222
const BODY_HEIGHT = 28;
2323

2424
type Props = {
25-
errorCrumbs: undefined | Crumb[];
25+
errorFrames: undefined | ErrorFrame[];
2626
startTimestampMs: number;
2727
};
2828

@@ -32,10 +32,10 @@ const cellMeasurer = {
3232
fixedHeight: true,
3333
};
3434

35-
function ErrorList({errorCrumbs, startTimestampMs}: Props) {
35+
function ErrorList({errorFrames, startTimestampMs}: Props) {
3636
const {currentTime, currentHoverTime} = useReplayContext();
3737

38-
const filterProps = useErrorFilters({errorCrumbs: errorCrumbs || []});
38+
const filterProps = useErrorFilters({errorFrames: errorFrames || []});
3939
const {items: filteredItems, searchTerm, setSearchTerm} = filterProps;
4040
const clearSearchTerm = () => setSearchTerm('');
4141
const {handleSort, items, sortConfig} = useSortErrors({items: filteredItems});
@@ -91,7 +91,7 @@ function ErrorList({errorCrumbs, startTimestampMs}: Props) {
9191
ref={e => e && registerChild?.(e)}
9292
rowIndex={rowIndex}
9393
sortConfig={sortConfig}
94-
crumb={error}
94+
frame={error}
9595
startTimestampMs={startTimestampMs}
9696
style={{...style, height: BODY_HEIGHT}}
9797
/>
@@ -103,9 +103,9 @@ function ErrorList({errorCrumbs, startTimestampMs}: Props) {
103103

104104
return (
105105
<FluidHeight>
106-
<ErrorFilters errorCrumbs={errorCrumbs} {...filterProps} />
106+
<ErrorFilters errorFrames={errorFrames} {...filterProps} />
107107
<ErrorTable>
108-
{errorCrumbs ? (
108+
{errorFrames ? (
109109
<OverflowHidden>
110110
<AutoSizer onResize={onWrapperResize}>
111111
{({height, width}) => (
@@ -121,7 +121,7 @@ function ErrorList({errorCrumbs, startTimestampMs}: Props) {
121121
height={height}
122122
noContentRenderer={() => (
123123
<NoRowRenderer
124-
unfilteredItems={errorCrumbs}
124+
unfilteredItems={errorFrames}
125125
clearSearchTerm={clearSearchTerm}
126126
>
127127
{t('No errors! Go make some.')}

static/app/views/replays/detail/errorList/useErrorFilters.tsx

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import {useCallback, useMemo} from 'react';
22

33
import type {SelectOption} from 'sentry/components/compactSelect';
4-
import type {Crumb} from 'sentry/types/breadcrumbs';
54
import {decodeList, decodeScalar} from 'sentry/utils/queryString';
65
import useFiltersInLocationQuery from 'sentry/utils/replays/hooks/useFiltersInLocationQuery';
6+
import type {ErrorFrame} from 'sentry/utils/replays/types';
77
import {filterItems} from 'sentry/views/replays/detail/utils';
88

99
export interface ErrorSelectOption extends SelectOption<string> {
@@ -18,28 +18,30 @@ export type FilterFields = {
1818
};
1919

2020
type Options = {
21-
errorCrumbs: Crumb[];
21+
errorFrames: ErrorFrame[];
2222
};
2323

2424
type Return = {
2525
getProjectOptions: () => ErrorSelectOption[];
26-
items: Crumb[];
26+
items: ErrorFrame[];
2727
searchTerm: string;
2828
selectValue: string[];
2929
setFilters: (val: ErrorSelectOption[]) => void;
3030
setSearchTerm: (searchTerm: string) => void;
3131
};
3232

3333
const FILTERS = {
34-
project: (item: Crumb, projects: string[]) =>
34+
project: (item: ErrorFrame, projects: string[]) =>
3535
// @ts-expect-error
3636
projects.length === 0 || projects.includes(item.data.project || ''),
3737

38-
searchTerm: (item: Crumb, searchTerm: string) =>
39-
JSON.stringify([item.message, item.description]).toLowerCase().includes(searchTerm),
38+
searchTerm: (item: ErrorFrame, searchTerm: string) =>
39+
[item.message, ...item.data.labels].some(str =>
40+
str.toLowerCase().includes(searchTerm)
41+
),
4042
};
4143

42-
function useErrorFilters({errorCrumbs}: Options): Return {
44+
function useErrorFilters({errorFrames}: Options): Return {
4345
const {setFilter, query} = useFiltersInLocationQuery<FilterFields>();
4446

4547
const project = decodeList(query.f_e_project);
@@ -48,18 +50,18 @@ function useErrorFilters({errorCrumbs}: Options): Return {
4850
const items = useMemo(
4951
() =>
5052
filterItems({
51-
items: errorCrumbs,
53+
items: errorFrames,
5254
filterFns: FILTERS,
5355
filterVals: {project, searchTerm},
5456
}),
55-
[errorCrumbs, project, searchTerm]
57+
[errorFrames, project, searchTerm]
5658
);
5759

5860
const getProjectOptions = useCallback(
5961
() =>
6062
Array.from(
6163
new Set(
62-
errorCrumbs
64+
errorFrames
6365
// @ts-expect-error
6466
.map(crumb => crumb.data.project)
6567
.concat(project)
@@ -74,7 +76,7 @@ function useErrorFilters({errorCrumbs}: Options): Return {
7476
qs: 'f_e_project',
7577
})
7678
),
77-
[errorCrumbs, project]
79+
[errorFrames, project]
7880
);
7981

8082
const setSearchTerm = useCallback(

static/app/views/replays/detail/errorList/useSortErrors.tsx

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,25 @@
11
import {useCallback, useMemo} from 'react';
22

3-
import type {Crumb} from 'sentry/types/breadcrumbs';
3+
import type {ErrorFrame} from 'sentry/utils/replays/types';
44
import useUrlParams from 'sentry/utils/useUrlParams';
55

66
interface SortConfig {
77
asc: boolean;
8-
by: keyof Crumb | string;
9-
getValue: (row: Crumb) => any;
8+
by: keyof ErrorFrame | string;
9+
getValue: (row: ErrorFrame) => any;
1010
}
1111

12-
const SortStrategies: Record<string, (row: Crumb) => any> = {
13-
id: row => row.id,
14-
title: row => row.description,
15-
// @ts-expect-error
16-
project: row => row.data?.project,
12+
const SortStrategies: Record<string, (row: ErrorFrame) => any> = {
13+
id: row => row.data.eventId,
14+
title: row => row.message,
15+
project: row => row.data.projectSlug,
1716
timestamp: row => row.timestamp,
1817
};
1918

2019
const DEFAULT_ASC = 'true';
2120
const DEFAULT_BY = 'timestamp';
2221

23-
type Opts = {items: Crumb[]};
22+
type Opts = {items: ErrorFrame[]};
2423

2524
function useSortErrors({items}: Opts) {
2625
const {getParamValue: getSortAsc, setParamValue: setSortAsc} = useUrlParams(
@@ -66,8 +65,8 @@ function useSortErrors({items}: Opts) {
6665
};
6766
}
6867

69-
function sortErrors(crumbs: Crumb[], sortConfig: SortConfig): Crumb[] {
70-
return [...crumbs].sort((a, b) => {
68+
function sortErrors(frames: ErrorFrame[], sortConfig: SortConfig): ErrorFrame[] {
69+
return [...frames].sort((a, b) => {
7170
let valueA = sortConfig.getValue(a);
7271
let valueB = sortConfig.getValue(b);
7372

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ function FocusArea({}: Props) {
4343
case TabKey.ERRORS:
4444
return (
4545
<ErrorList
46-
errorCrumbs={replay?.getIssueCrumbs()}
47-
startTimestampMs={replay?.getReplay()?.started_at?.getTime() || 0}
46+
errorFrames={replay?.getErrorFrames()}
47+
startTimestampMs={replay?.getReplay().started_at.getTime() ?? 0}
4848
/>
4949
);
5050
case TabKey.DOM:

0 commit comments

Comments
 (0)