Skip to content

Commit 849108f

Browse files
fix(replay): Adjust number of columns on Replay List depending on container size (#97791)
The columns visible change dynamically: | Desc | Img | | --- | --- | | Wide | <img width="793" height="146" alt="SCR-20250813-kedn" src="https://github.com/user-attachments/assets/463034c9-8917-470a-9cf1-47342a77d362" /> | Medium | <img width="590" height="119" alt="SCR-20250813-kece" src="https://github.com/user-attachments/assets/5185ad55-4ad3-4d6b-b519-85ed4e3f241a" /> | Narrow | <img width="430" height="146" alt="SCR-20250813-kecv" src="https://github.com/user-attachments/assets/c87d515f-843b-4f23-b5c3-9cb43a0dfa88" /> I also removed an extra `<Flex>` which helps to make the emails truncate with an ellipsis. The 2nd row doesn't get an ellipsis though right now. <img width="328" height="136" alt="SCR-20250813-kdyk" src="https://github.com/user-attachments/assets/c88d67fa-e2ac-4b02-a7cf-bd5825ae0dca" /> Fixes REPLAY-623 Co-authored-by: Nar Saynorath <[email protected]>
1 parent 087ef7f commit 849108f

File tree

5 files changed

+100
-43
lines changed

5 files changed

+100
-43
lines changed

static/app/components/replays/table/replayTable.tsx

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type {RefObject} from 'react';
12
import styled from '@emotion/styled';
23

34
import {Alert} from 'sentry/components/core/alert';
@@ -25,13 +26,15 @@ type Props = SortProps & {
2526
isPending: boolean;
2627
replays: ReplayListRecord[];
2728
showDropdownFilters: boolean;
29+
ref?: RefObject<HTMLDivElement | null>;
2830
};
2931

3032
export default function ReplayTable({
3133
columns,
3234
error,
3335
isPending,
3436
onSortClick,
37+
ref,
3538
replays,
3639
showDropdownFilters,
3740
sort,
@@ -43,6 +46,7 @@ export default function ReplayTable({
4346
return (
4447
<StyledSimpleTable
4548
data-test-id="replay-table-loading"
49+
ref={ref}
4650
style={{gridTemplateColumns}}
4751
>
4852
<ReplayTableHeader
@@ -62,6 +66,7 @@ export default function ReplayTable({
6266
return (
6367
<StyledSimpleTable
6468
data-test-id="replay-table-errored"
69+
ref={ref}
6570
style={{gridTemplateColumns}}
6671
>
6772
<ReplayTableHeader
@@ -82,7 +87,11 @@ export default function ReplayTable({
8287
}
8388

8489
return (
85-
<StyledSimpleTable data-test-id="replay-table" style={{gridTemplateColumns}}>
90+
<StyledSimpleTable
91+
data-test-id="replay-table"
92+
ref={ref}
93+
style={{gridTemplateColumns}}
94+
>
8695
<ReplayTableHeader
8796
columns={columns}
8897
onSortClick={onSortClick}
@@ -141,8 +150,6 @@ function getErrorMessage(fetchError: RequestError) {
141150
}
142151

143152
const RowCell = styled(SimpleTable.RowCell)`
144-
overflow-x: auto;
145-
146153
/* Used for cell menu items that are hidden by default */
147154
&:hover [data-visible-on-hover='true'] {
148155
opacity: 1;

static/app/components/replays/table/replayTableColumns.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -584,11 +584,10 @@ export const ReplaySessionColumn: ReplayTableColumn = {
584584
size={24}
585585
/>
586586
<SubText>
587-
<Flex gap="xs" align="start">
588-
<DisplayName data-underline-on-hover>
589-
{replay.user.display_name || t('Anonymous User')}
590-
</DisplayName>
591-
</Flex>
587+
<DisplayName data-underline-on-hover>
588+
{replay.user.display_name || t('Anonymous User')}
589+
</DisplayName>
590+
592591
<Flex gap="xs">
593592
{/* Avatar is used instead of ProjectBadge because using ProjectBadge increases spacing, which doesn't look as good */}
594593
{project ? <ProjectAvatar size={12} project={project} /> : null}
@@ -667,6 +666,7 @@ const CellLink = styled(Link)`
667666
margin: -${p => p.theme.space.xl};
668667
padding: ${p => p.theme.space.xl};
669668
flex-grow: 1;
669+
width: 100%;
670670
671671
&:before {
672672
content: '';

static/app/components/tables/simpleTable/index.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type {ComponentProps, CSSProperties, HTMLAttributes} from 'react';
1+
import type {ComponentProps, CSSProperties, HTMLAttributes, RefObject} from 'react';
22
import {css} from '@emotion/react';
33
import styled from '@emotion/styled';
44

@@ -10,6 +10,7 @@ import {defined} from 'sentry/utils';
1010

1111
interface TableProps extends HTMLAttributes<HTMLDivElement> {
1212
children: React.ReactNode;
13+
ref?: RefObject<HTMLDivElement | null>;
1314
}
1415

1516
interface RowProps extends HTMLAttributes<HTMLDivElement> {

static/app/views/replays/list/replayIndexTable.tsx

Lines changed: 8 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {Fragment, useMemo} from 'react';
1+
import {Fragment, useMemo, useRef} from 'react';
22
import styled from '@emotion/styled';
33

44
import {Button} from 'sentry/components/core/button';
@@ -9,17 +9,6 @@ import {
99
useNeedsJetpackComposePiiNotice,
1010
} from 'sentry/components/replays/jetpackComposePiiNotice';
1111
import ReplayTable from 'sentry/components/replays/table/replayTable';
12-
import {
13-
ReplayActivityColumn,
14-
ReplayBrowserColumn,
15-
ReplayCountDeadClicksColumn,
16-
ReplayCountErrorsColumn,
17-
ReplayCountRageClicksColumn,
18-
ReplayDurationColumn,
19-
ReplayOSColumn,
20-
ReplaySelectColumn,
21-
ReplaySessionColumn,
22-
} from 'sentry/components/replays/table/replayTableColumns';
2312
import useReplayTableSort from 'sentry/components/replays/table/useReplayTableSort';
2413
import {t, tct} from 'sentry/locale';
2514
import {trackAnalytics} from 'sentry/utils/analytics';
@@ -36,6 +25,7 @@ import {
3625
} from 'sentry/utils/replays/sdkVersions';
3726
import {MutableSearch} from 'sentry/utils/tokenizeSearch';
3827
import useLocationQuery from 'sentry/utils/url/useLocationQuery';
28+
import {useDimensions} from 'sentry/utils/useDimensions';
3929
import {useLocalStorageState} from 'sentry/utils/useLocalStorageState';
4030
import {useNavigate} from 'sentry/utils/useNavigate';
4131
import useOrganization from 'sentry/utils/useOrganization';
@@ -45,30 +35,10 @@ import useAllMobileProj from 'sentry/views/replays/detail/useAllMobileProj';
4535
import BulkDeleteAlert from 'sentry/views/replays/list/bulkDeleteAlert';
4636
import ReplaysFilters from 'sentry/views/replays/list/filters';
4737
import ReplaysSearch from 'sentry/views/replays/list/search';
38+
import useReplayIndexTableColumns from 'sentry/views/replays/list/useReplayIndexTableColumns';
4839
import DeadRageSelectorCards from 'sentry/views/replays/selectors/deadRageSelectorCards';
4940
import type {ReplayListRecord} from 'sentry/views/replays/types';
5041

51-
const COLUMNS_WEB = [
52-
ReplaySelectColumn,
53-
ReplaySessionColumn,
54-
ReplayOSColumn,
55-
ReplayBrowserColumn,
56-
ReplayDurationColumn,
57-
ReplayCountDeadClicksColumn,
58-
ReplayCountRageClicksColumn,
59-
ReplayCountErrorsColumn,
60-
ReplayActivityColumn,
61-
] as const;
62-
63-
const COLUMNS_MOBILE = [
64-
ReplaySelectColumn,
65-
ReplaySessionColumn,
66-
ReplayOSColumn,
67-
ReplayDurationColumn,
68-
ReplayCountErrorsColumn,
69-
ReplayActivityColumn,
70-
] as const;
71-
7242
export default function ReplayIndexTable() {
7343
const queryClient = useQueryClient();
7444
const organization = useOrganization();
@@ -77,6 +47,9 @@ export default function ReplayIndexTable() {
7747
selection: {projects},
7848
} = usePageFilters();
7949

50+
const tableRef = useRef<HTMLDivElement>(null);
51+
const tableDimensions = useDimensions({elementRef: tableRef});
52+
8053
const rageClicksSdkVersion = useProjectSdkNeedsUpdate({
8154
minVersion: MIN_DEAD_RAGE_CLICK_SDK.minVersion,
8255
projectId: projects.map(String),
@@ -109,7 +82,7 @@ export default function ReplayIndexTable() {
10982
const replays = data?.data?.map(mapResponseToReplayRecord) ?? [];
11083

11184
const {allMobileProj} = useAllMobileProj({});
112-
const columns = allMobileProj ? COLUMNS_MOBILE : COLUMNS_WEB;
85+
const columns = useReplayIndexTableColumns({allMobileProj, tableDimensions});
11386

11487
const needsSDKUpdateForClickSearch = useNeedsSDKUpdateForClickSearch(query);
11588

@@ -169,6 +142,7 @@ export default function ReplayIndexTable() {
169142
</Fragment>
170143
) : (
171144
<ReplayTable
145+
ref={tableRef}
172146
columns={columns}
173147
error={error}
174148
isPending={isPending}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import {useMemo} from 'react';
2+
3+
import {
4+
ReplayActivityColumn,
5+
ReplayBrowserColumn,
6+
ReplayCountDeadClicksColumn,
7+
ReplayCountErrorsColumn,
8+
ReplayCountRageClicksColumn,
9+
ReplayDurationColumn,
10+
ReplayOSColumn,
11+
ReplaySelectColumn,
12+
ReplaySessionColumn,
13+
} from 'sentry/components/replays/table/replayTableColumns';
14+
import type {useDimensions} from 'sentry/utils/useDimensions';
15+
16+
const MOBILE = [
17+
ReplaySelectColumn,
18+
ReplaySessionColumn,
19+
ReplayOSColumn,
20+
ReplayDurationColumn,
21+
ReplayCountErrorsColumn,
22+
ReplayActivityColumn,
23+
] as const;
24+
25+
const WEB_MAX_1000 = [
26+
ReplaySelectColumn,
27+
ReplaySessionColumn,
28+
ReplayOSColumn,
29+
ReplayBrowserColumn,
30+
ReplayDurationColumn,
31+
ReplayCountErrorsColumn,
32+
ReplayActivityColumn,
33+
] as const;
34+
35+
const WEB_MAX_800 = [
36+
ReplaySelectColumn,
37+
ReplaySessionColumn,
38+
ReplayOSColumn,
39+
ReplayBrowserColumn,
40+
ReplayDurationColumn,
41+
ReplayCountErrorsColumn,
42+
] as const;
43+
44+
const WEB_ALL = [
45+
ReplaySelectColumn,
46+
ReplaySessionColumn,
47+
ReplayOSColumn,
48+
ReplayBrowserColumn,
49+
ReplayDurationColumn,
50+
ReplayCountDeadClicksColumn,
51+
ReplayCountRageClicksColumn,
52+
ReplayCountErrorsColumn,
53+
ReplayActivityColumn,
54+
] as const;
55+
56+
export default function useReplayIndexTableColumns({
57+
allMobileProj,
58+
tableDimensions,
59+
}: {
60+
allMobileProj: boolean;
61+
tableDimensions: ReturnType<typeof useDimensions>;
62+
}) {
63+
return useMemo(() => {
64+
if (allMobileProj) {
65+
return MOBILE;
66+
}
67+
if (tableDimensions.width < 800) {
68+
return WEB_MAX_800;
69+
}
70+
if (tableDimensions.width < 1000) {
71+
return WEB_MAX_1000;
72+
}
73+
return WEB_ALL;
74+
}, [allMobileProj, tableDimensions.width]);
75+
}

0 commit comments

Comments
 (0)