Skip to content

Commit 530d898

Browse files
authored
feat(replays): Display component name in selector when available (#68840)
Adds a display selector so the selector is more readable if there's a component name. <img width="1134" alt="image" src="https://github.com/getsentry/sentry/assets/55311782/34f41b68-b1b4-4430-beed-010c13bf6fcc"> Related to #64673
1 parent 24ebac1 commit 530d898

File tree

3 files changed

+60
-21
lines changed

3 files changed

+60
-21
lines changed

static/app/views/replays/deadRageClick/constructSelector.spec.tsx

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,107 +7,121 @@ describe('constructSelector', () => {
77
alt: 'view more',
88
aria_label: 'View More',
99
class: ['classA'],
10+
component_name: 'TestButton',
1011
id: 'ID1',
1112
role: 'button',
1213
tag: 'button',
1314
testid: 'button-test',
1415
title: 'cool title',
1516
},
1617
fullSelector:
17-
'button#ID1.classA[role="button"][aria="View More"][data-test-id="button-test"][alt="view more"][title="cool title"]',
18+
'button#ID1.classA[role="button"][aria="View More"][data-test-id="button-test"][alt="view more"][title="cool title"][data-sentry-component="TestButton"]',
1819
selector:
19-
'button#ID1.classA[role="button"][aria="View More"][data-test-id="button-test"][alt="view more"][title="cool title"]',
20+
'TestButton#ID1[role="button"][aria="View More"][data-test-id="button-test"][alt="view more"][title="cool title"]',
2021
},
2122
{
2223
element: {
2324
alt: '',
2425
aria_label: '',
2526
class: ['', ''],
27+
component_name: '',
2628
id: '',
2729
role: '',
2830
tag: 'a',
2931
testid: '',
3032
title: '',
3133
},
32-
fullSelector: 'a[role=""][aria=""][data-test-id=""][alt=""][title=""]',
34+
fullSelector:
35+
'a[role=""][aria=""][data-test-id=""][alt=""][title=""][data-sentry-component=""]',
3336
selector: 'a',
3437
},
3538
{
3639
element: {
3740
alt: '',
3841
aria_label: '',
3942
class: ['classA', ''],
43+
component_name: '',
4044
id: '',
4145
role: '',
4246
tag: 'a',
4347
testid: '',
4448
title: '',
4549
},
46-
fullSelector: 'a.classA[role=""][aria=""][data-test-id=""][alt=""][title=""]',
50+
fullSelector:
51+
'a.classA[role=""][aria=""][data-test-id=""][alt=""][title=""][data-sentry-component=""]',
4752
selector: 'a.classA',
4853
},
4954
{
5055
element: {
5156
alt: '',
5257
aria_label: '',
5358
class: ['classA', ''],
59+
component_name: '',
5460
id: 'ID2',
5561
role: '',
5662
tag: 'a',
5763
testid: '',
5864
title: '',
5965
},
60-
fullSelector: 'a#ID2.classA[role=""][aria=""][data-test-id=""][alt=""][title=""]',
66+
fullSelector:
67+
'a#ID2.classA[role=""][aria=""][data-test-id=""][alt=""][title=""][data-sentry-component=""]',
6168
selector: 'a#ID2.classA',
6269
},
6370
{
6471
element: {
6572
alt: '',
6673
aria_label: '',
6774
class: ['classA', 'classB'],
75+
component_name: 'TestButton',
6876
id: 'ID2',
6977
role: '',
7078
tag: 'a',
7179
testid: '',
7280
title: '',
7381
},
7482
fullSelector:
75-
'a#ID2.classA.classB[role=""][aria=""][data-test-id=""][alt=""][title=""]',
76-
selector: 'a#ID2.classA.classB',
83+
'a#ID2.classA.classB[role=""][aria=""][data-test-id=""][alt=""][title=""][data-sentry-component="TestButton"]',
84+
selector: 'TestButton#ID2',
7785
},
7886
{
7987
element: {
8088
alt: '',
8189
aria_label: 'hello',
8290
class: ['classA', 'classB'],
91+
component_name: '',
8392
id: 'ID2',
8493
role: '',
8594
tag: 'a',
8695
testid: '',
8796
title: '',
8897
},
8998
fullSelector:
90-
'a#ID2.classA.classB[role=""][aria="hello"][data-test-id=""][alt=""][title=""]',
99+
'a#ID2.classA.classB[role=""][aria="hello"][data-test-id=""][alt=""][title=""][data-sentry-component=""]',
91100
selector: 'a#ID2.classA.classB[aria="hello"]',
92101
},
93102
{
94103
element: {
95104
alt: '',
96105
aria_label: 'hello',
106+
component_name: 'TestHello',
97107
class: [''],
98108
id: 'ID2',
99109
role: '',
100110
tag: 'a',
101111
testid: '',
102112
title: '',
103113
},
104-
fullSelector: 'a#ID2[role=""][aria="hello"][data-test-id=""][alt=""][title=""]',
105-
selector: 'a#ID2[aria="hello"]',
114+
fullSelector:
115+
'a#ID2[role=""][aria="hello"][data-test-id=""][alt=""][title=""][data-sentry-component="TestHello"]',
116+
selector: 'TestHello#ID2[aria="hello"]',
106117
},
107118
])(
108119
'should construct the correct trimmed selector and full selector, for each element in the list',
109120
({element, fullSelector, selector}) => {
110-
expect(constructSelector(element)).toStrictEqual({fullSelector, selector});
121+
expect(constructSelector(element)).toStrictEqual({
122+
fullSelector,
123+
selector,
124+
});
111125
}
112126
);
113127
});

