@atlaskit/react-ufo 4.15.6 → 4.15.8
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/CHANGELOG.md +16 -0
- package/api-reference.md +1188 -0
- package/dist/cjs/create-post-interaction-log-payload/get-late-mutations.js +2 -3
- package/dist/cjs/vc/vc-observer/observers/ssr-placeholders/index.js +28 -0
- package/dist/cjs/vc/vc-observer-new/index.js +1 -1
- package/dist/cjs/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.js +11 -7
- package/dist/cjs/vc/vc-observer-new/raw-data-handler/index.js +1 -1
- package/dist/cjs/vc/vc-observer-new/viewport-observer/index.js +2 -2
- package/dist/es2019/create-post-interaction-log-payload/get-late-mutations.js +2 -3
- package/dist/es2019/vc/vc-observer/observers/ssr-placeholders/index.js +24 -0
- package/dist/es2019/vc/vc-observer-new/index.js +1 -1
- package/dist/es2019/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.js +11 -7
- package/dist/es2019/vc/vc-observer-new/raw-data-handler/index.js +1 -1
- package/dist/es2019/vc/vc-observer-new/viewport-observer/index.js +2 -2
- package/dist/esm/create-post-interaction-log-payload/get-late-mutations.js +2 -3
- package/dist/esm/vc/vc-observer/observers/ssr-placeholders/index.js +28 -0
- package/dist/esm/vc/vc-observer-new/index.js +1 -1
- package/dist/esm/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.js +11 -7
- package/dist/esm/vc/vc-observer-new/raw-data-handler/index.js +1 -1
- package/dist/esm/vc/vc-observer-new/viewport-observer/index.js +2 -2
- package/dist/types/common/vc/types.d.ts +1 -1
- package/dist/types/vc/vc-observer/observers/ssr-placeholders/index.d.ts +2 -0
- package/dist/types-ts4.5/common/vc/types.d.ts +1 -1
- package/dist/types-ts4.5/vc/vc-observer/observers/ssr-placeholders/index.d.ts +2 -0
- package/package.json +2 -2
package/api-reference.md
ADDED
|
@@ -0,0 +1,1188 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: React-UFO - Instrumentation API Reference
|
|
3
|
+
description: 'Complete API reference for @atlaskit/react-ufo performance instrumentation'
|
|
4
|
+
platform: platform
|
|
5
|
+
product: ufo
|
|
6
|
+
category: devguide
|
|
7
|
+
subcategory: react-ufo
|
|
8
|
+
date: '2025-11-10'
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# React UFO Instrumentation API Reference
|
|
12
|
+
|
|
13
|
+
React UFO provides performance instrumentation components and utilities to measure user experience metrics across Atlassian products. This library captures key performance indicators like Time to Interactive (TTI), Time to Visual Complete (TTVC), and custom interaction metrics.
|
|
14
|
+
|
|
15
|
+
## Getting Started
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
yarn add @atlaskit/react-ufo
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## API Reference
|
|
22
|
+
|
|
23
|
+
### Interaction Initializers
|
|
24
|
+
|
|
25
|
+
- [traceUFOPageLoad](#traceufopageload)
|
|
26
|
+
- [traceUFOPress](#traceufopress)
|
|
27
|
+
- [traceUFOTransition](#traceufotransition)
|
|
28
|
+
- [traceUFOInteraction](#traceufointeraction)
|
|
29
|
+
- [traceUFORedirect](#traceuforedirect)
|
|
30
|
+
|
|
31
|
+
### React Components
|
|
32
|
+
|
|
33
|
+
- [UFOSegment](#ufosegment)
|
|
34
|
+
- [UFOLabel](#ufolabel)
|
|
35
|
+
- [UFOLoadHold](#ufoloadhold)
|
|
36
|
+
- [Suspense](#suspense)
|
|
37
|
+
- [UFOCustomData](#ufocustomdata)
|
|
38
|
+
- [UFOCustomCohortData](#ufocustomcohortdata) ⚠️ Experimental
|
|
39
|
+
|
|
40
|
+
### React Hooks
|
|
41
|
+
|
|
42
|
+
- [usePressTracing](#usepresstracing)
|
|
43
|
+
- [useUFOTypingPerformanceTracing](#useufotypingperformancetracing) ⚠️ Experimental
|
|
44
|
+
- [useUFOTransitionCompleter](#useufotransitioncompleter)
|
|
45
|
+
- [useUFOReportError](#useuforeporterror)
|
|
46
|
+
|
|
47
|
+
### Utility Functions
|
|
48
|
+
|
|
49
|
+
- [addUFOCustomData](#addufocustomdata)
|
|
50
|
+
- [addUFOCustomCohortData](#addufocustomcohortdata) ⚠️ Experimental
|
|
51
|
+
- [addFeatureFlagAccessed](#addfeatureflagaccessed)
|
|
52
|
+
- [setInteractionError](#setinteractionerror)
|
|
53
|
+
- [getUFORouteName](#getuforoutename)
|
|
54
|
+
|
|
55
|
+
### Component Integration
|
|
56
|
+
|
|
57
|
+
- [interactionName Property](#interactionname)
|
|
58
|
+
|
|
59
|
+
### Advanced APIs
|
|
60
|
+
|
|
61
|
+
- [SSR Configuration](#ssr-configuration)
|
|
62
|
+
- [VC (Visual Completion) Observer](#vc-observer)
|
|
63
|
+
- [Third-Party Segments](#third-party-segments)
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Interaction Initializers
|
|
68
|
+
|
|
69
|
+
### traceUFOPageLoad
|
|
70
|
+
|
|
71
|
+
Initializes measurement of page load performance for a specific route or page. This function should be called early in the page lifecycle to capture accurate timing data.
|
|
72
|
+
|
|
73
|
+
**Import:**
|
|
74
|
+
```typescript
|
|
75
|
+
import traceUFOPageLoad from '@atlaskit/react-ufo/trace-pageload';
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**Usage:**
|
|
79
|
+
```typescript
|
|
80
|
+
traceUFOPageLoad('issue-view');
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
**Parameters:**
|
|
84
|
+
- `ufoName` (string): The UFO interaction name used for sampling and event identification
|
|
85
|
+
|
|
86
|
+
**Example:**
|
|
87
|
+
```typescript
|
|
88
|
+
// In your route component
|
|
89
|
+
import { useEffect } from 'react';
|
|
90
|
+
import traceUFOPageLoad from '@atlaskit/react-ufo/trace-pageload';
|
|
91
|
+
|
|
92
|
+
function IssueView() {
|
|
93
|
+
useEffect(() => {
|
|
94
|
+
traceUFOPageLoad('issue-view');
|
|
95
|
+
}, []);
|
|
96
|
+
|
|
97
|
+
return <div>Issue content...</div>;
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**Note:** The function uses sampling rates configured in your UFO settings to control which page loads are measured.
|
|
102
|
+
|
|
103
|
+
### traceUFOPress
|
|
104
|
+
|
|
105
|
+
Measures user interaction performance from a press/click event until all loading is complete. Use this when `usePressTracing` hook or the `interactionName` property on components isn't suitable.
|
|
106
|
+
|
|
107
|
+
**Import:**
|
|
108
|
+
```typescript
|
|
109
|
+
import traceUFOPress from '@atlaskit/react-ufo/trace-press';
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
**Usage:**
|
|
113
|
+
```typescript
|
|
114
|
+
traceUFOPress('button-click');
|
|
115
|
+
traceUFOPress('menu-open', performance.now()); // with custom timestamp
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
**Parameters:**
|
|
119
|
+
- `name` (string): Unique identifier for the interaction
|
|
120
|
+
- `timestamp` (number, optional): Custom timestamp, defaults to `performance.now()`
|
|
121
|
+
|
|
122
|
+
**Example:**
|
|
123
|
+
```typescript
|
|
124
|
+
import traceUFOPress from '@atlaskit/react-ufo/trace-press';
|
|
125
|
+
|
|
126
|
+
function CustomButton() {
|
|
127
|
+
const handleClick = () => {
|
|
128
|
+
traceUFOPress('custom-action');
|
|
129
|
+
// Trigger your action that may cause loading states
|
|
130
|
+
performAsyncAction();
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
return <button onClick={handleClick}>Custom Action</button>;
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
**When to use:**
|
|
138
|
+
- Custom components that don't support `interactionName`
|
|
139
|
+
- Complex interaction patterns
|
|
140
|
+
- Manual event handling scenarios
|
|
141
|
+
|
|
142
|
+
**Note:** Results are sent as `inline-result` type events. Prefer `usePressTracing` or `interactionName` when possible.
|
|
143
|
+
|
|
144
|
+
### traceUFOTransition
|
|
145
|
+
|
|
146
|
+
Measures navigation transition performance between routes or major state changes within a single-page application. This function aborts active interactions and starts measuring the new transition.
|
|
147
|
+
|
|
148
|
+
**Import:**
|
|
149
|
+
```typescript
|
|
150
|
+
import traceUFOTransition from '@atlaskit/react-ufo/trace-transition';
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
**Usage:**
|
|
154
|
+
```typescript
|
|
155
|
+
traceUFOTransition('board-to-backlog');
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
**Parameters:**
|
|
159
|
+
- `ufoName` (string): The UFO name for the transition
|
|
160
|
+
|
|
161
|
+
**Example:**
|
|
162
|
+
```typescript
|
|
163
|
+
import { useRouter } from '@atlassian/react-resource-router';
|
|
164
|
+
import traceUFOTransition from '@atlaskit/react-ufo/trace-transition';
|
|
165
|
+
|
|
166
|
+
function NavigationHandler() {
|
|
167
|
+
const [routerState] = useRouter();
|
|
168
|
+
|
|
169
|
+
useEffect(() => {
|
|
170
|
+
const routeName = getUFORouteName(routerState.route);
|
|
171
|
+
if (routeName) {
|
|
172
|
+
traceUFOTransition(routeName);
|
|
173
|
+
}
|
|
174
|
+
}, [routerState]);
|
|
175
|
+
|
|
176
|
+
return <div>Page content...</div>;
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### traceUFOInteraction
|
|
181
|
+
|
|
182
|
+
Measures performance of user interactions based on browser events. This function automatically detects the interaction type from the event and applies appropriate timing.
|
|
183
|
+
|
|
184
|
+
**Import:**
|
|
185
|
+
```typescript
|
|
186
|
+
import traceUFOInteraction from '@atlaskit/react-ufo/trace-interaction';
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
**Usage:**
|
|
190
|
+
```typescript
|
|
191
|
+
traceUFOInteraction('dropdown-open', event);
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
**Parameters:**
|
|
195
|
+
- `name` (string): Unique identifier for the interaction
|
|
196
|
+
- `event` (Event | UIEvent): Browser event object (must be trusted)
|
|
197
|
+
|
|
198
|
+
**Supported Event Types:**
|
|
199
|
+
- `click`
|
|
200
|
+
- `dblclick`
|
|
201
|
+
- `mousedown`
|
|
202
|
+
- `mouseenter`
|
|
203
|
+
- `mouseover`
|
|
204
|
+
|
|
205
|
+
**Example:**
|
|
206
|
+
```typescript
|
|
207
|
+
import traceUFOInteraction from '@atlaskit/react-ufo/trace-interaction';
|
|
208
|
+
|
|
209
|
+
function InteractiveComponent() {
|
|
210
|
+
const handleClick = (event: React.MouseEvent) => {
|
|
211
|
+
traceUFOInteraction('component-action', event.nativeEvent);
|
|
212
|
+
|
|
213
|
+
// Your interaction logic
|
|
214
|
+
setShowModal(true);
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
return (
|
|
218
|
+
<div onClick={handleClick}>
|
|
219
|
+
Click me
|
|
220
|
+
</div>
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
**Notes:**
|
|
226
|
+
- Only trusted events (user-initiated) are processed
|
|
227
|
+
- Unsupported event types are ignored
|
|
228
|
+
- Event timestamp is automatically extracted from the event object
|
|
229
|
+
|
|
230
|
+
### traceUFORedirect
|
|
231
|
+
|
|
232
|
+
Specialized function for tracking navigation between routes, providing detailed route transition information. This is typically used with routing libraries like `@atlassian/react-resource-router`.
|
|
233
|
+
|
|
234
|
+
**Import:**
|
|
235
|
+
```typescript
|
|
236
|
+
import traceUFORedirect from '@atlaskit/react-ufo/trace-redirect';
|
|
237
|
+
import getUFORouteName from '@atlaskit/react-ufo/route-name';
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
**Usage:**
|
|
241
|
+
```typescript
|
|
242
|
+
traceUFORedirect(fromUfoName, nextUfoName, nextRouteName, timestamp);
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
**Parameters:**
|
|
246
|
+
- `fromUfoName` (string): Current route UFO name
|
|
247
|
+
- `nextUfoName` (string): Target route UFO name from route config
|
|
248
|
+
- `nextRouteName` (string): Target route name from route definition
|
|
249
|
+
- `time` (number): High-precision timestamp from `performance.now()`
|
|
250
|
+
|
|
251
|
+
**Example with React Resource Router:**
|
|
252
|
+
```typescript
|
|
253
|
+
import { matchRoute, useRouter } from '@atlassian/react-resource-router';
|
|
254
|
+
import traceUFORedirect from '@atlaskit/react-ufo/trace-redirect';
|
|
255
|
+
import getUFORouteName from '@atlaskit/react-ufo/route-name';
|
|
256
|
+
|
|
257
|
+
function NavigationTracker() {
|
|
258
|
+
const [routerState] = useRouter();
|
|
259
|
+
|
|
260
|
+
const handleNavigation = (nextLocation) => {
|
|
261
|
+
const currentRouteName = getUFORouteName(routerState.route);
|
|
262
|
+
|
|
263
|
+
const nextMatchObject = matchRoute(routes, nextLocation.pathname, nextLocation.search);
|
|
264
|
+
const { route: nextRoute } = nextMatchObject;
|
|
265
|
+
const nextRouteUfoName = getUFORouteName(nextRoute);
|
|
266
|
+
|
|
267
|
+
traceUFORedirect(
|
|
268
|
+
currentRouteName,
|
|
269
|
+
nextRouteUfoName,
|
|
270
|
+
nextRoute.name,
|
|
271
|
+
performance.now()
|
|
272
|
+
);
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
return null; // This is typically a side-effect component
|
|
276
|
+
}
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
**Use Cases:**
|
|
280
|
+
- Single-page application route changes
|
|
281
|
+
- Cross-product navigation
|
|
282
|
+
- Complex navigation patterns requiring detailed tracking
|
|
283
|
+
|
|
284
|
+
---
|
|
285
|
+
|
|
286
|
+
## React Components
|
|
287
|
+
|
|
288
|
+
### UFOSegment
|
|
289
|
+
|
|
290
|
+
A React context provider that defines distinct, measurable sections of your application. Each segment generates its own performance payload, allowing granular analysis of different page areas.
|
|
291
|
+
|
|
292
|
+
**Import:**
|
|
293
|
+
```typescript
|
|
294
|
+
import UFOSegment from '@atlaskit/react-ufo/segment';
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
**Basic Usage:**
|
|
298
|
+
```typescript
|
|
299
|
+
<UFOSegment name="sidebar">
|
|
300
|
+
<SidebarComponent />
|
|
301
|
+
</UFOSegment>
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
**Parameters:**
|
|
305
|
+
- `name` (string): Unique identifier for this page segment
|
|
306
|
+
- `children` (ReactNode): Components to be measured within this segment
|
|
307
|
+
|
|
308
|
+
**Real-world Example:**
|
|
309
|
+
```typescript
|
|
310
|
+
import UFOSegment from '@atlaskit/react-ufo/segment';
|
|
311
|
+
|
|
312
|
+
function IssuePage() {
|
|
313
|
+
return (
|
|
314
|
+
<div>
|
|
315
|
+
<UFOSegment name="issue-header">
|
|
316
|
+
<IssueHeader />
|
|
317
|
+
</UFOSegment>
|
|
318
|
+
|
|
319
|
+
<UFOSegment name="issue-content">
|
|
320
|
+
<IssueDescription />
|
|
321
|
+
<IssueComments />
|
|
322
|
+
</UFOSegment>
|
|
323
|
+
|
|
324
|
+
<UFOSegment name="issue-sidebar">
|
|
325
|
+
<IssueSidebar />
|
|
326
|
+
</UFOSegment>
|
|
327
|
+
</div>
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
**Event Generation:**
|
|
333
|
+
For a page load with UFO name `issue-view`, segments generate:
|
|
334
|
+
- Main interaction: `jira.fe.page-load.issue-view`
|
|
335
|
+
- Segment events:
|
|
336
|
+
- `jira.fe.page-segment-load.issue-header`
|
|
337
|
+
- `jira.fe.page-segment-load.issue-content`
|
|
338
|
+
- `jira.fe.page-segment-load.issue-sidebar`
|
|
339
|
+
|
|
340
|
+
**Best Practices:**
|
|
341
|
+
- Use distinct, meaningful names for each segment
|
|
342
|
+
- Represent logical page sections that could theoretically render independently
|
|
343
|
+
- Avoid generic names like "content" or "main" - be specific
|
|
344
|
+
- Segments with identical names are aggregated in analytics
|
|
345
|
+
- Nesting is supported but use judiciously
|
|
346
|
+
|
|
347
|
+
**Third-Party Extensions:**
|
|
348
|
+
```typescript
|
|
349
|
+
import { UFOThirdPartySegment } from '@atlaskit/react-ufo/segment';
|
|
350
|
+
|
|
351
|
+
// For external/plugin content that should be tracked separately
|
|
352
|
+
<UFOThirdPartySegment name="confluence-macro-calendar">
|
|
353
|
+
<CalendarMacro />
|
|
354
|
+
</UFOThirdPartySegment>
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
### UFOLabel
|
|
358
|
+
|
|
359
|
+
A React context provider used to annotate sections of your component tree with descriptive names. Unlike segments, labels don't generate separate events but provide context for debugging and analysis.
|
|
360
|
+
|
|
361
|
+
**Import:**
|
|
362
|
+
```typescript
|
|
363
|
+
import UFOLabel from '@atlaskit/react-ufo/label';
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
**Usage:**
|
|
367
|
+
```typescript
|
|
368
|
+
<UFOLabel name="welcome_banner">
|
|
369
|
+
<Text>Hello folks</Text>
|
|
370
|
+
</UFOLabel>
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
**Example:**
|
|
374
|
+
```typescript
|
|
375
|
+
import UFOLabel from '@atlaskit/react-ufo/label';
|
|
376
|
+
|
|
377
|
+
function CommentThread() {
|
|
378
|
+
return (
|
|
379
|
+
<UFOLabel name="comment_thread">
|
|
380
|
+
<div>
|
|
381
|
+
<UFOLabel name="comment_author">
|
|
382
|
+
<UserAvatar user={author} />
|
|
383
|
+
</UFOLabel>
|
|
384
|
+
<UFOLabel name="comment_content">
|
|
385
|
+
<CommentBody content={comment.body} />
|
|
386
|
+
</UFOLabel>
|
|
387
|
+
</div>
|
|
388
|
+
</UFOLabel>
|
|
389
|
+
);
|
|
390
|
+
}
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
**Guidelines:**
|
|
394
|
+
- Use static strings only - no dynamic values
|
|
395
|
+
- Focus on being distinct rather than semantically perfect
|
|
396
|
+
- No need to label every component - use judiciously
|
|
397
|
+
- Helps with debugging performance issues in specific UI areas
|
|
398
|
+
|
|
399
|
+
### UFOLoadHold
|
|
400
|
+
|
|
401
|
+
Identifies loading elements and prevents interaction completion until they are no longer visible to the user. This component is crucial for accurate TTI (Time to Interactive) measurements.
|
|
402
|
+
|
|
403
|
+
**Import:**
|
|
404
|
+
```typescript
|
|
405
|
+
import UFOLoadHold from '@atlaskit/react-ufo/load-hold';
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
**Usage Patterns:**
|
|
409
|
+
|
|
410
|
+
**1. Wrapping loading elements:**
|
|
411
|
+
```typescript
|
|
412
|
+
if (isLoading) {
|
|
413
|
+
return (
|
|
414
|
+
<UFOLoadHold name="card-loading">
|
|
415
|
+
<Skeleton />
|
|
416
|
+
</UFOLoadHold>
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
**2. As a sibling component:**
|
|
422
|
+
```typescript
|
|
423
|
+
return (
|
|
424
|
+
<>
|
|
425
|
+
<Skeleton />
|
|
426
|
+
<UFOLoadHold name="card-loading" />
|
|
427
|
+
</>
|
|
428
|
+
);
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
**3. Conditional wrapping:**
|
|
432
|
+
```typescript
|
|
433
|
+
<UFOLoadHold name="card" hold={isLoading}>
|
|
434
|
+
<Card />
|
|
435
|
+
</UFOLoadHold>
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
**4. Conditional sibling:**
|
|
439
|
+
```typescript
|
|
440
|
+
return (
|
|
441
|
+
<>
|
|
442
|
+
<Card />
|
|
443
|
+
<UFOLoadHold name="card" hold={isLoading} />
|
|
444
|
+
</>
|
|
445
|
+
);
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
**Parameters:**
|
|
449
|
+
- `name` (string): Unique identifier for this loading state
|
|
450
|
+
- `hold` (boolean, optional): Controls whether the hold is active
|
|
451
|
+
- `children` (ReactNode, optional): Components wrapped by the hold
|
|
452
|
+
|
|
453
|
+
**Real-world Examples:**
|
|
454
|
+
```typescript
|
|
455
|
+
import UFOLoadHold from '@atlaskit/react-ufo/load-hold';
|
|
456
|
+
|
|
457
|
+
// Loading skeleton
|
|
458
|
+
function IssueCard({ issue }) {
|
|
459
|
+
if (!issue) {
|
|
460
|
+
return (
|
|
461
|
+
<UFOLoadHold name="issue-card-loading">
|
|
462
|
+
<IssueCardSkeleton />
|
|
463
|
+
</UFOLoadHold>
|
|
464
|
+
);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
return <IssueCardContent issue={issue} />;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// Conditional loading
|
|
471
|
+
function CommentsSection({ loading, comments }) {
|
|
472
|
+
return (
|
|
473
|
+
<div>
|
|
474
|
+
<UFOLoadHold name="comments-loading" hold={loading} />
|
|
475
|
+
{loading ? <CommentsSkeleton /> : <CommentsList comments={comments} />}
|
|
476
|
+
</div>
|
|
477
|
+
);
|
|
478
|
+
}
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
**Notes:**
|
|
482
|
+
- Only instrument loading elements once if they're reused
|
|
483
|
+
- Multiple holds with the same name are fine
|
|
484
|
+
- Holds automatically release when component unmounts
|
|
485
|
+
|
|
486
|
+
### Suspense
|
|
487
|
+
|
|
488
|
+
An enhanced Suspense boundary that provides UFO instrumentation. Use this as a drop-in replacement for React's `Suspense` component.
|
|
489
|
+
|
|
490
|
+
**Import:**
|
|
491
|
+
```typescript
|
|
492
|
+
import Suspense from '@atlaskit/react-ufo/suspense';
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
**Usage:**
|
|
496
|
+
```typescript
|
|
497
|
+
<Suspense name="lazy-component" fallback={<Skeleton />}>
|
|
498
|
+
<LazyComponent />
|
|
499
|
+
</Suspense>
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
**Parameters:**
|
|
503
|
+
- `name` (string): Unique identifier for this suspense boundary
|
|
504
|
+
- `fallback` (ReactNode): Component to show while suspended
|
|
505
|
+
- `children` (ReactNode): Components that might suspend
|
|
506
|
+
|
|
507
|
+
**Example:**
|
|
508
|
+
```typescript
|
|
509
|
+
import Suspense from '@atlaskit/react-ufo/suspense';
|
|
510
|
+
|
|
511
|
+
const LazyIssueView = lazy(() => import('./IssueView'));
|
|
512
|
+
|
|
513
|
+
function App() {
|
|
514
|
+
return (
|
|
515
|
+
<Suspense
|
|
516
|
+
name="issue-view-lazy"
|
|
517
|
+
fallback={<IssueViewSkeleton />}
|
|
518
|
+
>
|
|
519
|
+
<LazyIssueView />
|
|
520
|
+
</Suspense>
|
|
521
|
+
);
|
|
522
|
+
}
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
### UFOCustomData
|
|
526
|
+
|
|
527
|
+
Adds custom metadata to the current UFO interaction. This data appears in analytics events with a "custom:" prefix.
|
|
528
|
+
|
|
529
|
+
**Import:**
|
|
530
|
+
```typescript
|
|
531
|
+
import UFOCustomData from '@atlaskit/react-ufo/custom-data';
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
**Usage:**
|
|
535
|
+
```typescript
|
|
536
|
+
<UFOCustomData data={{ viewType: 'list', itemCount: 42 }} />
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
**Example:**
|
|
540
|
+
```typescript
|
|
541
|
+
import UFOCustomData from '@atlaskit/react-ufo/custom-data';
|
|
542
|
+
|
|
543
|
+
function IssueList({ issues, viewType }) {
|
|
544
|
+
const customData = {
|
|
545
|
+
viewType,
|
|
546
|
+
issueCount: issues.length,
|
|
547
|
+
hasFilters: filters.length > 0
|
|
548
|
+
};
|
|
549
|
+
|
|
550
|
+
return (
|
|
551
|
+
<div>
|
|
552
|
+
<UFOCustomData data={customData} />
|
|
553
|
+
<IssueTable issues={issues} />
|
|
554
|
+
</div>
|
|
555
|
+
);
|
|
556
|
+
}
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
**Output in analytics:**
|
|
560
|
+
```javascript
|
|
561
|
+
{
|
|
562
|
+
'custom:viewType': 'list',
|
|
563
|
+
'custom:issueCount': 42,
|
|
564
|
+
'custom:hasFilters': true
|
|
565
|
+
}
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
### UFOCustomCohortData ⚠️ Experimental
|
|
569
|
+
|
|
570
|
+
**Note: This is an experimental feature that may change or be removed in future versions.**
|
|
571
|
+
|
|
572
|
+
Adds cohort-specific custom data to UFO interactions. This is used for A/B testing and feature flag correlation.
|
|
573
|
+
|
|
574
|
+
**Import:**
|
|
575
|
+
```typescript
|
|
576
|
+
import UFOCustomCohortData from '@atlaskit/react-ufo/custom-cohort-data';
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
**Usage:**
|
|
580
|
+
```typescript
|
|
581
|
+
<UFOCustomCohortData dataKey="experiment_variant" value="control" />
|
|
582
|
+
```
|
|
583
|
+
|
|
584
|
+
**Example:**
|
|
585
|
+
```typescript
|
|
586
|
+
import UFOCustomCohortData from '@atlaskit/react-ufo/custom-cohort-data';
|
|
587
|
+
import { fg } from '@atlassian/jira-feature-gating';
|
|
588
|
+
|
|
589
|
+
function ExperimentalFeature() {
|
|
590
|
+
const variant = fg('new_issue_view') ? 'treatment' : 'control';
|
|
591
|
+
|
|
592
|
+
return (
|
|
593
|
+
<div>
|
|
594
|
+
<UFOCustomCohortData dataKey="new_issue_view_variant" value={variant} />
|
|
595
|
+
{variant === 'treatment' ? <NewIssueView /> : <OldIssueView />}
|
|
596
|
+
</div>
|
|
597
|
+
);
|
|
598
|
+
}
|
|
599
|
+
```
|
|
600
|
+
|
|
601
|
+
---
|
|
602
|
+
|
|
603
|
+
## React Hooks
|
|
604
|
+
|
|
605
|
+
### usePressTracing
|
|
606
|
+
|
|
607
|
+
A hook that provides a function to trace user press interactions. Use this for custom components that need interaction tracking.
|
|
608
|
+
|
|
609
|
+
**Import:**
|
|
610
|
+
```typescript
|
|
611
|
+
import usePressTracing from '@atlaskit/react-ufo/use-press-tracing';
|
|
612
|
+
```
|
|
613
|
+
|
|
614
|
+
**Usage:**
|
|
615
|
+
```typescript
|
|
616
|
+
const traceInteraction = usePressTracing('custom-button-click');
|
|
617
|
+
```
|
|
618
|
+
|
|
619
|
+
**Example:**
|
|
620
|
+
```typescript
|
|
621
|
+
import { useCallback } from 'react';
|
|
622
|
+
import usePressTracing from '@atlaskit/react-ufo/use-press-tracing';
|
|
623
|
+
|
|
624
|
+
function CustomButton({ onAction }) {
|
|
625
|
+
const traceInteraction = usePressTracing('custom-action');
|
|
626
|
+
|
|
627
|
+
const handleClick = useCallback((event) => {
|
|
628
|
+
traceInteraction(event.timeStamp);
|
|
629
|
+
onAction();
|
|
630
|
+
}, [traceInteraction, onAction]);
|
|
631
|
+
|
|
632
|
+
return <button onClick={handleClick}>Custom Action</button>;
|
|
633
|
+
}
|
|
634
|
+
```
|
|
635
|
+
|
|
636
|
+
**Parameters:**
|
|
637
|
+
- `name` (string): The UFO interaction name
|
|
638
|
+
|
|
639
|
+
**Returns:**
|
|
640
|
+
- `traceInteraction` (function): Function to call when interaction starts
|
|
641
|
+
- `timeStamp` (number, optional): Custom timestamp
|
|
642
|
+
|
|
643
|
+
**Note:** Results are sent as `inline-result` type events.
|
|
644
|
+
|
|
645
|
+
### useUFOTypingPerformanceTracing ⚠️ Experimental
|
|
646
|
+
|
|
647
|
+
**Note: This is an experimental feature that may change or be removed in future versions.**
|
|
648
|
+
|
|
649
|
+
Measures typing latency in input fields to track user experience during text input.
|
|
650
|
+
|
|
651
|
+
**Import:**
|
|
652
|
+
```typescript
|
|
653
|
+
import useUFOTypingPerformanceTracing from '@atlaskit/react-ufo/typing-performance-tracing';
|
|
654
|
+
```
|
|
655
|
+
|
|
656
|
+
**Usage:**
|
|
657
|
+
```typescript
|
|
658
|
+
const typingRef = useUFOTypingPerformanceTracing<HTMLInputElement>('text-input-typing');
|
|
659
|
+
```
|
|
660
|
+
|
|
661
|
+
**Example:**
|
|
662
|
+
```typescript
|
|
663
|
+
import useUFOTypingPerformanceTracing from '@atlaskit/react-ufo/typing-performance-tracing';
|
|
664
|
+
|
|
665
|
+
function CommentEditor() {
|
|
666
|
+
const typingRef = useUFOTypingPerformanceTracing<HTMLTextAreaElement>(
|
|
667
|
+
'comment-editor-typing'
|
|
668
|
+
);
|
|
669
|
+
|
|
670
|
+
return (
|
|
671
|
+
<textarea
|
|
672
|
+
ref={typingRef}
|
|
673
|
+
placeholder="Write a comment..."
|
|
674
|
+
/>
|
|
675
|
+
);
|
|
676
|
+
}
|
|
677
|
+
```
|
|
678
|
+
|
|
679
|
+
**Metrics Captured:**
|
|
680
|
+
- Minimum typing latency
|
|
681
|
+
- Maximum typing latency
|
|
682
|
+
- Average typing latency
|
|
683
|
+
- Key press count
|
|
684
|
+
- Count of key presses under 50ms
|
|
685
|
+
|
|
686
|
+
**Notes:**
|
|
687
|
+
- Starts measuring on first keypress
|
|
688
|
+
- Sends metrics after 2 seconds of inactivity
|
|
689
|
+
- Only character-producing keys are measured (not backspace, etc.)
|
|
690
|
+
|
|
691
|
+
### useUFOTransitionCompleter
|
|
692
|
+
|
|
693
|
+
Automatically completes UFO transitions when the component mounts. Use this in destination route components.
|
|
694
|
+
|
|
695
|
+
**Import:**
|
|
696
|
+
```typescript
|
|
697
|
+
import { useUFOTransitionCompleter } from '@atlaskit/react-ufo/trace-transition';
|
|
698
|
+
```
|
|
699
|
+
|
|
700
|
+
**Usage:**
|
|
701
|
+
```typescript
|
|
702
|
+
function MyPageComponent() {
|
|
703
|
+
useUFOTransitionCompleter();
|
|
704
|
+
|
|
705
|
+
return <div>Page content</div>;
|
|
706
|
+
}
|
|
707
|
+
```
|
|
708
|
+
|
|
709
|
+
**Example:**
|
|
710
|
+
```typescript
|
|
711
|
+
import { useUFOTransitionCompleter } from '@atlaskit/react-ufo/trace-transition';
|
|
712
|
+
|
|
713
|
+
function IssueViewPage() {
|
|
714
|
+
// This will complete any active transition when the page renders
|
|
715
|
+
useUFOTransitionCompleter();
|
|
716
|
+
|
|
717
|
+
return (
|
|
718
|
+
<div>
|
|
719
|
+
<IssueHeader />
|
|
720
|
+
<IssueContent />
|
|
721
|
+
</div>
|
|
722
|
+
);
|
|
723
|
+
}
|
|
724
|
+
```
|
|
725
|
+
|
|
726
|
+
### useUFOReportError
|
|
727
|
+
|
|
728
|
+
A hook that provides a function to report errors within UFO interactions. This allows you to capture and track errors that occur during performance-critical user flows.
|
|
729
|
+
|
|
730
|
+
**Import:**
|
|
731
|
+
```typescript
|
|
732
|
+
import { useUFOReportError } from '@atlaskit/react-ufo/report-error';
|
|
733
|
+
```
|
|
734
|
+
|
|
735
|
+
**Usage:**
|
|
736
|
+
```typescript
|
|
737
|
+
const reportError = useUFOReportError();
|
|
738
|
+
```
|
|
739
|
+
|
|
740
|
+
**Parameters:**
|
|
741
|
+
- None
|
|
742
|
+
|
|
743
|
+
**Returns:**
|
|
744
|
+
- `reportError` (function): Function to report errors to the current UFO interaction
|
|
745
|
+
- `error` (object): Error information with the following properties:
|
|
746
|
+
- `name` (string): Error name/type
|
|
747
|
+
- `errorMessage` (string): Error description
|
|
748
|
+
- `errorStack` (string, optional): Error stack trace
|
|
749
|
+
- `forcedError` (boolean, optional): Whether this was intentionally triggered
|
|
750
|
+
- `errorHash` (string, optional): Custom error identifier
|
|
751
|
+
- `errorStatusCode` (number, optional): HTTP status code if applicable
|
|
752
|
+
|
|
753
|
+
**Example:**
|
|
754
|
+
```typescript
|
|
755
|
+
import { useUFOReportError } from '@atlaskit/react-ufo/report-error';
|
|
756
|
+
|
|
757
|
+
function DataLoader({ issueId }) {
|
|
758
|
+
const reportError = useUFOReportError();
|
|
759
|
+
|
|
760
|
+
const loadIssueData = async () => {
|
|
761
|
+
try {
|
|
762
|
+
const issue = await fetchIssue(issueId);
|
|
763
|
+
return issue;
|
|
764
|
+
} catch (error) {
|
|
765
|
+
reportError({
|
|
766
|
+
name: 'IssueLoadError',
|
|
767
|
+
errorMessage: error.message,
|
|
768
|
+
errorStack: error.stack,
|
|
769
|
+
errorStatusCode: error.status
|
|
770
|
+
});
|
|
771
|
+
throw error; // Re-throw for component error handling
|
|
772
|
+
}
|
|
773
|
+
};
|
|
774
|
+
|
|
775
|
+
// Component logic...
|
|
776
|
+
}
|
|
777
|
+
```
|
|
778
|
+
|
|
779
|
+
**Advanced Example with Context:**
|
|
780
|
+
```typescript
|
|
781
|
+
import { useUFOReportError } from '@atlaskit/react-ufo/report-error';
|
|
782
|
+
|
|
783
|
+
function SearchComponent() {
|
|
784
|
+
const reportError = useUFOReportError();
|
|
785
|
+
|
|
786
|
+
const handleSearchError = (error, searchContext) => {
|
|
787
|
+
reportError({
|
|
788
|
+
name: 'SearchAPIError',
|
|
789
|
+
errorMessage: `Search failed: ${error.message}`,
|
|
790
|
+
errorStack: error.stack,
|
|
791
|
+
errorStatusCode: error.response?.status,
|
|
792
|
+
// Custom error hash for grouping similar errors
|
|
793
|
+
errorHash: `search-${searchContext.query}-${error.type}`
|
|
794
|
+
});
|
|
795
|
+
};
|
|
796
|
+
|
|
797
|
+
return (
|
|
798
|
+
<div>
|
|
799
|
+
{/* Search UI */}
|
|
800
|
+
</div>
|
|
801
|
+
);
|
|
802
|
+
}
|
|
803
|
+
```
|
|
804
|
+
|
|
805
|
+
**Notes:**
|
|
806
|
+
- Errors are automatically associated with the currently active UFO interaction
|
|
807
|
+
- If no interaction is active, errors are added to all active interactions
|
|
808
|
+
- Use meaningful error names for better categorization in analytics
|
|
809
|
+
- Include stack traces when possible for debugging
|
|
810
|
+
|
|
811
|
+
---
|
|
812
|
+
|
|
813
|
+
## Utility Functions
|
|
814
|
+
|
|
815
|
+
### addUFOCustomData
|
|
816
|
+
|
|
817
|
+
Programmatically adds custom data to the current UFO interaction without using a component.
|
|
818
|
+
|
|
819
|
+
**Import:**
|
|
820
|
+
```typescript
|
|
821
|
+
import { addUFOCustomData } from '@atlaskit/react-ufo/custom-data';
|
|
822
|
+
```
|
|
823
|
+
|
|
824
|
+
**Usage:**
|
|
825
|
+
```typescript
|
|
826
|
+
addUFOCustomData({ key: 'value', count: 42 });
|
|
827
|
+
```
|
|
828
|
+
|
|
829
|
+
**Example:**
|
|
830
|
+
```typescript
|
|
831
|
+
import { addUFOCustomData } from '@atlaskit/react-ufo/custom-data';
|
|
832
|
+
|
|
833
|
+
function performSearch(query) {
|
|
834
|
+
addUFOCustomData({
|
|
835
|
+
searchQuery: query,
|
|
836
|
+
searchType: 'advanced',
|
|
837
|
+
hasFilters: filters.length > 0
|
|
838
|
+
});
|
|
839
|
+
|
|
840
|
+
return searchAPI(query);
|
|
841
|
+
}
|
|
842
|
+
```
|
|
843
|
+
|
|
844
|
+
### addUFOCustomCohortData ⚠️ Experimental
|
|
845
|
+
|
|
846
|
+
**Note: This is an experimental feature that may change or be removed in future versions.**
|
|
847
|
+
|
|
848
|
+
Programmatically adds cohort data for A/B testing and feature flag tracking.
|
|
849
|
+
|
|
850
|
+
**Import:**
|
|
851
|
+
```typescript
|
|
852
|
+
import { addUFOCustomCohortData } from '@atlaskit/react-ufo/custom-cohort-data';
|
|
853
|
+
```
|
|
854
|
+
|
|
855
|
+
**Usage:**
|
|
856
|
+
```typescript
|
|
857
|
+
addUFOCustomCohortData('feature_flag_key', true);
|
|
858
|
+
```
|
|
859
|
+
|
|
860
|
+
**Example:**
|
|
861
|
+
```typescript
|
|
862
|
+
import { addUFOCustomCohortData } from '@atlaskit/react-ufo/custom-cohort-data';
|
|
863
|
+
import { fg } from '@atlassian/jira-feature-gating';
|
|
864
|
+
|
|
865
|
+
function initializeExperiment() {
|
|
866
|
+
const isNewDesignEnabled = fg('new_design_system');
|
|
867
|
+
|
|
868
|
+
addUFOCustomCohortData('design_system_version', isNewDesignEnabled ? 'v2' : 'v1');
|
|
869
|
+
addUFOCustomCohortData('user_segment', getUserSegment());
|
|
870
|
+
}
|
|
871
|
+
```
|
|
872
|
+
|
|
873
|
+
### addFeatureFlagAccessed
|
|
874
|
+
|
|
875
|
+
Records feature flag usage for correlation with performance metrics.
|
|
876
|
+
|
|
877
|
+
**Import:**
|
|
878
|
+
```typescript
|
|
879
|
+
import { addFeatureFlagAccessed } from '@atlaskit/react-ufo/feature-flags-accessed';
|
|
880
|
+
```
|
|
881
|
+
|
|
882
|
+
**Usage:**
|
|
883
|
+
```typescript
|
|
884
|
+
addFeatureFlagAccessed('feature_flag_name', flagValue);
|
|
885
|
+
```
|
|
886
|
+
|
|
887
|
+
**Example:**
|
|
888
|
+
```typescript
|
|
889
|
+
import { addFeatureFlagAccessed } from '@atlaskit/react-ufo/feature-flags-accessed';
|
|
890
|
+
import { fg } from '@atlassian/jira-feature-gating';
|
|
891
|
+
|
|
892
|
+
function checkFeatureFlag(flagKey) {
|
|
893
|
+
const flagValue = fg(flagKey);
|
|
894
|
+
|
|
895
|
+
// Record this flag access for performance correlation
|
|
896
|
+
if (shouldRecordFeatureFlagForUFO(flagKey)) {
|
|
897
|
+
addFeatureFlagAccessed(flagKey, flagValue);
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
return flagValue;
|
|
901
|
+
}
|
|
902
|
+
```
|
|
903
|
+
|
|
904
|
+
**Note:** Use cautiously as recording all feature flags can significantly increase payload size.
|
|
905
|
+
|
|
906
|
+
### setInteractionError
|
|
907
|
+
|
|
908
|
+
Manually records an error for the current UFO interaction.
|
|
909
|
+
|
|
910
|
+
**Import:**
|
|
911
|
+
```typescript
|
|
912
|
+
import { setInteractionError } from '@atlaskit/react-ufo/set-interaction-error';
|
|
913
|
+
```
|
|
914
|
+
|
|
915
|
+
**Usage:**
|
|
916
|
+
```typescript
|
|
917
|
+
setInteractionError('interaction-name', {
|
|
918
|
+
errorMessage: 'Something went wrong',
|
|
919
|
+
name: 'CustomError'
|
|
920
|
+
});
|
|
921
|
+
```
|
|
922
|
+
|
|
923
|
+
**Example:**
|
|
924
|
+
```typescript
|
|
925
|
+
import { setInteractionError } from '@atlaskit/react-ufo/set-interaction-error';
|
|
926
|
+
|
|
927
|
+
function loadIssueData(issueId) {
|
|
928
|
+
try {
|
|
929
|
+
return loadIssue(issueId);
|
|
930
|
+
} catch (error) {
|
|
931
|
+
setInteractionError('issue-view', {
|
|
932
|
+
errorMessage: error.message,
|
|
933
|
+
name: 'IssueLoadError'
|
|
934
|
+
});
|
|
935
|
+
throw error;
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
```
|
|
939
|
+
|
|
940
|
+
### getUFORouteName
|
|
941
|
+
|
|
942
|
+
Extracts the UFO name from a route configuration object.
|
|
943
|
+
|
|
944
|
+
**Import:**
|
|
945
|
+
```typescript
|
|
946
|
+
import getUFORouteName from '@atlaskit/react-ufo/route-name';
|
|
947
|
+
```
|
|
948
|
+
|
|
949
|
+
**Usage:**
|
|
950
|
+
```typescript
|
|
951
|
+
const ufoName = getUFORouteName(route);
|
|
952
|
+
```
|
|
953
|
+
|
|
954
|
+
**Example:**
|
|
955
|
+
```typescript
|
|
956
|
+
import getUFORouteName from '@atlaskit/react-ufo/route-name';
|
|
957
|
+
import { useRouter } from '@atlassian/react-resource-router';
|
|
958
|
+
|
|
959
|
+
function RouteAnalytics() {
|
|
960
|
+
const [routerState] = useRouter();
|
|
961
|
+
const ufoName = getUFORouteName(routerState.route);
|
|
962
|
+
|
|
963
|
+
useEffect(() => {
|
|
964
|
+
if (ufoName) {
|
|
965
|
+
console.log('Current UFO route:', ufoName);
|
|
966
|
+
}
|
|
967
|
+
}, [ufoName]);
|
|
968
|
+
|
|
969
|
+
return null;
|
|
970
|
+
}
|
|
971
|
+
```
|
|
972
|
+
|
|
973
|
+
---
|
|
974
|
+
|
|
975
|
+
## Component Integration
|
|
976
|
+
|
|
977
|
+
### interactionName Property
|
|
978
|
+
|
|
979
|
+
Many Atlassian Design System components support an `interactionName` prop that automatically adds UFO tracking.
|
|
980
|
+
|
|
981
|
+
**Supported Components:**
|
|
982
|
+
|
|
983
|
+
**@atlaskit/button:**
|
|
984
|
+
```typescript
|
|
985
|
+
import Button from '@atlaskit/button';
|
|
986
|
+
|
|
987
|
+
<Button interactionName="save-issue">Save</Button>
|
|
988
|
+
```
|
|
989
|
+
|
|
990
|
+
**@atlaskit/spinner:**
|
|
991
|
+
```typescript
|
|
992
|
+
import Spinner from '@atlaskit/spinner';
|
|
993
|
+
|
|
994
|
+
<Spinner interactionName="loading-comments" size="medium" />
|
|
995
|
+
```
|
|
996
|
+
|
|
997
|
+
**Examples:**
|
|
998
|
+
```typescript
|
|
999
|
+
// Button with interaction tracking
|
|
1000
|
+
<Button
|
|
1001
|
+
interactionName="create-issue"
|
|
1002
|
+
onClick={handleCreateIssue}
|
|
1003
|
+
>
|
|
1004
|
+
Create Issue
|
|
1005
|
+
</Button>
|
|
1006
|
+
|
|
1007
|
+
// Spinner that holds interaction until unmounted
|
|
1008
|
+
{isLoading && (
|
|
1009
|
+
<Spinner
|
|
1010
|
+
interactionName="issue-loading"
|
|
1011
|
+
size="large"
|
|
1012
|
+
/>
|
|
1013
|
+
)}
|
|
1014
|
+
```
|
|
1015
|
+
|
|
1016
|
+
**Notes:**
|
|
1017
|
+
- Button: Triggers a UFO press interaction on click
|
|
1018
|
+
- Spinner: Adds a UFO hold while the spinner is mounted
|
|
1019
|
+
- Use descriptive, unique names for each interaction
|
|
1020
|
+
|
|
1021
|
+
---
|
|
1022
|
+
|
|
1023
|
+
## Advanced APIs
|
|
1024
|
+
|
|
1025
|
+
### SSR Configuration
|
|
1026
|
+
|
|
1027
|
+
Configure UFO for Server-Side Rendering scenarios. This will produce a more accurate FMP Topline Metric in Glance.
|
|
1028
|
+
|
|
1029
|
+
**Import:**
|
|
1030
|
+
```typescript
|
|
1031
|
+
import { configure } from '@atlaskit/react-ufo/ssr';
|
|
1032
|
+
```
|
|
1033
|
+
|
|
1034
|
+
**Usage:**
|
|
1035
|
+
```typescript
|
|
1036
|
+
configure({
|
|
1037
|
+
getDoneMark: () => performance.now(),
|
|
1038
|
+
getFeatureFlags: () => ({ flag1: true, flag2: false }),
|
|
1039
|
+
getTimings: () => ({
|
|
1040
|
+
total: { startTime: 0, duration: 1200 },
|
|
1041
|
+
render: { startTime: 100, duration: 800 }
|
|
1042
|
+
}),
|
|
1043
|
+
getSsrPhaseSuccess: () => ({
|
|
1044
|
+
prefetch: true,
|
|
1045
|
+
earlyFlush: true,
|
|
1046
|
+
done: true
|
|
1047
|
+
})
|
|
1048
|
+
});
|
|
1049
|
+
```
|
|
1050
|
+
|
|
1051
|
+
**Configuration Options:**
|
|
1052
|
+
- `getDoneMark()`: Returns timestamp when SSR rendering completed
|
|
1053
|
+
- `getFeatureFlags()`: Returns feature flags used during SSR
|
|
1054
|
+
- `getTimings()`: Returns detailed SSR timing breakdown
|
|
1055
|
+
- `getSsrPhaseSuccess()`: Returns success status for SSR phases
|
|
1056
|
+
|
|
1057
|
+
### Third-Party Segments
|
|
1058
|
+
|
|
1059
|
+
Special segment type for tracking external or plugin content separately from first-party code.
|
|
1060
|
+
|
|
1061
|
+
**Import:**
|
|
1062
|
+
```typescript
|
|
1063
|
+
import { UFOThirdPartySegment } from '@atlaskit/react-ufo/segment';
|
|
1064
|
+
```
|
|
1065
|
+
|
|
1066
|
+
**Usage:**
|
|
1067
|
+
```typescript
|
|
1068
|
+
<UFOThirdPartySegment name="external-calendar-widget">
|
|
1069
|
+
<CalendarWidget />
|
|
1070
|
+
</UFOThirdPartySegment>
|
|
1071
|
+
```
|
|
1072
|
+
|
|
1073
|
+
**Use Cases:**
|
|
1074
|
+
- External service integrations
|
|
1075
|
+
- Third-party plugins or widgets
|
|
1076
|
+
- Partner-provided components
|
|
1077
|
+
- Any content not directly controlled by your team
|
|
1078
|
+
|
|
1079
|
+
---
|
|
1080
|
+
|
|
1081
|
+
## Best Practices
|
|
1082
|
+
|
|
1083
|
+
### Naming Conventions
|
|
1084
|
+
- Use descriptive, specific names: `issue-header` not `header`
|
|
1085
|
+
- Use kebab-case for consistency
|
|
1086
|
+
- Include context when helpful: `board-issue-card` vs `issue-card`
|
|
1087
|
+
- Avoid dynamic values in names
|
|
1088
|
+
|
|
1089
|
+
### Performance Considerations
|
|
1090
|
+
- Don't over-instrument - focus on key user journeys
|
|
1091
|
+
- Use segments for logical page sections (like header, content, sidebar)
|
|
1092
|
+
- Be selective with custom data to avoid large payloads
|
|
1093
|
+
- Prefer component-level holds over page-level holds for granular tracking
|
|
1094
|
+
- Use meaningful, descriptive names that help identify performance bottlenecks
|
|
1095
|
+
- Consider using UFOThirdPartySegment for external content that may impact performance
|
|
1096
|
+
|
|
1097
|
+
### Debugging
|
|
1098
|
+
- Use browser dev tools to inspect UFO events
|
|
1099
|
+
- Check network tab for UFO payload requests (look for analytics events)
|
|
1100
|
+
- Use UFO labels to identify problematic code areas
|
|
1101
|
+
- Monitor UFO events in your application's analytics dashboard
|
|
1102
|
+
- Enable verbose logging in development to see interaction lifecycle
|
|
1103
|
+
- Use meaningful names for segments and holds to quickly identify issues
|
|
1104
|
+
- Test with feature flags to ensure performance tracking works across variants
|
|
1105
|
+
- Pay attention to "holds" that never release - they prevent interactions from completing
|
|
1106
|
+
|
|
1107
|
+
### Migration Guide
|
|
1108
|
+
- Use `interactionName` props on ADS components when possible
|
|
1109
|
+
- Migrate from manual event tracking to UFO components
|
|
1110
|
+
- Consider SSR implications when adding new instrumentation
|
|
1111
|
+
|
|
1112
|
+
---
|
|
1113
|
+
|
|
1114
|
+
## Common Patterns
|
|
1115
|
+
|
|
1116
|
+
### Page Load Tracking
|
|
1117
|
+
```typescript
|
|
1118
|
+
function MyPage() {
|
|
1119
|
+
useEffect(() => {
|
|
1120
|
+
traceUFOPageLoad('my-page');
|
|
1121
|
+
}, []);
|
|
1122
|
+
|
|
1123
|
+
return (
|
|
1124
|
+
<UFOSegment name="main-content">
|
|
1125
|
+
<PageContent />
|
|
1126
|
+
</UFOSegment>
|
|
1127
|
+
);
|
|
1128
|
+
}
|
|
1129
|
+
```
|
|
1130
|
+
|
|
1131
|
+
### Loading States
|
|
1132
|
+
```typescript
|
|
1133
|
+
function DataComponent() {
|
|
1134
|
+
const [loading, setLoading] = useState(true);
|
|
1135
|
+
|
|
1136
|
+
return (
|
|
1137
|
+
<UFOSegment name="data-section">
|
|
1138
|
+
{loading ? (
|
|
1139
|
+
<UFOLoadHold name="data-loading">
|
|
1140
|
+
<Skeleton />
|
|
1141
|
+
</UFOLoadHold>
|
|
1142
|
+
) : (
|
|
1143
|
+
<DataTable />
|
|
1144
|
+
)}
|
|
1145
|
+
</UFOSegment>
|
|
1146
|
+
);
|
|
1147
|
+
}
|
|
1148
|
+
```
|
|
1149
|
+
|
|
1150
|
+
### Navigation Tracking
|
|
1151
|
+
```typescript
|
|
1152
|
+
function NavigationWrapper() {
|
|
1153
|
+
const [routerState] = useRouter();
|
|
1154
|
+
|
|
1155
|
+
useEffect(() => {
|
|
1156
|
+
const ufoName = getUFORouteName(routerState.route);
|
|
1157
|
+
if (ufoName) {
|
|
1158
|
+
traceUFOTransition(ufoName);
|
|
1159
|
+
}
|
|
1160
|
+
}, [routerState]);
|
|
1161
|
+
|
|
1162
|
+
return <Router />;
|
|
1163
|
+
}
|
|
1164
|
+
```
|
|
1165
|
+
|
|
1166
|
+
## Troubleshooting
|
|
1167
|
+
|
|
1168
|
+
### Common Issues
|
|
1169
|
+
|
|
1170
|
+
**Interaction Never Completes**
|
|
1171
|
+
- Check for holds that are never released (components that mount `UFOLoadHold` but never unmount)
|
|
1172
|
+
- Verify all async operations complete properly
|
|
1173
|
+
- Ensure error boundaries don't prevent holds from releasing
|
|
1174
|
+
|
|
1175
|
+
**Missing Analytics Events**
|
|
1176
|
+
- Verify UFO configuration is properly set up in your application
|
|
1177
|
+
- Check that sampling rates are configured for your interaction names
|
|
1178
|
+
- Ensure you're calling trace functions early in the component lifecycle
|
|
1179
|
+
|
|
1180
|
+
**Performance Impact**
|
|
1181
|
+
- Avoid over-instrumentation - focus on critical user journeys
|
|
1182
|
+
- Use feature flags to control UFO instrumentation in production
|
|
1183
|
+
- Monitor payload sizes if adding extensive custom data
|
|
1184
|
+
|
|
1185
|
+
**SSR Compatibility**
|
|
1186
|
+
- Use the SSR configuration APIs for server-side rendered applications
|
|
1187
|
+
- Ensure UFO components handle server-side rendering gracefully
|
|
1188
|
+
- Test SSR placeholder behavior with your specific setup
|