@atlaskit/react-ufo 5.4.0 → 5.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +20 -9
- package/CHANGELOG.md +9 -0
- package/api-reference.md +899 -456
- package/dist/cjs/config/index.js +13 -6
- package/dist/cjs/create-interaction-extra-metrics-payload/index.js +19 -18
- package/dist/cjs/create-payload/index.js +2 -2
- package/dist/es2019/config/index.js +12 -8
- package/dist/es2019/create-interaction-extra-metrics-payload/index.js +6 -5
- package/dist/es2019/create-payload/index.js +1 -1
- package/dist/esm/config/index.js +12 -6
- package/dist/esm/create-interaction-extra-metrics-payload/index.js +20 -19
- package/dist/esm/create-payload/index.js +2 -2
- package/dist/types/common/react-ufo-payload-schema.d.ts +1 -1
- package/dist/types/config/index.d.ts +2 -1
- package/dist/types-ts4.5/common/react-ufo-payload-schema.d.ts +1 -1
- package/dist/types-ts4.5/config/index.d.ts +2 -1
- package/package.json +2 -2
package/api-reference.md
CHANGED
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: React-UFO - Instrumentation API Reference
|
|
3
|
-
description:
|
|
3
|
+
description: Complete API reference for @atlaskit/react-ufo performance instrumentation
|
|
4
4
|
platform: platform
|
|
5
5
|
product: ufo
|
|
6
6
|
category: devguide
|
|
7
7
|
subcategory: react-ufo
|
|
8
|
-
date: '
|
|
8
|
+
date: '2026-02-27'
|
|
9
9
|
---
|
|
10
10
|
|
|
11
11
|
# React UFO Instrumentation API Reference
|
|
12
12
|
|
|
13
|
-
React UFO
|
|
14
|
-
metrics
|
|
15
|
-
|
|
13
|
+
React UFO is a performance monitoring library for React applications at Atlassian. It measures user
|
|
14
|
+
experience metrics including Time to App Idle (TTAI), Visual Completion (VC90/VC100), First
|
|
15
|
+
Meaningful Paint (FMP), and interaction responsiveness metrics like Input Delay (ID) and Input to
|
|
16
|
+
Next Paint (INP).
|
|
17
|
+
|
|
18
|
+
For a deep dive into how React UFO calculates these metrics, see
|
|
19
|
+
[An overview of how React UFO calculates frontend performance metrics](https://hello.atlassian.net/wiki/spaces/APD/pages/5046492124).
|
|
16
20
|
|
|
17
21
|
## Getting Started
|
|
18
22
|
|
|
@@ -20,6 +24,23 @@ Interactive (TTI), Time to Visual Complete (TTVC), and custom interaction metric
|
|
|
20
24
|
yarn add @atlaskit/react-ufo
|
|
21
25
|
```
|
|
22
26
|
|
|
27
|
+
## Key Metrics
|
|
28
|
+
|
|
29
|
+
| Metric | Description |
|
|
30
|
+
| ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
31
|
+
| **TTAI** (Time to App Idle) | Time from interaction start until the next paint after all UFO holds become inactive and DOM mutations finish |
|
|
32
|
+
| **VC90 / VC100** (Visual Completion) | Time until 90% / 100% of visible viewport mutations are complete |
|
|
33
|
+
| **FMP** (First Meaningful Paint) | Time for primary page content to become visible. Derived from BM3 instrumentation, SSR timing, or defaults to TTAI |
|
|
34
|
+
| **TTI** (Time to Interactive) | When the page is visible and ready for interaction. Calculated via BM3 manual instrumentation, earliest Apdex `stopTime`, or defaults to TTAI |
|
|
35
|
+
| **INP** (Input to Next Paint) | Time for browser to render the next frame after receiving user input |
|
|
36
|
+
| **ID** (Input Delay) | Time from the physical user action until first script execution |
|
|
37
|
+
| **Response** | Time to next active React UFO hold rendered on the page. If no hold is active, equals Result (TTAI) |
|
|
38
|
+
| **Result** | Time to next render when no React UFO hold is active. Equivalent to TTAI |
|
|
39
|
+
| **IVC100** (Interaction Visually Complete) | Time until last mutation visible in viewport for press interactions |
|
|
40
|
+
| **TBT** (Total Blocking Time) | Sum of blocking time from long tasks (>50ms) between FCP and TTI |
|
|
41
|
+
| **CLS** (Cumulative Layout Shift) | Largest burst of layout shift scores during the interaction window |
|
|
42
|
+
| **LCP** (Largest Contentful Paint) | Render time of the largest image or text block |
|
|
43
|
+
|
|
23
44
|
## API Reference
|
|
24
45
|
|
|
25
46
|
### Interaction Initializers
|
|
@@ -28,41 +49,53 @@ yarn add @atlaskit/react-ufo
|
|
|
28
49
|
- [traceUFOPress](#traceufopress)
|
|
29
50
|
- [traceUFOTransition](#traceufotransition)
|
|
30
51
|
- [traceUFOInteraction](#traceufointeraction)
|
|
31
|
-
- [
|
|
52
|
+
- [traceUFOHover](#traceufohover)
|
|
53
|
+
- [traceUFORedirect](#traceufodirect)
|
|
32
54
|
|
|
33
55
|
### React Components
|
|
34
56
|
|
|
35
57
|
- [UFOSegment](#ufosegment)
|
|
58
|
+
- [UFOThirdPartySegment](#ufothirdpartysegment)
|
|
36
59
|
- [UFOLabel](#ufolabel)
|
|
37
60
|
- [UFOLoadHold](#ufoloadhold)
|
|
61
|
+
- [UFOInteractionIgnore](#ufointeractionignore)
|
|
38
62
|
- [Suspense](#suspense)
|
|
39
63
|
- [UFOCustomData](#ufocustomdata)
|
|
40
|
-
- [UFOCustomCohortData](#ufocustomcohortdata)
|
|
64
|
+
- [UFOCustomCohortData](#ufocustomcohortdata)
|
|
65
|
+
- [UFOCustomMark](#ufocustommark)
|
|
66
|
+
- [Placeholder](#placeholder)
|
|
41
67
|
|
|
42
68
|
### React Hooks
|
|
43
69
|
|
|
44
70
|
- [usePressTracing](#usepresstracing)
|
|
45
|
-
- [useUFOTypingPerformanceTracing](#useufotypingperformancetracing)
|
|
71
|
+
- [useUFOTypingPerformanceTracing](#useufotypingperformancetracing)
|
|
46
72
|
- [useUFOTransitionCompleter](#useufotransitioncompleter)
|
|
47
73
|
- [useUFOReportError](#useuforeporterror)
|
|
74
|
+
- [useReportTerminalError](#usereportterminaerror)
|
|
48
75
|
|
|
49
76
|
### Utility Functions
|
|
50
77
|
|
|
51
78
|
- [addUFOCustomData](#addufocustomdata)
|
|
52
|
-
- [addUFOCustomCohortData](#addufocustomcohortdata)
|
|
79
|
+
- [addUFOCustomCohortData](#addufocustomcohortdata)
|
|
80
|
+
- [addUFOCustomMark](#addufocustommark-1)
|
|
81
|
+
- [addCustomSpans](#addcustomspans)
|
|
82
|
+
- [addBM3TimingsToUFO](#addbm3timingstoufo)
|
|
53
83
|
- [addFeatureFlagAccessed](#addfeatureflagaccessed)
|
|
54
84
|
- [setInteractionError](#setinteractionerror)
|
|
85
|
+
- [setTerminalError](#setterminalerror)
|
|
86
|
+
- [sinkTerminalErrorHandler](#sinkterminalerrorhandler)
|
|
55
87
|
- [getUFORouteName](#getuforoutename)
|
|
88
|
+
- [updatePageloadName](#updatepageloadname)
|
|
56
89
|
|
|
57
90
|
### Component Integration
|
|
58
91
|
|
|
59
|
-
- [interactionName Property](#interactionname)
|
|
92
|
+
- [interactionName Property](#interactionname-property)
|
|
60
93
|
|
|
61
94
|
### Advanced APIs
|
|
62
95
|
|
|
63
96
|
- [SSR Configuration](#ssr-configuration)
|
|
64
|
-
- [
|
|
65
|
-
- [
|
|
97
|
+
- [Configuration](#configuration)
|
|
98
|
+
- [Experience Trace ID Context](#experience-trace-id-context)
|
|
66
99
|
|
|
67
100
|
---
|
|
68
101
|
|
|
@@ -70,8 +103,14 @@ yarn add @atlaskit/react-ufo
|
|
|
70
103
|
|
|
71
104
|
### traceUFOPageLoad
|
|
72
105
|
|
|
73
|
-
Initializes measurement of page load performance for a specific route or page.
|
|
74
|
-
|
|
106
|
+
Initializes measurement of page load performance for a specific route or page. Should be called
|
|
107
|
+
early in the page lifecycle to capture accurate timing data. Uses sampling rates configured in your
|
|
108
|
+
UFO settings.
|
|
109
|
+
|
|
110
|
+
Only one page load interaction can be active at a time. If called without a `ufoName` while no
|
|
111
|
+
interaction is active, it creates a new interaction with a hold until a name is provided. If called
|
|
112
|
+
with a `ufoName` while an unnamed page load interaction is active, it updates the name and releases
|
|
113
|
+
the hold.
|
|
75
114
|
|
|
76
115
|
**Import:**
|
|
77
116
|
|
|
@@ -79,57 +118,64 @@ be called early in the page lifecycle to capture accurate timing data.
|
|
|
79
118
|
import traceUFOPageLoad from '@atlaskit/react-ufo/trace-pageload';
|
|
80
119
|
```
|
|
81
120
|
|
|
82
|
-
**
|
|
121
|
+
**Signature:**
|
|
83
122
|
|
|
84
123
|
```typescript
|
|
85
|
-
traceUFOPageLoad(
|
|
124
|
+
function traceUFOPageLoad(
|
|
125
|
+
ufoName?: string | null | undefined,
|
|
126
|
+
routeName?: string | null | undefined, // defaults to ufoName
|
|
127
|
+
): void;
|
|
86
128
|
```
|
|
87
129
|
|
|
88
130
|
**Parameters:**
|
|
89
131
|
|
|
90
|
-
- `ufoName` (string): The UFO interaction name used for sampling and event identification
|
|
132
|
+
- `ufoName` (string, optional): The UFO interaction name used for sampling and event identification
|
|
133
|
+
- `routeName` (string, optional): The route name for context. Defaults to `ufoName` if not provided
|
|
91
134
|
|
|
92
135
|
**Example:**
|
|
93
136
|
|
|
94
137
|
```typescript
|
|
95
|
-
// In your route component
|
|
96
|
-
import { useEffect } from 'react';
|
|
97
138
|
import traceUFOPageLoad from '@atlaskit/react-ufo/trace-pageload';
|
|
98
139
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
traceUFOPageLoad('issue-view');
|
|
102
|
-
}, []);
|
|
140
|
+
// Simple usage
|
|
141
|
+
traceUFOPageLoad('issue-view');
|
|
103
142
|
|
|
104
|
-
|
|
105
|
-
|
|
143
|
+
// With separate route name
|
|
144
|
+
traceUFOPageLoad('issue-view', 'jira-issue-route');
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
**Also exports:**
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
import { updatePageloadName } from '@atlaskit/react-ufo/trace-pageload';
|
|
106
151
|
```
|
|
107
152
|
|
|
108
|
-
|
|
109
|
-
loads are measured.
|
|
153
|
+
See [updatePageloadName](#updatepageloadname) for details.
|
|
110
154
|
|
|
111
155
|
### traceUFOPress
|
|
112
156
|
|
|
113
157
|
Measures user interaction performance from a press/click event until all loading is complete. Use
|
|
114
158
|
this when `usePressTracing` hook or the `interactionName` property on components isn't suitable.
|
|
115
159
|
|
|
160
|
+
Only one interaction can be active at a time. Starting a new press interaction aborts any currently
|
|
161
|
+
active interaction. Aborted interactions do **not** report metrics to prevent data skewing.
|
|
162
|
+
|
|
116
163
|
**Import:**
|
|
117
164
|
|
|
118
165
|
```typescript
|
|
119
166
|
import traceUFOPress from '@atlaskit/react-ufo/trace-press';
|
|
120
167
|
```
|
|
121
168
|
|
|
122
|
-
**
|
|
169
|
+
**Signature:**
|
|
123
170
|
|
|
124
171
|
```typescript
|
|
125
|
-
traceUFOPress(
|
|
126
|
-
traceUFOPress('menu-open', performance.now()); // with custom timestamp
|
|
172
|
+
function traceUFOPress(name: string, timestamp?: number): void;
|
|
127
173
|
```
|
|
128
174
|
|
|
129
175
|
**Parameters:**
|
|
130
176
|
|
|
131
177
|
- `name` (string): Unique identifier for the interaction
|
|
132
|
-
- `timestamp` (number, optional): Custom timestamp
|
|
178
|
+
- `timestamp` (number, optional): Custom timestamp. Defaults to `performance.now()`
|
|
133
179
|
|
|
134
180
|
**Example:**
|
|
135
181
|
|
|
@@ -137,13 +183,12 @@ traceUFOPress('menu-open', performance.now()); // with custom timestamp
|
|
|
137
183
|
import traceUFOPress from '@atlaskit/react-ufo/trace-press';
|
|
138
184
|
|
|
139
185
|
function CustomButton() {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
};
|
|
186
|
+
const handleClick = () => {
|
|
187
|
+
traceUFOPress('custom-action');
|
|
188
|
+
performAsyncAction();
|
|
189
|
+
};
|
|
145
190
|
|
|
146
|
-
|
|
191
|
+
return <button onClick={handleClick}>Custom Action</button>;
|
|
147
192
|
}
|
|
148
193
|
```
|
|
149
194
|
|
|
@@ -159,8 +204,7 @@ function CustomButton() {
|
|
|
159
204
|
### traceUFOTransition
|
|
160
205
|
|
|
161
206
|
Measures navigation transition performance between routes or major state changes within a
|
|
162
|
-
single-page application.
|
|
163
|
-
transition.
|
|
207
|
+
single-page application. Aborts all active interactions and starts measuring the new transition.
|
|
164
208
|
|
|
165
209
|
**Import:**
|
|
166
210
|
|
|
@@ -168,40 +212,45 @@ transition.
|
|
|
168
212
|
import traceUFOTransition from '@atlaskit/react-ufo/trace-transition';
|
|
169
213
|
```
|
|
170
214
|
|
|
171
|
-
**
|
|
215
|
+
**Signature:**
|
|
172
216
|
|
|
173
217
|
```typescript
|
|
174
|
-
traceUFOTransition(
|
|
218
|
+
function traceUFOTransition(
|
|
219
|
+
ufoName: string | null | undefined,
|
|
220
|
+
routeName?: string | null | undefined, // defaults to ufoName
|
|
221
|
+
): void;
|
|
175
222
|
```
|
|
176
223
|
|
|
177
224
|
**Parameters:**
|
|
178
225
|
|
|
179
226
|
- `ufoName` (string): The UFO name for the transition
|
|
227
|
+
- `routeName` (string, optional): The route name for context. Defaults to `ufoName`
|
|
180
228
|
|
|
181
229
|
**Example:**
|
|
182
230
|
|
|
183
231
|
```typescript
|
|
184
232
|
import { useRouter } from '@atlassian/react-resource-router';
|
|
185
233
|
import traceUFOTransition from '@atlaskit/react-ufo/trace-transition';
|
|
234
|
+
import getUFORouteName from '@atlaskit/react-ufo/route-name';
|
|
186
235
|
|
|
187
236
|
function NavigationHandler() {
|
|
188
|
-
|
|
237
|
+
const [routerState] = useRouter();
|
|
189
238
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
239
|
+
useEffect(() => {
|
|
240
|
+
const routeName = getUFORouteName(routerState.route);
|
|
241
|
+
if (routeName) {
|
|
242
|
+
traceUFOTransition(routeName);
|
|
243
|
+
}
|
|
244
|
+
}, [routerState]);
|
|
196
245
|
|
|
197
|
-
|
|
246
|
+
return <div>Page content...</div>;
|
|
198
247
|
}
|
|
199
248
|
```
|
|
200
249
|
|
|
201
250
|
### traceUFOInteraction
|
|
202
251
|
|
|
203
|
-
Measures performance of user interactions based on browser events.
|
|
204
|
-
|
|
252
|
+
Measures performance of user interactions based on browser events. Automatically detects the
|
|
253
|
+
interaction type (press or hover) from the event and applies appropriate timing.
|
|
205
254
|
|
|
206
255
|
**Import:**
|
|
207
256
|
|
|
@@ -209,10 +258,10 @@ detects the interaction type from the event and applies appropriate timing.
|
|
|
209
258
|
import traceUFOInteraction from '@atlaskit/react-ufo/trace-interaction';
|
|
210
259
|
```
|
|
211
260
|
|
|
212
|
-
**
|
|
261
|
+
**Signature:**
|
|
213
262
|
|
|
214
263
|
```typescript
|
|
215
|
-
traceUFOInteraction(
|
|
264
|
+
function traceUFOInteraction(name: string, event: Event | UIEvent): void;
|
|
216
265
|
```
|
|
217
266
|
|
|
218
267
|
**Parameters:**
|
|
@@ -222,11 +271,13 @@ traceUFOInteraction('dropdown-open', event);
|
|
|
222
271
|
|
|
223
272
|
**Supported Event Types:**
|
|
224
273
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
274
|
+
| Event Type | Maps to Interaction Type |
|
|
275
|
+
| ------------ | ------------------------ |
|
|
276
|
+
| `click` | `press` |
|
|
277
|
+
| `dblclick` | `press` |
|
|
278
|
+
| `mousedown` | `press` |
|
|
279
|
+
| `mouseenter` | `hover` |
|
|
280
|
+
| `mouseover` | `hover` |
|
|
230
281
|
|
|
231
282
|
**Example:**
|
|
232
283
|
|
|
@@ -234,39 +285,78 @@ traceUFOInteraction('dropdown-open', event);
|
|
|
234
285
|
import traceUFOInteraction from '@atlaskit/react-ufo/trace-interaction';
|
|
235
286
|
|
|
236
287
|
function InteractiveComponent() {
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
setShowModal(true);
|
|
242
|
-
};
|
|
288
|
+
const handleClick = (event: React.MouseEvent) => {
|
|
289
|
+
traceUFOInteraction('component-action', event.nativeEvent);
|
|
290
|
+
setShowModal(true);
|
|
291
|
+
};
|
|
243
292
|
|
|
244
|
-
|
|
293
|
+
return <div onClick={handleClick}>Click me</div>;
|
|
245
294
|
}
|
|
246
295
|
```
|
|
247
296
|
|
|
248
297
|
**Notes:**
|
|
249
298
|
|
|
250
299
|
- Only trusted events (user-initiated) are processed
|
|
251
|
-
- Unsupported event types are ignored
|
|
300
|
+
- Unsupported event types are silently ignored
|
|
252
301
|
- Event timestamp is automatically extracted from the event object
|
|
253
302
|
|
|
303
|
+
### traceUFOHover
|
|
304
|
+
|
|
305
|
+
Measures performance of hover interactions. Use this for tracking hover-triggered loading states
|
|
306
|
+
such as popups, tooltips, or preview cards.
|
|
307
|
+
|
|
308
|
+
**Import:**
|
|
309
|
+
|
|
310
|
+
```typescript
|
|
311
|
+
import traceUFOHover from '@atlaskit/react-ufo/trace-hover';
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
**Signature:**
|
|
315
|
+
|
|
316
|
+
```typescript
|
|
317
|
+
function traceUFOHover(name: string, timestamp?: number): void;
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
**Parameters:**
|
|
321
|
+
|
|
322
|
+
- `name` (string): Unique identifier for the hover interaction
|
|
323
|
+
- `timestamp` (number, optional): Custom timestamp. Defaults to `performance.now()`
|
|
324
|
+
|
|
325
|
+
**Example:**
|
|
326
|
+
|
|
327
|
+
```typescript
|
|
328
|
+
import traceUFOHover from '@atlaskit/react-ufo/trace-hover';
|
|
329
|
+
|
|
330
|
+
function HoverCard() {
|
|
331
|
+
const handleMouseEnter = () => {
|
|
332
|
+
traceUFOHover('user-profile-hover');
|
|
333
|
+
loadUserProfile();
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
return <div onMouseEnter={handleMouseEnter}>Hover for details</div>;
|
|
337
|
+
}
|
|
338
|
+
```
|
|
339
|
+
|
|
254
340
|
### traceUFORedirect
|
|
255
341
|
|
|
256
|
-
|
|
257
|
-
|
|
342
|
+
Tracks navigation redirects between routes, providing detailed route transition information. This is
|
|
343
|
+
typically used with routing libraries like `@atlassian/react-resource-router`.
|
|
258
344
|
|
|
259
345
|
**Import:**
|
|
260
346
|
|
|
261
347
|
```typescript
|
|
262
348
|
import traceUFORedirect from '@atlaskit/react-ufo/trace-redirect';
|
|
263
|
-
import getUFORouteName from '@atlaskit/react-ufo/route-name';
|
|
264
349
|
```
|
|
265
350
|
|
|
266
|
-
**
|
|
351
|
+
**Signature:**
|
|
267
352
|
|
|
268
353
|
```typescript
|
|
269
|
-
traceUFORedirect(
|
|
354
|
+
function traceUFORedirect(
|
|
355
|
+
fromUfoName: string,
|
|
356
|
+
nextUfoName: string,
|
|
357
|
+
nextRouteName: string,
|
|
358
|
+
time: number,
|
|
359
|
+
): void;
|
|
270
360
|
```
|
|
271
361
|
|
|
272
362
|
**Parameters:**
|
|
@@ -276,7 +366,7 @@ traceUFORedirect(fromUfoName, nextUfoName, nextRouteName, timestamp);
|
|
|
276
366
|
- `nextRouteName` (string): Target route name from route definition
|
|
277
367
|
- `time` (number): High-precision timestamp from `performance.now()`
|
|
278
368
|
|
|
279
|
-
**Example
|
|
369
|
+
**Example:**
|
|
280
370
|
|
|
281
371
|
```typescript
|
|
282
372
|
import { matchRoute, useRouter } from '@atlassian/react-resource-router';
|
|
@@ -288,7 +378,6 @@ function NavigationTracker() {
|
|
|
288
378
|
|
|
289
379
|
const handleNavigation = (nextLocation) => {
|
|
290
380
|
const currentRouteName = getUFORouteName(routerState.route);
|
|
291
|
-
|
|
292
381
|
const nextMatchObject = matchRoute(routes, nextLocation.pathname, nextLocation.search);
|
|
293
382
|
const { route: nextRoute } = nextMatchObject;
|
|
294
383
|
const nextRouteUfoName = getUFORouteName(nextRoute);
|
|
@@ -296,16 +385,10 @@ function NavigationTracker() {
|
|
|
296
385
|
traceUFORedirect(currentRouteName, nextRouteUfoName, nextRoute.name, performance.now());
|
|
297
386
|
};
|
|
298
387
|
|
|
299
|
-
return null;
|
|
388
|
+
return null;
|
|
300
389
|
}
|
|
301
390
|
```
|
|
302
391
|
|
|
303
|
-
**Use Cases:**
|
|
304
|
-
|
|
305
|
-
- Single-page application route changes
|
|
306
|
-
- Cross-product navigation
|
|
307
|
-
- Complex navigation patterns requiring detailed tracking
|
|
308
|
-
|
|
309
392
|
---
|
|
310
393
|
|
|
311
394
|
## React Components
|
|
@@ -321,41 +404,37 @@ segment generates its own performance payload, allowing granular analysis of dif
|
|
|
321
404
|
import UFOSegment from '@atlaskit/react-ufo/segment';
|
|
322
405
|
```
|
|
323
406
|
|
|
324
|
-
**
|
|
325
|
-
|
|
326
|
-
```typescript
|
|
327
|
-
<UFOSegment name="sidebar">
|
|
328
|
-
<SidebarComponent />
|
|
329
|
-
</UFOSegment>
|
|
330
|
-
```
|
|
331
|
-
|
|
332
|
-
**Parameters:**
|
|
407
|
+
**Props:**
|
|
333
408
|
|
|
334
|
-
|
|
335
|
-
|
|
409
|
+
| Prop | Type | Required | Description |
|
|
410
|
+
| ---------- | -------------------------------- | -------- | --------------------------------------------------- |
|
|
411
|
+
| `name` | `string` | Yes | Unique identifier for this page segment |
|
|
412
|
+
| `children` | `ReactNode` | Yes | Components to be measured within this segment |
|
|
413
|
+
| `mode` | `'list' \| 'single'` | No | Rendering mode for the segment |
|
|
414
|
+
| `type` | `'first-party' \| 'third-party'` | No | Segment ownership type. Defaults to `'first-party'` |
|
|
336
415
|
|
|
337
|
-
**
|
|
416
|
+
**Example:**
|
|
338
417
|
|
|
339
418
|
```typescript
|
|
340
419
|
import UFOSegment from '@atlaskit/react-ufo/segment';
|
|
341
420
|
|
|
342
421
|
function IssuePage() {
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
422
|
+
return (
|
|
423
|
+
<div>
|
|
424
|
+
<UFOSegment name="issue-header">
|
|
425
|
+
<IssueHeader />
|
|
426
|
+
</UFOSegment>
|
|
427
|
+
|
|
428
|
+
<UFOSegment name="issue-content">
|
|
429
|
+
<IssueDescription />
|
|
430
|
+
<IssueComments />
|
|
431
|
+
</UFOSegment>
|
|
432
|
+
|
|
433
|
+
<UFOSegment name="issue-sidebar">
|
|
434
|
+
<IssueSidebar />
|
|
435
|
+
</UFOSegment>
|
|
436
|
+
</div>
|
|
437
|
+
);
|
|
359
438
|
}
|
|
360
439
|
```
|
|
361
440
|
|
|
@@ -371,26 +450,47 @@ function IssuePage() {
|
|
|
371
450
|
|
|
372
451
|
- Use distinct, meaningful names for each segment
|
|
373
452
|
- Represent logical page sections that could theoretically render independently
|
|
374
|
-
- Avoid generic names like "content" or "main"
|
|
453
|
+
- Avoid generic names like "content" or "main" — be specific
|
|
375
454
|
- Segments with identical names are aggregated in analytics
|
|
376
455
|
- Nesting is supported but use judiciously
|
|
377
456
|
|
|
378
|
-
|
|
457
|
+
### UFOThirdPartySegment
|
|
458
|
+
|
|
459
|
+
A specialized segment for tracking external or plugin content separately from first-party code.
|
|
460
|
+
Wraps `UFOSegment` with `type="third-party"`.
|
|
461
|
+
|
|
462
|
+
**Import:**
|
|
463
|
+
|
|
464
|
+
```typescript
|
|
465
|
+
import { UFOThirdPartySegment } from '@atlaskit/react-ufo/segment';
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
**Props:**
|
|
469
|
+
|
|
470
|
+
Same as `UFOSegment` except `type` is automatically set to `'third-party'`.
|
|
471
|
+
|
|
472
|
+
**Example:**
|
|
379
473
|
|
|
380
474
|
```typescript
|
|
381
475
|
import { UFOThirdPartySegment } from '@atlaskit/react-ufo/segment';
|
|
382
476
|
|
|
383
|
-
// For external/plugin content that should be tracked separately
|
|
384
477
|
<UFOThirdPartySegment name="confluence-macro-calendar">
|
|
385
|
-
|
|
386
|
-
</UFOThirdPartySegment
|
|
478
|
+
<CalendarMacro />
|
|
479
|
+
</UFOThirdPartySegment>
|
|
387
480
|
```
|
|
388
481
|
|
|
482
|
+
**Use Cases:**
|
|
483
|
+
|
|
484
|
+
- External service integrations
|
|
485
|
+
- Third-party plugins or widgets
|
|
486
|
+
- Partner-provided components
|
|
487
|
+
- Any content not directly controlled by your team
|
|
488
|
+
|
|
389
489
|
### UFOLabel
|
|
390
490
|
|
|
391
491
|
A React context provider used to annotate sections of your component tree with descriptive names.
|
|
392
492
|
Unlike segments, labels don't generate separate events but provide context for debugging and
|
|
393
|
-
analysis.
|
|
493
|
+
analysis in VC revision data.
|
|
394
494
|
|
|
395
495
|
**Import:**
|
|
396
496
|
|
|
@@ -398,13 +498,12 @@ analysis.
|
|
|
398
498
|
import UFOLabel from '@atlaskit/react-ufo/label';
|
|
399
499
|
```
|
|
400
500
|
|
|
401
|
-
**
|
|
501
|
+
**Props:**
|
|
402
502
|
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
```
|
|
503
|
+
| Prop | Type | Required | Description |
|
|
504
|
+
| ---------- | ----------- | -------- | -------------------------------------- |
|
|
505
|
+
| `name` | `string` | Yes | Descriptive name for this section |
|
|
506
|
+
| `children` | `ReactNode` | Yes | Components within this labeled section |
|
|
408
507
|
|
|
409
508
|
**Example:**
|
|
410
509
|
|
|
@@ -412,32 +511,33 @@ import UFOLabel from '@atlaskit/react-ufo/label';
|
|
|
412
511
|
import UFOLabel from '@atlaskit/react-ufo/label';
|
|
413
512
|
|
|
414
513
|
function CommentThread() {
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
514
|
+
return (
|
|
515
|
+
<UFOLabel name="comment_thread">
|
|
516
|
+
<div>
|
|
517
|
+
<UFOLabel name="comment_author">
|
|
518
|
+
<UserAvatar user={author} />
|
|
519
|
+
</UFOLabel>
|
|
520
|
+
<UFOLabel name="comment_content">
|
|
521
|
+
<CommentBody content={comment.body} />
|
|
522
|
+
</UFOLabel>
|
|
523
|
+
</div>
|
|
524
|
+
</UFOLabel>
|
|
525
|
+
);
|
|
427
526
|
}
|
|
428
527
|
```
|
|
429
528
|
|
|
430
529
|
**Guidelines:**
|
|
431
530
|
|
|
432
|
-
- Use static strings only
|
|
531
|
+
- Use static strings only — no dynamic values
|
|
433
532
|
- Focus on being distinct rather than semantically perfect
|
|
434
|
-
- No need to label every component
|
|
533
|
+
- No need to label every component — use judiciously
|
|
435
534
|
- Helps with debugging performance issues in specific UI areas
|
|
436
535
|
|
|
437
536
|
### UFOLoadHold
|
|
438
537
|
|
|
439
|
-
Identifies loading elements and prevents interaction completion until they are no longer
|
|
440
|
-
|
|
538
|
+
Identifies loading elements and prevents interaction completion until they are no longer active.
|
|
539
|
+
This component is crucial for accurate TTAI measurements — an interaction's TTAI is determined by
|
|
540
|
+
the point at which the last hold is released.
|
|
441
541
|
|
|
442
542
|
**Import:**
|
|
443
543
|
|
|
@@ -445,17 +545,26 @@ the user. This component is crucial for accurate TTI (Time to Interactive) measu
|
|
|
445
545
|
import UFOLoadHold from '@atlaskit/react-ufo/load-hold';
|
|
446
546
|
```
|
|
447
547
|
|
|
548
|
+
**Props:**
|
|
549
|
+
|
|
550
|
+
| Prop | Type | Required | Default | Description |
|
|
551
|
+
| -------------- | ----------- | -------- | ----------- | ------------------------------------------------------------------------------- |
|
|
552
|
+
| `name` | `string` | Yes | — | Unique identifier for this loading state |
|
|
553
|
+
| `hold` | `boolean` | No | `undefined` | Controls whether the hold is active. When omitted, hold is active while mounted |
|
|
554
|
+
| `children` | `ReactNode` | No | — | Components wrapped by the hold |
|
|
555
|
+
| `experimental` | `boolean` | No | `undefined` | Enables experimental hold behavior |
|
|
556
|
+
|
|
448
557
|
**Usage Patterns:**
|
|
449
558
|
|
|
450
|
-
**1. Wrapping loading elements:**
|
|
559
|
+
**1. Wrapping loading elements (hold while mounted):**
|
|
451
560
|
|
|
452
561
|
```typescript
|
|
453
562
|
if (isLoading) {
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
563
|
+
return (
|
|
564
|
+
<UFOLoadHold name="card-loading">
|
|
565
|
+
<Skeleton />
|
|
566
|
+
</UFOLoadHold>
|
|
567
|
+
);
|
|
459
568
|
}
|
|
460
569
|
```
|
|
461
570
|
|
|
@@ -463,18 +572,18 @@ if (isLoading) {
|
|
|
463
572
|
|
|
464
573
|
```typescript
|
|
465
574
|
return (
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
575
|
+
<>
|
|
576
|
+
<Skeleton />
|
|
577
|
+
<UFOLoadHold name="card-loading" />
|
|
578
|
+
</>
|
|
470
579
|
);
|
|
471
580
|
```
|
|
472
581
|
|
|
473
|
-
**3. Conditional
|
|
582
|
+
**3. Conditional hold via prop:**
|
|
474
583
|
|
|
475
584
|
```typescript
|
|
476
585
|
<UFOLoadHold name="card" hold={isLoading}>
|
|
477
|
-
|
|
586
|
+
<Card />
|
|
478
587
|
</UFOLoadHold>
|
|
479
588
|
```
|
|
480
589
|
|
|
@@ -482,58 +591,69 @@ return (
|
|
|
482
591
|
|
|
483
592
|
```typescript
|
|
484
593
|
return (
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
594
|
+
<>
|
|
595
|
+
<Card />
|
|
596
|
+
<UFOLoadHold name="card" hold={isLoading} />
|
|
597
|
+
</>
|
|
489
598
|
);
|
|
490
599
|
```
|
|
491
600
|
|
|
492
|
-
**
|
|
601
|
+
**Notes:**
|
|
602
|
+
|
|
603
|
+
- Only instrument loading elements once if they're reused
|
|
604
|
+
- Multiple holds with the same name are fine
|
|
605
|
+
- Holds automatically release when the component unmounts
|
|
606
|
+
- Holds that are added after the interaction is already complete (after TTAI) are called "late
|
|
607
|
+
holds" and are tracked separately
|
|
493
608
|
|
|
494
|
-
|
|
495
|
-
- `hold` (boolean, optional): Controls whether the hold is active
|
|
496
|
-
- `children` (ReactNode, optional): Components wrapped by the hold
|
|
609
|
+
### UFOInteractionIgnore
|
|
497
610
|
|
|
498
|
-
|
|
611
|
+
Prevents a subtree from holding up an interaction. Use this when a component loads late but isn't
|
|
612
|
+
considered a breach of your SLO.
|
|
613
|
+
|
|
614
|
+
**Import:**
|
|
499
615
|
|
|
500
616
|
```typescript
|
|
501
|
-
import
|
|
617
|
+
import UFOInteractionIgnore from '@atlaskit/react-ufo/interaction-ignore';
|
|
618
|
+
```
|
|
502
619
|
|
|
503
|
-
|
|
504
|
-
function IssueCard({ issue }) {
|
|
505
|
-
if (!issue) {
|
|
506
|
-
return (
|
|
507
|
-
<UFOLoadHold name="issue-card-loading">
|
|
508
|
-
<IssueCardSkeleton />
|
|
509
|
-
</UFOLoadHold>
|
|
510
|
-
);
|
|
511
|
-
}
|
|
620
|
+
**Props:**
|
|
512
621
|
|
|
513
|
-
|
|
514
|
-
|
|
622
|
+
| Prop | Type | Required | Default | Description |
|
|
623
|
+
| ---------- | ----------------------- | -------- | ------- | --------------------------------------- |
|
|
624
|
+
| `children` | `ReactNode` | No | — | Components within the ignored subtree |
|
|
625
|
+
| `ignore` | `boolean` | No | `true` | Whether to ignore holds in this subtree |
|
|
626
|
+
| `reason` | `'third-party-element'` | No | — | Reason for ignoring holds |
|
|
515
627
|
|
|
516
|
-
|
|
517
|
-
function CommentsSection({ loading, comments }) {
|
|
518
|
-
return (
|
|
519
|
-
<div>
|
|
520
|
-
<UFOLoadHold name="comments-loading" hold={loading} />
|
|
521
|
-
{loading ? <CommentsSkeleton /> : <CommentsList comments={comments} />}
|
|
522
|
-
</div>
|
|
523
|
-
);
|
|
524
|
-
}
|
|
525
|
-
```
|
|
628
|
+
**Example:**
|
|
526
629
|
|
|
527
|
-
|
|
630
|
+
```typescript
|
|
631
|
+
import UFOInteractionIgnore from '@atlaskit/react-ufo/interaction-ignore';
|
|
528
632
|
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
633
|
+
function PageLayout() {
|
|
634
|
+
return (
|
|
635
|
+
<App>
|
|
636
|
+
<Main />
|
|
637
|
+
<Sidebar>
|
|
638
|
+
<UFOInteractionIgnore>
|
|
639
|
+
<InsightsButton />
|
|
640
|
+
</UFOInteractionIgnore>
|
|
641
|
+
</Sidebar>
|
|
642
|
+
</App>
|
|
643
|
+
);
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
// Conditional usage
|
|
647
|
+
<UFOInteractionIgnore ignore={!isCriticalContent}>
|
|
648
|
+
<OptionalWidget />
|
|
649
|
+
</UFOInteractionIgnore>
|
|
650
|
+
```
|
|
532
651
|
|
|
533
652
|
### Suspense
|
|
534
653
|
|
|
535
|
-
An enhanced Suspense boundary that
|
|
536
|
-
|
|
654
|
+
An enhanced React Suspense boundary that integrates with UFO instrumentation. Wraps React's
|
|
655
|
+
`Suspense` component and adds a `UFOLoadHold` around the fallback, so the interaction is held until
|
|
656
|
+
the suspended component resolves.
|
|
537
657
|
|
|
538
658
|
**Import:**
|
|
539
659
|
|
|
@@ -541,19 +661,13 @@ for React's `Suspense` component.
|
|
|
541
661
|
import Suspense from '@atlaskit/react-ufo/suspense';
|
|
542
662
|
```
|
|
543
663
|
|
|
544
|
-
**
|
|
545
|
-
|
|
546
|
-
```typescript
|
|
547
|
-
<Suspense name="lazy-component" fallback={<Skeleton />}>
|
|
548
|
-
<LazyComponent />
|
|
549
|
-
</Suspense>
|
|
550
|
-
```
|
|
551
|
-
|
|
552
|
-
**Parameters:**
|
|
664
|
+
**Props:**
|
|
553
665
|
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
666
|
+
| Prop | Type | Required | Description |
|
|
667
|
+
| ----------------- | ----------- | -------- | -------------------------------------------------------------------- |
|
|
668
|
+
| `interactionName` | `string` | Yes | Unique identifier for this suspense boundary (used as the hold name) |
|
|
669
|
+
| `fallback` | `ReactNode` | Yes | Component to show while suspended |
|
|
670
|
+
| `children` | `ReactNode` | Yes | Components that might suspend |
|
|
557
671
|
|
|
558
672
|
**Example:**
|
|
559
673
|
|
|
@@ -563,18 +677,18 @@ import Suspense from '@atlaskit/react-ufo/suspense';
|
|
|
563
677
|
const LazyIssueView = lazy(() => import('./IssueView'));
|
|
564
678
|
|
|
565
679
|
function App() {
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
680
|
+
return (
|
|
681
|
+
<Suspense interactionName="issue-view-lazy" fallback={<IssueViewSkeleton />}>
|
|
682
|
+
<LazyIssueView />
|
|
683
|
+
</Suspense>
|
|
684
|
+
);
|
|
571
685
|
}
|
|
572
686
|
```
|
|
573
687
|
|
|
574
688
|
### UFOCustomData
|
|
575
689
|
|
|
576
690
|
Adds custom metadata to the current UFO interaction. This data appears in analytics events with a
|
|
577
|
-
|
|
691
|
+
`custom:` prefix.
|
|
578
692
|
|
|
579
693
|
**Import:**
|
|
580
694
|
|
|
@@ -582,11 +696,11 @@ Adds custom metadata to the current UFO interaction. This data appears in analyt
|
|
|
582
696
|
import UFOCustomData from '@atlaskit/react-ufo/custom-data';
|
|
583
697
|
```
|
|
584
698
|
|
|
585
|
-
**
|
|
699
|
+
**Props:**
|
|
586
700
|
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
701
|
+
| Prop | Type | Required | Description |
|
|
702
|
+
| ------ | ------------------------- | -------- | ------------------------------ |
|
|
703
|
+
| `data` | `Record<string, unknown>` | Yes | Key-value pairs of custom data |
|
|
590
704
|
|
|
591
705
|
**Example:**
|
|
592
706
|
|
|
@@ -594,18 +708,12 @@ import UFOCustomData from '@atlaskit/react-ufo/custom-data';
|
|
|
594
708
|
import UFOCustomData from '@atlaskit/react-ufo/custom-data';
|
|
595
709
|
|
|
596
710
|
function IssueList({ issues, viewType }) {
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
return (
|
|
604
|
-
<div>
|
|
605
|
-
<UFOCustomData data={customData} />
|
|
606
|
-
<IssueTable issues={issues} />
|
|
607
|
-
</div>
|
|
608
|
-
);
|
|
711
|
+
return (
|
|
712
|
+
<div>
|
|
713
|
+
<UFOCustomData data={{ viewType, issueCount: issues.length }} />
|
|
714
|
+
<IssueTable issues={issues} />
|
|
715
|
+
</div>
|
|
716
|
+
);
|
|
609
717
|
}
|
|
610
718
|
```
|
|
611
719
|
|
|
@@ -614,16 +722,13 @@ function IssueList({ issues, viewType }) {
|
|
|
614
722
|
```javascript
|
|
615
723
|
{
|
|
616
724
|
'custom:viewType': 'list',
|
|
617
|
-
'custom:issueCount': 42
|
|
618
|
-
'custom:hasFilters': true
|
|
725
|
+
'custom:issueCount': 42
|
|
619
726
|
}
|
|
620
727
|
```
|
|
621
728
|
|
|
622
|
-
### UFOCustomCohortData
|
|
729
|
+
### UFOCustomCohortData
|
|
623
730
|
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
Adds cohort-specific custom data to UFO interactions. This is used for A/B testing and feature flag
|
|
731
|
+
Adds cohort-specific custom data to UFO interactions. Used for A/B testing and feature flag
|
|
627
732
|
correlation.
|
|
628
733
|
|
|
629
734
|
**Import:**
|
|
@@ -632,27 +737,103 @@ correlation.
|
|
|
632
737
|
import UFOCustomCohortData from '@atlaskit/react-ufo/custom-cohort-data';
|
|
633
738
|
```
|
|
634
739
|
|
|
635
|
-
**
|
|
740
|
+
**Props:**
|
|
636
741
|
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
742
|
+
| Prop | Type | Required | Description |
|
|
743
|
+
| --------- | -------------------------------------------------- | -------- | ----------------- |
|
|
744
|
+
| `dataKey` | `string` | Yes | Cohort data key |
|
|
745
|
+
| `value` | `string \| number \| boolean \| null \| undefined` | Yes | Cohort data value |
|
|
640
746
|
|
|
641
747
|
**Example:**
|
|
642
748
|
|
|
643
749
|
```typescript
|
|
644
750
|
import UFOCustomCohortData from '@atlaskit/react-ufo/custom-cohort-data';
|
|
645
|
-
import { fg } from '@
|
|
751
|
+
import { fg } from '@atlaskit/platform-feature-flags';
|
|
646
752
|
|
|
647
753
|
function ExperimentalFeature() {
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
754
|
+
const variant = fg('new_issue_view') ? 'treatment' : 'control';
|
|
755
|
+
|
|
756
|
+
return (
|
|
757
|
+
<div>
|
|
758
|
+
<UFOCustomCohortData dataKey="new_issue_view_variant" value={variant} />
|
|
759
|
+
{variant === 'treatment' ? <NewIssueView /> : <OldIssueView />}
|
|
760
|
+
</div>
|
|
761
|
+
);
|
|
762
|
+
}
|
|
763
|
+
```
|
|
764
|
+
|
|
765
|
+
### UFOCustomMark
|
|
766
|
+
|
|
767
|
+
Records a custom performance mark at a specific point in time during the current interaction.
|
|
768
|
+
|
|
769
|
+
**Import:**
|
|
770
|
+
|
|
771
|
+
```typescript
|
|
772
|
+
import UFOCustomMark from '@atlaskit/react-ufo/custom-mark';
|
|
773
|
+
```
|
|
774
|
+
|
|
775
|
+
**Props:**
|
|
776
|
+
|
|
777
|
+
| Prop | Type | Required | Description |
|
|
778
|
+
| ----------- | -------- | -------- | ------------------------------------------------- |
|
|
779
|
+
| `name` | `string` | Yes | Name of the mark |
|
|
780
|
+
| `timestamp` | `number` | No | Custom timestamp. Defaults to `performance.now()` |
|
|
781
|
+
|
|
782
|
+
**Example:**
|
|
783
|
+
|
|
784
|
+
```typescript
|
|
785
|
+
import UFOCustomMark from '@atlaskit/react-ufo/custom-mark';
|
|
786
|
+
|
|
787
|
+
function DataTable({ data }) {
|
|
788
|
+
return (
|
|
789
|
+
<div>
|
|
790
|
+
<UFOCustomMark name="data-table-render-start" />
|
|
791
|
+
<Table data={data} />
|
|
792
|
+
</div>
|
|
793
|
+
);
|
|
794
|
+
}
|
|
795
|
+
```
|
|
796
|
+
|
|
797
|
+
**Also available:** `UFOCustomMarks` component for setting multiple marks at once:
|
|
798
|
+
|
|
799
|
+
```typescript
|
|
800
|
+
import { UFOCustomMarks } from '@atlaskit/react-ufo/custom-mark';
|
|
801
|
+
|
|
802
|
+
<UFOCustomMarks data={{ 'mark-a': timestamp1, 'mark-b': timestamp2 }} />
|
|
803
|
+
```
|
|
804
|
+
|
|
805
|
+
### Placeholder
|
|
806
|
+
|
|
807
|
+
A UFO-instrumented wrapper around lazy-loaded components using Suspense. Provides hold tracking for
|
|
808
|
+
lazy-loaded content.
|
|
809
|
+
|
|
810
|
+
**Import:**
|
|
811
|
+
|
|
812
|
+
```typescript
|
|
813
|
+
import Placeholder from '@atlaskit/react-ufo/placeholder';
|
|
814
|
+
```
|
|
815
|
+
|
|
816
|
+
**Props:**
|
|
817
|
+
|
|
818
|
+
| Prop | Type | Required | Description |
|
|
819
|
+
| ---------- | ----------- | -------- | -------------------------------------- |
|
|
820
|
+
| `name` | `string` | Yes | Unique identifier for this placeholder |
|
|
821
|
+
| `children` | `ReactNode` | No | Lazy-loaded components |
|
|
822
|
+
| `fallback` | `ReactNode` | No | Component to show while loading |
|
|
823
|
+
|
|
824
|
+
**Example:**
|
|
825
|
+
|
|
826
|
+
```typescript
|
|
827
|
+
import Placeholder from '@atlaskit/react-ufo/placeholder';
|
|
828
|
+
|
|
829
|
+
const LazyPanel = lazy(() => import('./Panel'));
|
|
830
|
+
|
|
831
|
+
function App() {
|
|
832
|
+
return (
|
|
833
|
+
<Placeholder name="panel-placeholder" fallback={<PanelSkeleton />}>
|
|
834
|
+
<LazyPanel />
|
|
835
|
+
</Placeholder>
|
|
836
|
+
);
|
|
656
837
|
}
|
|
657
838
|
```
|
|
658
839
|
|
|
@@ -671,12 +852,20 @@ that need interaction tracking.
|
|
|
671
852
|
import usePressTracing from '@atlaskit/react-ufo/use-press-tracing';
|
|
672
853
|
```
|
|
673
854
|
|
|
674
|
-
**
|
|
855
|
+
**Signature:**
|
|
675
856
|
|
|
676
857
|
```typescript
|
|
677
|
-
|
|
858
|
+
function usePressTracing(name: string): (timeStamp?: number) => void;
|
|
678
859
|
```
|
|
679
860
|
|
|
861
|
+
**Parameters:**
|
|
862
|
+
|
|
863
|
+
- `name` (string): The UFO interaction name
|
|
864
|
+
|
|
865
|
+
**Returns:**
|
|
866
|
+
|
|
867
|
+
- A function to call when the interaction starts. Accepts an optional `timeStamp` parameter.
|
|
868
|
+
|
|
680
869
|
**Example:**
|
|
681
870
|
|
|
682
871
|
```typescript
|
|
@@ -684,34 +873,23 @@ import { useCallback } from 'react';
|
|
|
684
873
|
import usePressTracing from '@atlaskit/react-ufo/use-press-tracing';
|
|
685
874
|
|
|
686
875
|
function CustomButton({ onAction }) {
|
|
687
|
-
|
|
876
|
+
const traceInteraction = usePressTracing('custom-action');
|
|
688
877
|
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
878
|
+
const handleClick = useCallback(
|
|
879
|
+
(event) => {
|
|
880
|
+
traceInteraction(event.timeStamp);
|
|
881
|
+
onAction();
|
|
882
|
+
},
|
|
883
|
+
[traceInteraction, onAction],
|
|
884
|
+
);
|
|
696
885
|
|
|
697
|
-
|
|
886
|
+
return <button onClick={handleClick}>Custom Action</button>;
|
|
698
887
|
}
|
|
699
888
|
```
|
|
700
889
|
|
|
701
|
-
**Parameters:**
|
|
702
|
-
|
|
703
|
-
- `name` (string): The UFO interaction name
|
|
704
|
-
|
|
705
|
-
**Returns:**
|
|
706
|
-
|
|
707
|
-
- `traceInteraction` (function): Function to call when interaction starts
|
|
708
|
-
- `timeStamp` (number, optional): Custom timestamp
|
|
709
|
-
|
|
710
890
|
**Note:** Results are sent as `inline-result` type events.
|
|
711
891
|
|
|
712
|
-
### useUFOTypingPerformanceTracing
|
|
713
|
-
|
|
714
|
-
**Note: This is an experimental feature that may change or be removed in future versions.**
|
|
892
|
+
### useUFOTypingPerformanceTracing
|
|
715
893
|
|
|
716
894
|
Measures typing latency in input fields to track user experience during text input.
|
|
717
895
|
|
|
@@ -721,21 +899,29 @@ Measures typing latency in input fields to track user experience during text inp
|
|
|
721
899
|
import useUFOTypingPerformanceTracing from '@atlaskit/react-ufo/typing-performance-tracing';
|
|
722
900
|
```
|
|
723
901
|
|
|
724
|
-
**
|
|
902
|
+
**Signature:**
|
|
725
903
|
|
|
726
904
|
```typescript
|
|
727
|
-
|
|
905
|
+
function useUFOTypingPerformanceTracing<T extends HTMLElement>(name: string): React.RefObject<T>;
|
|
728
906
|
```
|
|
729
907
|
|
|
908
|
+
**Parameters:**
|
|
909
|
+
|
|
910
|
+
- `name` (string): Identifier for the typing performance trace
|
|
911
|
+
|
|
912
|
+
**Returns:**
|
|
913
|
+
|
|
914
|
+
- A React ref to attach to the input element
|
|
915
|
+
|
|
730
916
|
**Example:**
|
|
731
917
|
|
|
732
918
|
```typescript
|
|
733
919
|
import useUFOTypingPerformanceTracing from '@atlaskit/react-ufo/typing-performance-tracing';
|
|
734
920
|
|
|
735
921
|
function CommentEditor() {
|
|
736
|
-
|
|
922
|
+
const typingRef = useUFOTypingPerformanceTracing<HTMLTextAreaElement>('comment-editor-typing');
|
|
737
923
|
|
|
738
|
-
|
|
924
|
+
return <textarea ref={typingRef} placeholder="Write a comment..." />;
|
|
739
925
|
}
|
|
740
926
|
```
|
|
741
927
|
|
|
@@ -756,7 +942,7 @@ function CommentEditor() {
|
|
|
756
942
|
### useUFOTransitionCompleter
|
|
757
943
|
|
|
758
944
|
Automatically completes UFO transitions when the component mounts. Use this in destination route
|
|
759
|
-
components.
|
|
945
|
+
components to signal that the transition is complete.
|
|
760
946
|
|
|
761
947
|
**Import:**
|
|
762
948
|
|
|
@@ -764,38 +950,27 @@ components.
|
|
|
764
950
|
import { useUFOTransitionCompleter } from '@atlaskit/react-ufo/trace-transition';
|
|
765
951
|
```
|
|
766
952
|
|
|
767
|
-
**Usage:**
|
|
768
|
-
|
|
769
|
-
```typescript
|
|
770
|
-
function MyPageComponent() {
|
|
771
|
-
useUFOTransitionCompleter();
|
|
772
|
-
|
|
773
|
-
return <div>Page content</div>;
|
|
774
|
-
}
|
|
775
|
-
```
|
|
776
|
-
|
|
777
953
|
**Example:**
|
|
778
954
|
|
|
779
955
|
```typescript
|
|
780
956
|
import { useUFOTransitionCompleter } from '@atlaskit/react-ufo/trace-transition';
|
|
781
957
|
|
|
782
958
|
function IssueViewPage() {
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
);
|
|
959
|
+
useUFOTransitionCompleter();
|
|
960
|
+
|
|
961
|
+
return (
|
|
962
|
+
<div>
|
|
963
|
+
<IssueHeader />
|
|
964
|
+
<IssueContent />
|
|
965
|
+
</div>
|
|
966
|
+
);
|
|
792
967
|
}
|
|
793
968
|
```
|
|
794
969
|
|
|
795
970
|
### useUFOReportError
|
|
796
971
|
|
|
797
|
-
A hook that provides a function to report errors within UFO interactions.
|
|
798
|
-
|
|
972
|
+
A hook that provides a function to report errors within UFO interactions. Errors are automatically
|
|
973
|
+
associated with the currently active UFO interaction.
|
|
799
974
|
|
|
800
975
|
**Import:**
|
|
801
976
|
|
|
@@ -803,26 +978,15 @@ and track errors that occur during performance-critical user flows.
|
|
|
803
978
|
import { useUFOReportError } from '@atlaskit/react-ufo/report-error';
|
|
804
979
|
```
|
|
805
980
|
|
|
806
|
-
**Usage:**
|
|
807
|
-
|
|
808
|
-
```typescript
|
|
809
|
-
const reportError = useUFOReportError();
|
|
810
|
-
```
|
|
811
|
-
|
|
812
|
-
**Parameters:**
|
|
813
|
-
|
|
814
|
-
- None
|
|
815
|
-
|
|
816
981
|
**Returns:**
|
|
817
982
|
|
|
818
|
-
-
|
|
819
|
-
- `
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
- `errorStatusCode` (number, optional): HTTP status code if applicable
|
|
983
|
+
- A function that accepts an error object with:
|
|
984
|
+
- `name` (string): Error name/type
|
|
985
|
+
- `errorMessage` (string): Error description
|
|
986
|
+
- `errorStack` (string, optional): Error stack trace
|
|
987
|
+
- `forcedError` (boolean, optional): Whether this was intentionally triggered
|
|
988
|
+
- `errorHash` (string, optional): Custom error identifier
|
|
989
|
+
- `errorStatusCode` (number, optional): HTTP status code if applicable
|
|
826
990
|
|
|
827
991
|
**Example:**
|
|
828
992
|
|
|
@@ -843,7 +1007,7 @@ function DataLoader({ issueId }) {
|
|
|
843
1007
|
errorStack: error.stack,
|
|
844
1008
|
errorStatusCode: error.status,
|
|
845
1009
|
});
|
|
846
|
-
throw error;
|
|
1010
|
+
throw error;
|
|
847
1011
|
}
|
|
848
1012
|
};
|
|
849
1013
|
|
|
@@ -851,35 +1015,47 @@ function DataLoader({ issueId }) {
|
|
|
851
1015
|
}
|
|
852
1016
|
```
|
|
853
1017
|
|
|
854
|
-
|
|
1018
|
+
### useReportTerminalError
|
|
855
1019
|
|
|
856
|
-
|
|
857
|
-
|
|
1020
|
+
A hook that reports a terminal error to UFO when the error is first set. Terminal errors represent
|
|
1021
|
+
critical failures that prevent the user from completing their task — typically rendered via error
|
|
1022
|
+
boundaries.
|
|
858
1023
|
|
|
859
|
-
|
|
860
|
-
const reportError = useUFOReportError();
|
|
1024
|
+
**Import:**
|
|
861
1025
|
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
errorMessage: `Search failed: ${error.message}`,
|
|
866
|
-
errorStack: error.stack,
|
|
867
|
-
errorStatusCode: error.response?.status,
|
|
868
|
-
// Custom error hash for grouping similar errors
|
|
869
|
-
errorHash: `search-${searchContext.query}-${error.type}`,
|
|
870
|
-
});
|
|
871
|
-
};
|
|
1026
|
+
```typescript
|
|
1027
|
+
import { useReportTerminalError } from '@atlaskit/react-ufo/set-terminal-error';
|
|
1028
|
+
```
|
|
872
1029
|
|
|
873
|
-
|
|
874
|
-
|
|
1030
|
+
**Signature:**
|
|
1031
|
+
|
|
1032
|
+
```typescript
|
|
1033
|
+
function useReportTerminalError(
|
|
1034
|
+
error: Error | null | undefined,
|
|
1035
|
+
additionalAttributes?: TerminalErrorAdditionalAttributes,
|
|
1036
|
+
): void;
|
|
875
1037
|
```
|
|
876
1038
|
|
|
877
|
-
**
|
|
1039
|
+
**Parameters:**
|
|
1040
|
+
|
|
1041
|
+
- `error` (Error | null | undefined): The error to report. Only reported once when first set.
|
|
1042
|
+
- `additionalAttributes` (optional): See [setTerminalError](#setterminalerror) for details.
|
|
1043
|
+
|
|
1044
|
+
**Example:**
|
|
878
1045
|
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
1046
|
+
```typescript
|
|
1047
|
+
import { useReportTerminalError } from '@atlaskit/react-ufo/set-terminal-error';
|
|
1048
|
+
|
|
1049
|
+
function ErrorBoundaryFallback({ error }) {
|
|
1050
|
+
useReportTerminalError(error, {
|
|
1051
|
+
errorBoundaryId: 'issue-view',
|
|
1052
|
+
fallbackType: 'page',
|
|
1053
|
+
teamName: 'my-team',
|
|
1054
|
+
});
|
|
1055
|
+
|
|
1056
|
+
return <ErrorPage />;
|
|
1057
|
+
}
|
|
1058
|
+
```
|
|
883
1059
|
|
|
884
1060
|
---
|
|
885
1061
|
|
|
@@ -895,10 +1071,10 @@ Programmatically adds custom data to the current UFO interaction without using a
|
|
|
895
1071
|
import { addUFOCustomData } from '@atlaskit/react-ufo/custom-data';
|
|
896
1072
|
```
|
|
897
1073
|
|
|
898
|
-
**
|
|
1074
|
+
**Signature:**
|
|
899
1075
|
|
|
900
1076
|
```typescript
|
|
901
|
-
addUFOCustomData(
|
|
1077
|
+
function addUFOCustomData(data: Record<string, unknown>): void;
|
|
902
1078
|
```
|
|
903
1079
|
|
|
904
1080
|
**Example:**
|
|
@@ -917,9 +1093,7 @@ function performSearch(query) {
|
|
|
917
1093
|
}
|
|
918
1094
|
```
|
|
919
1095
|
|
|
920
|
-
### addUFOCustomCohortData
|
|
921
|
-
|
|
922
|
-
**Note: This is an experimental feature that may change or be removed in future versions.**
|
|
1096
|
+
### addUFOCustomCohortData
|
|
923
1097
|
|
|
924
1098
|
Programmatically adds cohort data for A/B testing and feature flag tracking.
|
|
925
1099
|
|
|
@@ -929,17 +1103,20 @@ Programmatically adds cohort data for A/B testing and feature flag tracking.
|
|
|
929
1103
|
import { addUFOCustomCohortData } from '@atlaskit/react-ufo/custom-cohort-data';
|
|
930
1104
|
```
|
|
931
1105
|
|
|
932
|
-
**
|
|
1106
|
+
**Signature:**
|
|
933
1107
|
|
|
934
1108
|
```typescript
|
|
935
|
-
addUFOCustomCohortData(
|
|
1109
|
+
function addUFOCustomCohortData(
|
|
1110
|
+
key: string,
|
|
1111
|
+
value: number | boolean | string | null | undefined,
|
|
1112
|
+
): void;
|
|
936
1113
|
```
|
|
937
1114
|
|
|
938
1115
|
**Example:**
|
|
939
1116
|
|
|
940
1117
|
```typescript
|
|
941
1118
|
import { addUFOCustomCohortData } from '@atlaskit/react-ufo/custom-cohort-data';
|
|
942
|
-
import { fg } from '@
|
|
1119
|
+
import { fg } from '@atlaskit/platform-feature-flags';
|
|
943
1120
|
|
|
944
1121
|
function initializeExperiment() {
|
|
945
1122
|
const isNewDesignEnabled = fg('new_design_system');
|
|
@@ -949,6 +1126,99 @@ function initializeExperiment() {
|
|
|
949
1126
|
}
|
|
950
1127
|
```
|
|
951
1128
|
|
|
1129
|
+
### addUFOCustomMark
|
|
1130
|
+
|
|
1131
|
+
Programmatically records a custom performance mark at a specific point in time.
|
|
1132
|
+
|
|
1133
|
+
**Import:**
|
|
1134
|
+
|
|
1135
|
+
```typescript
|
|
1136
|
+
import { addUFOCustomMark } from '@atlaskit/react-ufo/custom-mark';
|
|
1137
|
+
```
|
|
1138
|
+
|
|
1139
|
+
**Signature:**
|
|
1140
|
+
|
|
1141
|
+
```typescript
|
|
1142
|
+
function addUFOCustomMark(name: string, timestamp?: number): void;
|
|
1143
|
+
```
|
|
1144
|
+
|
|
1145
|
+
**Parameters:**
|
|
1146
|
+
|
|
1147
|
+
- `name` (string): Name of the mark
|
|
1148
|
+
- `timestamp` (number, optional): Custom timestamp. Defaults to `performance.now()`
|
|
1149
|
+
|
|
1150
|
+
**Example:**
|
|
1151
|
+
|
|
1152
|
+
```typescript
|
|
1153
|
+
import { addUFOCustomMark } from '@atlaskit/react-ufo/custom-mark';
|
|
1154
|
+
|
|
1155
|
+
function onDataFetched() {
|
|
1156
|
+
addUFOCustomMark('data-fetch-complete');
|
|
1157
|
+
}
|
|
1158
|
+
```
|
|
1159
|
+
|
|
1160
|
+
### addCustomSpans
|
|
1161
|
+
|
|
1162
|
+
Records a custom performance span (a time range with a start and end) to all active interactions.
|
|
1163
|
+
|
|
1164
|
+
**Import:**
|
|
1165
|
+
|
|
1166
|
+
```typescript
|
|
1167
|
+
import { addCustomSpans } from '@atlaskit/react-ufo/custom-spans';
|
|
1168
|
+
```
|
|
1169
|
+
|
|
1170
|
+
**Signature:**
|
|
1171
|
+
|
|
1172
|
+
```typescript
|
|
1173
|
+
function addCustomSpans(
|
|
1174
|
+
name: string,
|
|
1175
|
+
start: number,
|
|
1176
|
+
end?: number, // defaults to performance.now()
|
|
1177
|
+
size?: number, // defaults to 0
|
|
1178
|
+
): void;
|
|
1179
|
+
```
|
|
1180
|
+
|
|
1181
|
+
**Example:**
|
|
1182
|
+
|
|
1183
|
+
```typescript
|
|
1184
|
+
import { addCustomSpans } from '@atlaskit/react-ufo/custom-spans';
|
|
1185
|
+
|
|
1186
|
+
async function fetchData() {
|
|
1187
|
+
const start = performance.now();
|
|
1188
|
+
const data = await api.getData();
|
|
1189
|
+
addCustomSpans('api-fetch', start, performance.now(), data.length);
|
|
1190
|
+
return data;
|
|
1191
|
+
}
|
|
1192
|
+
```
|
|
1193
|
+
|
|
1194
|
+
### addBM3TimingsToUFO
|
|
1195
|
+
|
|
1196
|
+
Converts BM3 (Browser Metrics 3) timing marks into UFO custom timings. Useful for migrating from BM3
|
|
1197
|
+
instrumentation to UFO.
|
|
1198
|
+
|
|
1199
|
+
**Import:**
|
|
1200
|
+
|
|
1201
|
+
```typescript
|
|
1202
|
+
import { addBM3TimingsToUFO } from '@atlaskit/react-ufo/custom-timings';
|
|
1203
|
+
```
|
|
1204
|
+
|
|
1205
|
+
**Signature:**
|
|
1206
|
+
|
|
1207
|
+
```typescript
|
|
1208
|
+
function addBM3TimingsToUFO(
|
|
1209
|
+
marks?: { [key: string]: number },
|
|
1210
|
+
timingsConfig?: Array<{ key: string; startMark?: string; endMark?: string }>,
|
|
1211
|
+
): void;
|
|
1212
|
+
```
|
|
1213
|
+
|
|
1214
|
+
**Also available as a component:**
|
|
1215
|
+
|
|
1216
|
+
```typescript
|
|
1217
|
+
import { UFOBM3TimingsToUFO } from '@atlaskit/react-ufo/custom-timings';
|
|
1218
|
+
|
|
1219
|
+
<UFOBM3TimingsToUFO marks={marks} timings={timingsConfig} />
|
|
1220
|
+
```
|
|
1221
|
+
|
|
952
1222
|
### addFeatureFlagAccessed
|
|
953
1223
|
|
|
954
1224
|
Records feature flag usage for correlation with performance metrics.
|
|
@@ -959,26 +1229,20 @@ Records feature flag usage for correlation with performance metrics.
|
|
|
959
1229
|
import { addFeatureFlagAccessed } from '@atlaskit/react-ufo/feature-flags-accessed';
|
|
960
1230
|
```
|
|
961
1231
|
|
|
962
|
-
**
|
|
1232
|
+
**Signature:**
|
|
963
1233
|
|
|
964
1234
|
```typescript
|
|
965
|
-
addFeatureFlagAccessed(
|
|
1235
|
+
function addFeatureFlagAccessed(featureFlagName: string, featureFlagValue: FeatureFlagValue): void;
|
|
966
1236
|
```
|
|
967
1237
|
|
|
968
1238
|
**Example:**
|
|
969
1239
|
|
|
970
1240
|
```typescript
|
|
971
1241
|
import { addFeatureFlagAccessed } from '@atlaskit/react-ufo/feature-flags-accessed';
|
|
972
|
-
import { fg } from '@atlassian/jira-feature-gating';
|
|
973
1242
|
|
|
974
1243
|
function checkFeatureFlag(flagKey) {
|
|
975
1244
|
const flagValue = fg(flagKey);
|
|
976
|
-
|
|
977
|
-
// Record this flag access for performance correlation
|
|
978
|
-
if (shouldRecordFeatureFlagForUFO(flagKey)) {
|
|
979
|
-
addFeatureFlagAccessed(flagKey, flagValue);
|
|
980
|
-
}
|
|
981
|
-
|
|
1245
|
+
addFeatureFlagAccessed(flagKey, flagValue);
|
|
982
1246
|
return flagValue;
|
|
983
1247
|
}
|
|
984
1248
|
```
|
|
@@ -987,7 +1251,7 @@ function checkFeatureFlag(flagKey) {
|
|
|
987
1251
|
|
|
988
1252
|
### setInteractionError
|
|
989
1253
|
|
|
990
|
-
Manually records an error for
|
|
1254
|
+
Manually records an error for a specific named interaction.
|
|
991
1255
|
|
|
992
1256
|
**Import:**
|
|
993
1257
|
|
|
@@ -995,13 +1259,13 @@ Manually records an error for the current UFO interaction.
|
|
|
995
1259
|
import { setInteractionError } from '@atlaskit/react-ufo/set-interaction-error';
|
|
996
1260
|
```
|
|
997
1261
|
|
|
998
|
-
**
|
|
1262
|
+
**Signature:**
|
|
999
1263
|
|
|
1000
1264
|
```typescript
|
|
1001
|
-
setInteractionError(
|
|
1002
|
-
|
|
1003
|
-
name:
|
|
1004
|
-
|
|
1265
|
+
function setInteractionError(
|
|
1266
|
+
interactionName: string,
|
|
1267
|
+
error: { errorMessage: string; name: string },
|
|
1268
|
+
): void;
|
|
1005
1269
|
```
|
|
1006
1270
|
|
|
1007
1271
|
**Example:**
|
|
@@ -1022,9 +1286,84 @@ function loadIssueData(issueId) {
|
|
|
1022
1286
|
}
|
|
1023
1287
|
```
|
|
1024
1288
|
|
|
1289
|
+
### setTerminalError
|
|
1290
|
+
|
|
1291
|
+
Reports a terminal (critical) error to UFO. Terminal errors represent failures that prevent the user
|
|
1292
|
+
from completing their task, such as errors caught by error boundaries.
|
|
1293
|
+
|
|
1294
|
+
**Import:**
|
|
1295
|
+
|
|
1296
|
+
```typescript
|
|
1297
|
+
import { setTerminalError } from '@atlaskit/react-ufo/set-terminal-error';
|
|
1298
|
+
```
|
|
1299
|
+
|
|
1300
|
+
**Signature:**
|
|
1301
|
+
|
|
1302
|
+
```typescript
|
|
1303
|
+
function setTerminalError(
|
|
1304
|
+
error: Error,
|
|
1305
|
+
additionalAttributes?: TerminalErrorAdditionalAttributes,
|
|
1306
|
+
labelStack?: LabelStack,
|
|
1307
|
+
): void;
|
|
1308
|
+
```
|
|
1309
|
+
|
|
1310
|
+
**`TerminalErrorAdditionalAttributes`:**
|
|
1311
|
+
|
|
1312
|
+
| Property | Type | Description |
|
|
1313
|
+
| ---------------------- | ------------------------------ | ---------------------------------------------------------------- |
|
|
1314
|
+
| `teamName` | `string` | Team responsible for the component |
|
|
1315
|
+
| `packageName` | `string` | Package where the error occurred |
|
|
1316
|
+
| `errorBoundaryId` | `string` | Identifier of the error boundary |
|
|
1317
|
+
| `errorHash` | `string` | Custom error identifier for grouping |
|
|
1318
|
+
| `traceId` | `string` | Trace ID for distributed tracing |
|
|
1319
|
+
| `fallbackType` | `'page' \| 'flag' \| 'custom'` | Type of fallback rendered |
|
|
1320
|
+
| `statusCode` | `number` | HTTP status code if applicable |
|
|
1321
|
+
| `isClientNetworkError` | `boolean` | Whether this is a client network error (excluded from reporting) |
|
|
1322
|
+
|
|
1323
|
+
**Example:**
|
|
1324
|
+
|
|
1325
|
+
```typescript
|
|
1326
|
+
import { setTerminalError } from '@atlaskit/react-ufo/set-terminal-error';
|
|
1327
|
+
|
|
1328
|
+
class AppErrorBoundary extends React.Component {
|
|
1329
|
+
componentDidCatch(error) {
|
|
1330
|
+
setTerminalError(error, {
|
|
1331
|
+
errorBoundaryId: 'app-root',
|
|
1332
|
+
fallbackType: 'page',
|
|
1333
|
+
teamName: 'my-team',
|
|
1334
|
+
packageName: '@atlassian/my-app',
|
|
1335
|
+
});
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
```
|
|
1339
|
+
|
|
1340
|
+
### sinkTerminalErrorHandler
|
|
1341
|
+
|
|
1342
|
+
Registers a handler function that is called when terminal errors are reported via
|
|
1343
|
+
`setTerminalError`. This is used to configure how terminal error data is consumed (e.g., sent to
|
|
1344
|
+
analytics).
|
|
1345
|
+
|
|
1346
|
+
**Import:**
|
|
1347
|
+
|
|
1348
|
+
```typescript
|
|
1349
|
+
import { sinkTerminalErrorHandler } from '@atlaskit/react-ufo/set-terminal-error';
|
|
1350
|
+
```
|
|
1351
|
+
|
|
1352
|
+
**Signature:**
|
|
1353
|
+
|
|
1354
|
+
```typescript
|
|
1355
|
+
function sinkTerminalErrorHandler(
|
|
1356
|
+
fn: (errorData: TerminalErrorData, context: TerminalErrorContext) => void | Promise<void>,
|
|
1357
|
+
): void;
|
|
1358
|
+
```
|
|
1359
|
+
|
|
1360
|
+
The `TerminalErrorContext` provides information about the interaction state when the error occurred,
|
|
1361
|
+
including the active and previous interaction names, IDs, and types.
|
|
1362
|
+
|
|
1025
1363
|
### getUFORouteName
|
|
1026
1364
|
|
|
1027
|
-
Extracts the UFO name from a route configuration object.
|
|
1365
|
+
Extracts the UFO name from a route configuration object. Returns `ufoName` if defined, otherwise
|
|
1366
|
+
falls back to `name`.
|
|
1028
1367
|
|
|
1029
1368
|
**Import:**
|
|
1030
1369
|
|
|
@@ -1032,10 +1371,10 @@ Extracts the UFO name from a route configuration object.
|
|
|
1032
1371
|
import getUFORouteName from '@atlaskit/react-ufo/route-name';
|
|
1033
1372
|
```
|
|
1034
1373
|
|
|
1035
|
-
**
|
|
1374
|
+
**Signature:**
|
|
1036
1375
|
|
|
1037
1376
|
```typescript
|
|
1038
|
-
|
|
1377
|
+
function getUFORouteName(route: { ufoName?: string; name: string }): string;
|
|
1039
1378
|
```
|
|
1040
1379
|
|
|
1041
1380
|
**Example:**
|
|
@@ -1058,6 +1397,42 @@ function RouteAnalytics() {
|
|
|
1058
1397
|
}
|
|
1059
1398
|
```
|
|
1060
1399
|
|
|
1400
|
+
### updatePageloadName
|
|
1401
|
+
|
|
1402
|
+
Updates the UFO name of an active page load or transition interaction. Useful when the route name
|
|
1403
|
+
isn't known at the time `traceUFOPageLoad` is first called.
|
|
1404
|
+
|
|
1405
|
+
**Import:**
|
|
1406
|
+
|
|
1407
|
+
```typescript
|
|
1408
|
+
import { updatePageloadName } from '@atlaskit/react-ufo/trace-pageload';
|
|
1409
|
+
```
|
|
1410
|
+
|
|
1411
|
+
**Signature:**
|
|
1412
|
+
|
|
1413
|
+
```typescript
|
|
1414
|
+
function updatePageloadName(
|
|
1415
|
+
ufoName: string,
|
|
1416
|
+
routeName?: string | null | undefined, // defaults to ufoName
|
|
1417
|
+
): void;
|
|
1418
|
+
```
|
|
1419
|
+
|
|
1420
|
+
**Example:**
|
|
1421
|
+
|
|
1422
|
+
```typescript
|
|
1423
|
+
import { updatePageloadName } from '@atlaskit/react-ufo/trace-pageload';
|
|
1424
|
+
|
|
1425
|
+
function RouteResolver({ routeName }) {
|
|
1426
|
+
useEffect(() => {
|
|
1427
|
+
if (routeName) {
|
|
1428
|
+
updatePageloadName(routeName);
|
|
1429
|
+
}
|
|
1430
|
+
}, [routeName]);
|
|
1431
|
+
|
|
1432
|
+
return null;
|
|
1433
|
+
}
|
|
1434
|
+
```
|
|
1435
|
+
|
|
1061
1436
|
---
|
|
1062
1437
|
|
|
1063
1438
|
## Component Integration
|
|
@@ -1067,52 +1442,36 @@ function RouteAnalytics() {
|
|
|
1067
1442
|
Many Atlassian Design System components support an `interactionName` prop that automatically adds
|
|
1068
1443
|
UFO tracking.
|
|
1069
1444
|
|
|
1070
|
-
**Supported Components:**
|
|
1071
|
-
|
|
1072
1445
|
**@atlaskit/button:**
|
|
1073
1446
|
|
|
1074
1447
|
```typescript
|
|
1075
1448
|
import Button from '@atlaskit/button';
|
|
1076
1449
|
|
|
1077
|
-
|
|
1450
|
+
// Triggers a UFO press interaction on click
|
|
1451
|
+
<Button interactionName="save-issue" onClick={handleSave}>
|
|
1452
|
+
Save
|
|
1453
|
+
</Button>
|
|
1078
1454
|
```
|
|
1079
1455
|
|
|
1456
|
+
When no `interactionName` is provided, the button defaults to `unknown`. Unknown interactions are
|
|
1457
|
+
tracked for volume purposes but are **not** aggregated into metrics.
|
|
1458
|
+
|
|
1080
1459
|
**@atlaskit/spinner:**
|
|
1081
1460
|
|
|
1082
1461
|
```typescript
|
|
1083
1462
|
import Spinner from '@atlaskit/spinner';
|
|
1084
1463
|
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
**Examples:**
|
|
1089
|
-
|
|
1090
|
-
```typescript
|
|
1091
|
-
// Button with interaction tracking
|
|
1092
|
-
<Button interactionName="create-issue" onClick={handleCreateIssue}>
|
|
1093
|
-
Create Issue
|
|
1094
|
-
</Button>;
|
|
1095
|
-
|
|
1096
|
-
// Spinner that holds interaction until unmounted
|
|
1097
|
-
{
|
|
1098
|
-
isLoading && <Spinner interactionName="issue-loading" size="large" />;
|
|
1099
|
-
}
|
|
1464
|
+
// Adds a UFO hold while the spinner is mounted
|
|
1465
|
+
<Spinner interactionName="loading-comments" size="medium" />
|
|
1100
1466
|
```
|
|
1101
1467
|
|
|
1102
|
-
**Notes:**
|
|
1103
|
-
|
|
1104
|
-
- Button: Triggers a UFO press interaction on click
|
|
1105
|
-
- Spinner: Adds a UFO hold while the spinner is mounted
|
|
1106
|
-
- Use descriptive, unique names for each interaction
|
|
1107
|
-
|
|
1108
1468
|
---
|
|
1109
1469
|
|
|
1110
1470
|
## Advanced APIs
|
|
1111
1471
|
|
|
1112
1472
|
### SSR Configuration
|
|
1113
1473
|
|
|
1114
|
-
Configure UFO for Server-Side Rendering scenarios. This
|
|
1115
|
-
Metric in Glance.
|
|
1474
|
+
Configure UFO for Server-Side Rendering scenarios. This produces a more accurate FMP metric.
|
|
1116
1475
|
|
|
1117
1476
|
**Import:**
|
|
1118
1477
|
|
|
@@ -1120,22 +1479,21 @@ Metric in Glance.
|
|
|
1120
1479
|
import { configure } from '@atlaskit/react-ufo/ssr';
|
|
1121
1480
|
```
|
|
1122
1481
|
|
|
1123
|
-
**
|
|
1482
|
+
**Signature:**
|
|
1124
1483
|
|
|
1125
1484
|
```typescript
|
|
1126
|
-
|
|
1127
|
-
getDoneMark: () =>
|
|
1128
|
-
getFeatureFlags: () =>
|
|
1129
|
-
getTimings
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
});
|
|
1485
|
+
type SSRConfig = {
|
|
1486
|
+
getDoneMark: () => number | null;
|
|
1487
|
+
getFeatureFlags: () => SSRFeatureFlags | null;
|
|
1488
|
+
getTimings?: () => ReportedTimings | null;
|
|
1489
|
+
getSsrPhaseSuccess?: () => {
|
|
1490
|
+
prefetch?: boolean;
|
|
1491
|
+
earlyFlush?: boolean;
|
|
1492
|
+
done?: boolean;
|
|
1493
|
+
};
|
|
1494
|
+
};
|
|
1495
|
+
|
|
1496
|
+
function configure(ssrConfig: SSRConfig): void;
|
|
1139
1497
|
```
|
|
1140
1498
|
|
|
1141
1499
|
**Configuration Options:**
|
|
@@ -1143,32 +1501,71 @@ configure({
|
|
|
1143
1501
|
- `getDoneMark()`: Returns timestamp when SSR rendering completed
|
|
1144
1502
|
- `getFeatureFlags()`: Returns feature flags used during SSR
|
|
1145
1503
|
- `getTimings()`: Returns detailed SSR timing breakdown
|
|
1146
|
-
- `getSsrPhaseSuccess()`: Returns success status for SSR
|
|
1504
|
+
- `getSsrPhaseSuccess()`: Returns success status for each SSR phase
|
|
1505
|
+
|
|
1506
|
+
**Additional SSR exports:**
|
|
1147
1507
|
|
|
1148
|
-
|
|
1508
|
+
```typescript
|
|
1509
|
+
import {
|
|
1510
|
+
getSSRTimings,
|
|
1511
|
+
getSSRSuccess,
|
|
1512
|
+
getSSRPhaseSuccess,
|
|
1513
|
+
getSSRDoneTime,
|
|
1514
|
+
getSSRFeatureFlags,
|
|
1515
|
+
} from '@atlaskit/react-ufo/ssr';
|
|
1516
|
+
```
|
|
1517
|
+
|
|
1518
|
+
### Configuration
|
|
1149
1519
|
|
|
1150
|
-
|
|
1520
|
+
Set the global UFO configuration for your product.
|
|
1151
1521
|
|
|
1152
1522
|
**Import:**
|
|
1153
1523
|
|
|
1154
1524
|
```typescript
|
|
1155
|
-
import {
|
|
1525
|
+
import { setUFOConfig, getConfig, isUFOEnabled } from '@atlaskit/react-ufo/config';
|
|
1156
1526
|
```
|
|
1157
1527
|
|
|
1158
|
-
**
|
|
1528
|
+
**Key configuration fields:**
|
|
1529
|
+
|
|
1530
|
+
| Field | Type | Description |
|
|
1531
|
+
| ---------------------------------------------- | ------------------------ | ----------------------------------------------------------- |
|
|
1532
|
+
| `enabled` | `boolean` | Whether UFO is enabled |
|
|
1533
|
+
| `product` | `string` | Product identifier (e.g., `'jira'`, `'confluence'`) |
|
|
1534
|
+
| `region` | `string` | Deployment region |
|
|
1535
|
+
| `rates` | `Rates` | Sampling rates for different interaction types |
|
|
1536
|
+
| `interactionTimeout` | `Record<string, number>` | Custom timeouts per interaction |
|
|
1537
|
+
| `minorInteractions` | `string[]` | Interactions classified as minor |
|
|
1538
|
+
| `doNotAbortActivePressInteraction` | `string[]` | Press interactions that should not be aborted |
|
|
1539
|
+
| `doNotAbortActivePressInteractionOnTransition` | `string[]` | Press interactions that should not be aborted on transition |
|
|
1540
|
+
| `finishInteractionOnTransition` | `string[]` | Interactions to finish (not abort) on transition |
|
|
1541
|
+
| `vc` | `object` | Visual Completion observer configuration |
|
|
1542
|
+
| `terminalErrors` | `object` | Terminal error reporting configuration |
|
|
1543
|
+
|
|
1544
|
+
### Experience Trace ID Context
|
|
1545
|
+
|
|
1546
|
+
Manage distributed tracing context for UFO interactions. Enables correlation of frontend performance
|
|
1547
|
+
data with backend traces.
|
|
1548
|
+
|
|
1549
|
+
**Import:**
|
|
1159
1550
|
|
|
1160
1551
|
```typescript
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1552
|
+
import {
|
|
1553
|
+
getActiveTrace,
|
|
1554
|
+
setActiveTrace,
|
|
1555
|
+
clearActiveTrace,
|
|
1556
|
+
getActiveTraceHttpRequestHeaders,
|
|
1557
|
+
getActiveTraceAsQueryParams,
|
|
1558
|
+
} from '@atlaskit/react-ufo/experience-trace-id-context';
|
|
1164
1559
|
```
|
|
1165
1560
|
|
|
1166
|
-
**
|
|
1561
|
+
**Key Functions:**
|
|
1167
1562
|
|
|
1168
|
-
-
|
|
1169
|
-
-
|
|
1170
|
-
-
|
|
1171
|
-
-
|
|
1563
|
+
- `getActiveTrace()`: Returns the current active trace context
|
|
1564
|
+
- `setActiveTrace(traceId, spanId, type)`: Sets the active trace
|
|
1565
|
+
- `clearActiveTrace()`: Clears the active trace
|
|
1566
|
+
- `getActiveTraceHttpRequestHeaders(url?)`: Returns `X-B3-TraceId` and `X-B3-SpanId` headers for
|
|
1567
|
+
HTTP requests
|
|
1568
|
+
- `getActiveTraceAsQueryParams(url?)`: Returns trace context as query parameters
|
|
1172
1569
|
|
|
1173
1570
|
---
|
|
1174
1571
|
|
|
@@ -1183,29 +1580,37 @@ import { UFOThirdPartySegment } from '@atlaskit/react-ufo/segment';
|
|
|
1183
1580
|
|
|
1184
1581
|
### Performance Considerations
|
|
1185
1582
|
|
|
1186
|
-
- Don't over-instrument
|
|
1583
|
+
- Don't over-instrument — focus on key user journeys
|
|
1187
1584
|
- Use segments for logical page sections (like header, content, sidebar)
|
|
1188
1585
|
- Be selective with custom data to avoid large payloads
|
|
1189
1586
|
- Prefer component-level holds over page-level holds for granular tracking
|
|
1190
|
-
- Use
|
|
1191
|
-
-
|
|
1587
|
+
- Use `UFOThirdPartySegment` for external content that may impact performance
|
|
1588
|
+
- Use `UFOInteractionIgnore` for non-critical components that load late
|
|
1192
1589
|
|
|
1193
|
-
###
|
|
1590
|
+
### Interaction Lifecycle
|
|
1194
1591
|
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
- Test with feature flags to ensure performance tracking works across variants
|
|
1202
|
-
- Pay attention to "holds" that never release - they prevent interactions from completing
|
|
1592
|
+
1. An interaction starts via `traceUFOPageLoad`, `traceUFOTransition`, `traceUFOPress`, or
|
|
1593
|
+
`traceUFOHover`
|
|
1594
|
+
2. `UFOLoadHold` components register active holds that prevent completion
|
|
1595
|
+
3. The interaction completes (TTAI) at the next paint after all holds are released
|
|
1596
|
+
4. VC metrics are calculated based on viewport mutations during the interaction window
|
|
1597
|
+
5. Only one interaction can be active at a time — new interactions abort previous ones
|
|
1203
1598
|
|
|
1204
|
-
###
|
|
1599
|
+
### Feature Gating
|
|
1205
1600
|
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1601
|
+
All changes to React UFO should use feature gates:
|
|
1602
|
+
|
|
1603
|
+
```typescript
|
|
1604
|
+
import { fg } from '@atlaskit/platform-feature-flags';
|
|
1605
|
+
|
|
1606
|
+
if (fg('my_feature_gate')) {
|
|
1607
|
+
// New behavior
|
|
1608
|
+
} else {
|
|
1609
|
+
// Existing behavior
|
|
1610
|
+
}
|
|
1611
|
+
```
|
|
1612
|
+
|
|
1613
|
+
Register feature gates in `package.json` under `platform-feature-flags`.
|
|
1209
1614
|
|
|
1210
1615
|
---
|
|
1211
1616
|
|
|
@@ -1215,15 +1620,15 @@ import { UFOThirdPartySegment } from '@atlaskit/react-ufo/segment';
|
|
|
1215
1620
|
|
|
1216
1621
|
```typescript
|
|
1217
1622
|
function MyPage() {
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1623
|
+
useEffect(() => {
|
|
1624
|
+
traceUFOPageLoad('my-page');
|
|
1625
|
+
}, []);
|
|
1626
|
+
|
|
1627
|
+
return (
|
|
1628
|
+
<UFOSegment name="main-content">
|
|
1629
|
+
<PageContent />
|
|
1630
|
+
</UFOSegment>
|
|
1631
|
+
);
|
|
1227
1632
|
}
|
|
1228
1633
|
```
|
|
1229
1634
|
|
|
@@ -1231,19 +1636,19 @@ function MyPage() {
|
|
|
1231
1636
|
|
|
1232
1637
|
```typescript
|
|
1233
1638
|
function DataComponent() {
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1639
|
+
const [loading, setLoading] = useState(true);
|
|
1640
|
+
|
|
1641
|
+
return (
|
|
1642
|
+
<UFOSegment name="data-section">
|
|
1643
|
+
{loading ? (
|
|
1644
|
+
<UFOLoadHold name="data-loading">
|
|
1645
|
+
<Skeleton />
|
|
1646
|
+
</UFOLoadHold>
|
|
1647
|
+
) : (
|
|
1648
|
+
<DataTable />
|
|
1649
|
+
)}
|
|
1650
|
+
</UFOSegment>
|
|
1651
|
+
);
|
|
1247
1652
|
}
|
|
1248
1653
|
```
|
|
1249
1654
|
|
|
@@ -1251,19 +1656,37 @@ function DataComponent() {
|
|
|
1251
1656
|
|
|
1252
1657
|
```typescript
|
|
1253
1658
|
function NavigationWrapper() {
|
|
1254
|
-
|
|
1659
|
+
const [routerState] = useRouter();
|
|
1255
1660
|
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1661
|
+
useEffect(() => {
|
|
1662
|
+
const ufoName = getUFORouteName(routerState.route);
|
|
1663
|
+
if (ufoName) {
|
|
1664
|
+
traceUFOTransition(ufoName);
|
|
1665
|
+
}
|
|
1666
|
+
}, [routerState]);
|
|
1262
1667
|
|
|
1263
|
-
|
|
1668
|
+
return <Router />;
|
|
1264
1669
|
}
|
|
1265
1670
|
```
|
|
1266
1671
|
|
|
1672
|
+
### Error Boundary with Terminal Error Reporting
|
|
1673
|
+
|
|
1674
|
+
```typescript
|
|
1675
|
+
import { useReportTerminalError } from '@atlaskit/react-ufo/set-terminal-error';
|
|
1676
|
+
|
|
1677
|
+
function ErrorBoundaryFallback({ error }) {
|
|
1678
|
+
useReportTerminalError(error, {
|
|
1679
|
+
errorBoundaryId: 'main-content',
|
|
1680
|
+
fallbackType: 'page',
|
|
1681
|
+
teamName: 'my-team',
|
|
1682
|
+
});
|
|
1683
|
+
|
|
1684
|
+
return <ErrorPage message="Something went wrong" />;
|
|
1685
|
+
}
|
|
1686
|
+
```
|
|
1687
|
+
|
|
1688
|
+
---
|
|
1689
|
+
|
|
1267
1690
|
## Troubleshooting
|
|
1268
1691
|
|
|
1269
1692
|
### Common Issues
|
|
@@ -1273,21 +1696,41 @@ function NavigationWrapper() {
|
|
|
1273
1696
|
- Check for holds that are never released (components that mount `UFOLoadHold` but never unmount)
|
|
1274
1697
|
- Verify all async operations complete properly
|
|
1275
1698
|
- Ensure error boundaries don't prevent holds from releasing
|
|
1699
|
+
- Use `UFOInteractionIgnore` for non-critical components that load late
|
|
1276
1700
|
|
|
1277
1701
|
**Missing Analytics Events**
|
|
1278
1702
|
|
|
1279
|
-
- Verify UFO configuration is properly set up
|
|
1703
|
+
- Verify UFO configuration is properly set up via `setUFOConfig`
|
|
1280
1704
|
- Check that sampling rates are configured for your interaction names
|
|
1281
1705
|
- Ensure you're calling trace functions early in the component lifecycle
|
|
1282
1706
|
|
|
1707
|
+
**Aborted Interactions**
|
|
1708
|
+
|
|
1709
|
+
- Only one interaction can be active at a time
|
|
1710
|
+
- A new press interaction aborts the previous interaction
|
|
1711
|
+
- Aborted interactions do **not** report metrics to prevent data skewing
|
|
1712
|
+
- Check `doNotAbortActivePressInteraction` config if your interaction is unexpectedly aborted
|
|
1713
|
+
|
|
1283
1714
|
**Performance Impact**
|
|
1284
1715
|
|
|
1285
|
-
- Avoid over-instrumentation
|
|
1716
|
+
- Avoid over-instrumentation — focus on critical user journeys
|
|
1286
1717
|
- Use feature flags to control UFO instrumentation in production
|
|
1287
1718
|
- Monitor payload sizes if adding extensive custom data
|
|
1288
1719
|
|
|
1289
1720
|
**SSR Compatibility**
|
|
1290
1721
|
|
|
1291
1722
|
- Use the SSR configuration APIs for server-side rendered applications
|
|
1292
|
-
-
|
|
1723
|
+
- Confluence uses `window.performance.mark("CFP-63.ssr-ttr")` for SSR timing via `getDoneMark`
|
|
1293
1724
|
- Test SSR placeholder behavior with your specific setup
|
|
1725
|
+
|
|
1726
|
+
---
|
|
1727
|
+
|
|
1728
|
+
## Additional Resources
|
|
1729
|
+
|
|
1730
|
+
- [React UFO Overview](https://hello.atlassian.net/wiki/spaces/UFO/pages/2305847386/react-ufo+UFO+v2)
|
|
1731
|
+
- [How React UFO calculates metrics](https://hello.atlassian.net/wiki/spaces/APD/pages/5046492124)
|
|
1732
|
+
- [React UFO Contribution Guidelines](https://hello.atlassian.net/wiki/spaces/APD/pages/4717459313)
|
|
1733
|
+
- [Press Interactions Overview](https://hello.atlassian.net/wiki/spaces/APD/pages/5770720615)
|
|
1734
|
+
- [Interactivity Metrics](https://hello.atlassian.net/wiki/spaces/APD/pages/5026355203)
|
|
1735
|
+
- [React UFO for Platform Components](https://hello.atlassian.net/wiki/spaces/APD/pages/6170852473)
|
|
1736
|
+
- [React UFO: A Deeper Understanding](https://hello.atlassian.net/wiki/spaces/UFO/blog/2022/12/16/2280380649/react-UFO+A+deeper+understanding+of+performance)
|