static/app/views/replays/deadRageClick/constructSelector.tsx

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,33 +4,53 @@ function trimAttribute(elementAttribute, fullAlltribute) {
44
return elementAttribute === '' ? '' : fullAlltribute;
55
}
66

7+
// fullSelector is used for searches since searching without all attributes returns too many replays
8+
// selector is the selector shown in the selector widget/table and when you hover
79
export default function constructSelector(element: ReplayClickElement) {
8-
const fullAlt = '[alt="' + element.alt + '"]';
9-
const alt = trimAttribute(element.alt, fullAlt);
10+
const tag = element.tag;
1011

11-
const fullAriaLabel = '[aria="' + element.aria_label + '"]';
12-
const ariaLabel = trimAttribute(element.aria_label, fullAriaLabel);
12+
const id = trimAttribute(element.id, '#' + element.id);
1313

1414
const trimClass = element.class.filter(e => e !== '');
1515
const classWithPeriod = trimClass.join('.');
1616
const classNoPeriod = classWithPeriod.replace('.', '');
1717
const classes = trimAttribute(classNoPeriod, '.' + classWithPeriod);
1818

19-
const id = trimAttribute(element.id, '#' + element.id);
19+
const fullAlt = '[alt="' + element.alt + '"]';
20+
const alt = trimAttribute(element.alt, fullAlt);
21+
22+
const fullAriaLabel = '[aria="' + element.aria_label + '"]';
23+
const ariaLabel = trimAttribute(element.aria_label, fullAriaLabel);
2024

2125
const fullRole = '[role="' + element.role + '"]';
2226
const role = trimAttribute(element.role, fullRole);
2327

24-
const tag = element.tag;
25-
2628
const fullTestId = '[data-test-id="' + element.testid + '"]';
2729
const testId = trimAttribute(element.testid, fullTestId);
2830

2931
const fullTitle = '[title="' + element.title + '"]';
3032
const title = trimAttribute(element.title, fullTitle);
3133

34+
const fullComponentName = '[data-sentry-component="' + element.component_name + '"]';
35+
const componentName = trimAttribute(element.component_name, fullComponentName);
36+
3237
const fullSelector =
33-
tag + id + classes + fullRole + fullAriaLabel + fullTestId + fullAlt + fullTitle;
34-
const selector = tag + id + classes + role + ariaLabel + testId + alt + title;
38+
tag +
39+
id +
40+
classes +
41+
fullRole +
42+
fullAriaLabel +
43+
fullTestId +
44+
fullAlt +
45+
fullTitle +
46+
fullComponentName;
47+
const selector =
48+
(componentName ? element.component_name + id : tag + id + classes) +
49+
role +
50+
ariaLabel +
51+
testId +
52+
alt +
53+
title;
54+
3555
return {fullSelector, selector};
3656
}

static/app/views/replays/types.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,11 @@ export interface ReplayError {
176176

177177
export type DeadRageSelectorItem = {
178178
aria_label: string;
179-
dom_element: {fullSelector: string; projectId: number; selector: string};
179+
dom_element: {
180+
fullSelector: string;
181+
projectId: number;
182+
selector: string;
183+
};
180184
element: string;
181185
project_id: number;
182186
count_dead_clicks?: number;
@@ -197,6 +201,7 @@ export type ReplayClickElement = {
197201
alt: string;
198202
aria_label: string;
199203
class: string[];
204+
component_name: string;
200205
id: string;
201206
role: string;
202207
tag: string;

0 commit comments

Comments
 (0)