@atlaskit/analytics-next 11.2.0 → 11.2.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/CHANGELOG.md +14 -0
- package/afm-cc/tsconfig.json +0 -1
- package/afm-products/tsconfig.json +0 -1
- package/dist/cjs/events/UIAnalyticsEvent.js +11 -1
- package/dist/es2019/events/UIAnalyticsEvent.js +13 -1
- package/dist/esm/events/UIAnalyticsEvent.js +11 -1
- package/docs/0-intro.tsx +68 -37
- package/docs/10-concepts.tsx +138 -112
- package/docs/20-usage-with-presentational-components.tsx +177 -198
- package/docs/30-usage-for-container-components.tsx +69 -53
- package/docs/40-listeners.tsx +17 -14
- package/docs/50-error-boundary.tsx +28 -22
- package/docs/60-events.tsx +27 -19
- package/docs/70-advanced-usage.tsx +182 -179
- package/docs/80-upgrade-guide.tsx +182 -100
- package/docs/DocBlocks.tsx +106 -0
- package/package.json +2 -2
- package/afm-jira/tsconfig.json +0 -30
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# @atlaskit/analytics-next
|
|
2
2
|
|
|
3
|
+
## 11.2.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`35a1309b51bea`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/35a1309b51bea) -
|
|
8
|
+
Isolate handler errors in `UIAnalyticsEvent.fire()` so a throwing analytics handler never
|
|
9
|
+
propagates out and crashes the calling product UI.
|
|
10
|
+
|
|
11
|
+
Each handler is now wrapped in a `try/catch`. In non-production environments the error is surfaced
|
|
12
|
+
via `console.error`; in production it is swallowed silently. This prevents analytics from being in
|
|
13
|
+
the critical path of product UI rendering.
|
|
14
|
+
|
|
15
|
+
PIR: PIR-300717 / HOT-301294 (CFIND-6243)
|
|
16
|
+
|
|
3
17
|
## 11.2.0
|
|
4
18
|
|
|
5
19
|
### Minor Changes
|
package/afm-cc/tsconfig.json
CHANGED
|
@@ -61,7 +61,17 @@ var UIAnalyticsEvent = exports.default = /*#__PURE__*/function (_AnalyticsEvent)
|
|
|
61
61
|
return;
|
|
62
62
|
}
|
|
63
63
|
_this.handlers.forEach(function (handler) {
|
|
64
|
-
|
|
64
|
+
try {
|
|
65
|
+
handler(_this, channel);
|
|
66
|
+
} catch (e) {
|
|
67
|
+
// Analytics must never crash product UI. Swallow handler errors in
|
|
68
|
+
// production; surface them in development so misconfigured events are
|
|
69
|
+
// caught early.
|
|
70
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
71
|
+
// eslint-disable-next-line no-console
|
|
72
|
+
console.error('[analytics-next] UIAnalyticsEvent handler threw an error:', e);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
65
75
|
});
|
|
66
76
|
_this.hasFired = true;
|
|
67
77
|
});
|
|
@@ -42,7 +42,19 @@ export default class UIAnalyticsEvent extends AnalyticsEvent {
|
|
|
42
42
|
}
|
|
43
43
|
return;
|
|
44
44
|
}
|
|
45
|
-
this.handlers.forEach(handler =>
|
|
45
|
+
this.handlers.forEach(handler => {
|
|
46
|
+
try {
|
|
47
|
+
handler(this, channel);
|
|
48
|
+
} catch (e) {
|
|
49
|
+
// Analytics must never crash product UI. Swallow handler errors in
|
|
50
|
+
// production; surface them in development so misconfigured events are
|
|
51
|
+
// caught early.
|
|
52
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
53
|
+
// eslint-disable-next-line no-console
|
|
54
|
+
console.error('[analytics-next] UIAnalyticsEvent handler threw an error:', e);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
});
|
|
46
58
|
this.hasFired = true;
|
|
47
59
|
});
|
|
48
60
|
this.context = props.context || [];
|
|
@@ -55,7 +55,17 @@ var UIAnalyticsEvent = /*#__PURE__*/function (_AnalyticsEvent) {
|
|
|
55
55
|
return;
|
|
56
56
|
}
|
|
57
57
|
_this.handlers.forEach(function (handler) {
|
|
58
|
-
|
|
58
|
+
try {
|
|
59
|
+
handler(_this, channel);
|
|
60
|
+
} catch (e) {
|
|
61
|
+
// Analytics must never crash product UI. Swallow handler errors in
|
|
62
|
+
// production; surface them in development so misconfigured events are
|
|
63
|
+
// caught early.
|
|
64
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
65
|
+
// eslint-disable-next-line no-console
|
|
66
|
+
console.error('[analytics-next] UIAnalyticsEvent handler threw an error:', e);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
59
69
|
});
|
|
60
70
|
_this.hasFired = true;
|
|
61
71
|
});
|
package/docs/0-intro.tsx
CHANGED
|
@@ -1,42 +1,73 @@
|
|
|
1
|
+
/* eslint-disable @atlaskit/design-system/ensure-design-token-usage, @atlaskit/ui-styling-standard/enforce-style-prop, @atlaskit/design-system/use-primitives-text, @atlaskit/design-system/use-heading, @atlaskit/design-system/no-html-anchor -- Legacy analytics-next docs intentionally use plain HTML prose, links, and inline styles after the docs helper cleanup. */
|
|
1
2
|
import React from 'react';
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
const warningStyles: React.CSSProperties = {
|
|
5
|
+
backgroundColor: '#fffae6',
|
|
6
|
+
border: '1px solid #f5cd47',
|
|
7
|
+
borderRadius: 3,
|
|
8
|
+
marginBottom: 16,
|
|
9
|
+
padding: 16,
|
|
10
|
+
};
|
|
6
11
|
|
|
7
|
-
|
|
12
|
+
export default function Intro(): React.JSX.Element {
|
|
13
|
+
return (
|
|
14
|
+
<div>
|
|
15
|
+
<div style={warningStyles}>
|
|
16
|
+
<strong>Maintaince Mode: @atlaskit/analytics is in maintenance mode.</strong>
|
|
17
|
+
<p>
|
|
18
|
+
This package is officially in maintenance mode, which means only bugfixes or VULN fixes
|
|
19
|
+
are currently being accepted and no known breaking changes will be approved in the PR
|
|
20
|
+
process.
|
|
21
|
+
</p>
|
|
22
|
+
<p>
|
|
23
|
+
Please refer to this{' '}
|
|
24
|
+
<a
|
|
25
|
+
href="https://hello.atlassian.net/wiki/spaces/APD/pages/2470435075/DACI+analytics-next+in+a+maintenance+mode"
|
|
26
|
+
target="_blank"
|
|
27
|
+
rel="noreferrer"
|
|
28
|
+
>
|
|
29
|
+
DACI
|
|
30
|
+
</a>{' '}
|
|
31
|
+
for more details.
|
|
32
|
+
</p>
|
|
33
|
+
</div>
|
|
8
34
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
>
|
|
14
|
-
This package is officially in maintenance mode, which means only bugfixes or VULN fixes are
|
|
15
|
-
currently being accepted and no known breaking changes will be approved in the PR process.{' '}
|
|
16
|
-
<br />
|
|
17
|
-
Please refer to this
|
|
18
|
-
<Link
|
|
19
|
-
href="https://hello.atlassian.net/wiki/spaces/APD/pages/2470435075/DACI+analytics-next+in+a+maintenance+mode"
|
|
20
|
-
target="_blank"
|
|
21
|
-
>
|
|
22
|
-
{' '}
|
|
23
|
-
DACI{' '}
|
|
24
|
-
</Link>{' '}
|
|
25
|
-
for more details.
|
|
26
|
-
</SectionMessage>
|
|
27
|
-
)}
|
|
35
|
+
<p>
|
|
36
|
+
This package aims to help assist consumers track the way their React components are being
|
|
37
|
+
used.
|
|
38
|
+
</p>
|
|
28
39
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
<h3>Contents</h3>
|
|
41
|
+
<ul>
|
|
42
|
+
<li>
|
|
43
|
+
<a href="./analytics-next/docs/concepts">Concepts</a> (Read first!)
|
|
44
|
+
</li>
|
|
45
|
+
<li>
|
|
46
|
+
<a href="./analytics-next/docs/usage-with-presentational-components">
|
|
47
|
+
Usage with presentational components
|
|
48
|
+
</a>
|
|
49
|
+
</li>
|
|
50
|
+
<li>
|
|
51
|
+
<a href="./analytics-next/docs/usage-for-container-components">
|
|
52
|
+
Usage with container components
|
|
53
|
+
</a>
|
|
54
|
+
</li>
|
|
55
|
+
<li>
|
|
56
|
+
<a href="./analytics-next/docs/listeners">Listeners</a>
|
|
57
|
+
</li>
|
|
58
|
+
<li>
|
|
59
|
+
<a href="./analytics-next/docs/error-boundary">Error Boundary</a>
|
|
60
|
+
</li>
|
|
61
|
+
<li>
|
|
62
|
+
<a href="./analytics-next/docs/events">Events</a>
|
|
63
|
+
</li>
|
|
64
|
+
<li>
|
|
65
|
+
<a href="./analytics-next/docs/advanced-usage">Advanced usage</a>
|
|
66
|
+
</li>
|
|
67
|
+
<li>
|
|
68
|
+
<a href="./analytics-next/docs/upgrade-guide">Ugprade guide</a>
|
|
69
|
+
</li>
|
|
70
|
+
</ul>
|
|
71
|
+
</div>
|
|
72
|
+
);
|
|
73
|
+
}
|
package/docs/10-concepts.tsx
CHANGED
|
@@ -1,122 +1,148 @@
|
|
|
1
|
-
|
|
1
|
+
/* eslint-disable @atlaskit/design-system/use-primitives-text, @atlaskit/design-system/use-heading, @atlaskit/design-system/no-html-anchor -- Legacy analytics-next docs intentionally use plain HTML prose and links instead of ADS docs primitives. */
|
|
2
|
+
import React from 'react';
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
There are 3 abstract layers as part of \`@atlaskit/analytics-next\`:
|
|
4
|
+
import { CodeBlock } from './DocBlocks';
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
- [Analytics Contexts](./usage-for-container-components) (provides contextual data to consumer components about the \"container\" they are embeded in. This data is added to the event fired by all consumer components that are its children.)
|
|
6
|
+
const exampleCode = `
|
|
7
|
+
// ---- CONSUMER COMPONENT LAYER ----
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
#### Phase I (version 7.1.0)
|
|
19
|
-
|
|
20
|
-
Analytics consuming components receive only modern context. Listeners and the Context layer will provide both modern and legacy context by default.
|
|
21
|
-
|
|
22
|
-
At their own risk, package consumers can opt in to no longer supply legacy context by using the environment variable
|
|
23
|
-
ANALYTICS_NEXT_MODERN_CONTEXT=true.
|
|
24
|
-
|
|
25
|
-
When doing so, any analytics consumers that rely on legacy context will not receive any, and events may be lost! This would happen when using old atlaskit packages that consume a version of @atlaskit/analytics-next before version 7.1.0.
|
|
26
|
-
|
|
27
|
-
#### Phase II (future major)
|
|
28
|
-
|
|
29
|
-
In a future release (TBA) we will remove all legacy context support and clean up the branching around ANALYTICS_NEXT_MODERN_CONTEXT.
|
|
30
|
-
After this point, @atlaskit/analytics-next will not work with components that use a version prior to 7.1.0.
|
|
31
|
-
|
|
32
|
-
### Example
|
|
33
|
-
|
|
34
|
-
${code`
|
|
35
|
-
// ---- CONSUMER COMPONENT LAYER ----
|
|
9
|
+
// We want to add tracking for the 'click' of this button
|
|
10
|
+
const TargetButton = ({ onClick }) => {
|
|
11
|
+
// this analytics event is given to the callback via the
|
|
12
|
+
// HOC wrapping of this component below
|
|
13
|
+
const handler = (clickEvent, analyticsEvent) => {
|
|
14
|
+
analyticsEvent.fire("myTargetChannel");
|
|
15
|
+
};
|
|
36
16
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
// this analytics event is given to the callback via the
|
|
40
|
-
// HOC wrapping of this component below
|
|
41
|
-
const handler = (clickEvent, analyticsEvent) => {
|
|
42
|
-
analyticsEvent.fire("myTargetChannel");
|
|
43
|
-
};
|
|
17
|
+
return <button onClick={handler}>Track me</button>
|
|
18
|
+
}
|
|
44
19
|
|
|
45
|
-
|
|
20
|
+
// Analytics can be baked into your target components
|
|
21
|
+
// in a number of ways, this is just one of many!
|
|
22
|
+
const AnalyticsWrappedButton = withAnalyticsEvent({
|
|
23
|
+
onClick: {
|
|
24
|
+
component: 'button',
|
|
25
|
+
action: 'clicked'
|
|
46
26
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
27
|
+
})(TargetButton);
|
|
28
|
+
|
|
29
|
+
// ---- ANALYTICS CONTEXT LAYER ----
|
|
30
|
+
|
|
31
|
+
// Our target button is going to be used inside of a form
|
|
32
|
+
const FormContainer = ({ children }) => {
|
|
33
|
+
return (
|
|
34
|
+
<form>
|
|
35
|
+
{children}
|
|
36
|
+
</form>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// This wrapper gives the container a way of passing to its children
|
|
41
|
+
// components some context about where it is consumed.
|
|
42
|
+
// Again there are a few ways of doing this!
|
|
43
|
+
const AnalyticsWrappedForm = withAnayticsContext({
|
|
44
|
+
container: 'submissionForm',
|
|
45
|
+
page: '/invite-a-friend'
|
|
46
|
+
})(FormContainer);
|
|
47
|
+
|
|
48
|
+
// ---- ANALYTICS LISTENER LAYER ----
|
|
49
|
+
|
|
50
|
+
// This is our App's listener
|
|
51
|
+
// Note: It is possible to have multiple listeners, all listening
|
|
52
|
+
// on different channels, allowing for different handlers for different
|
|
53
|
+
// kind of events
|
|
54
|
+
const OurAnalyticsListener = ({ children }) => {
|
|
55
|
+
const onEvent = (event, channel) => {
|
|
56
|
+
// these components are agnostic to what you want to use
|
|
57
|
+
// to send events to the network
|
|
58
|
+
someClient.sendToNetwork(event);
|
|
59
|
+
|
|
60
|
+
expect(event.payload).toBe({
|
|
53
61
|
action: 'clicked'
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
<form>
|
|
63
|
-
{children}
|
|
64
|
-
</form>
|
|
65
|
-
);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// This wrapper gives the container a way of passing to its children
|
|
69
|
-
// components some context about where it is consumed.
|
|
70
|
-
// Again there are a few ways of doing this!
|
|
71
|
-
const AnalyticsWrappedForm = withAnayticsContext({
|
|
72
|
-
container: 'submissionForm',
|
|
73
|
-
page: '/invite-a-friend'
|
|
74
|
-
})(FormContainer);
|
|
75
|
-
|
|
76
|
-
// ---- ANALYTICS LISTENER LAYER ----
|
|
77
|
-
|
|
78
|
-
// This is our App's listener
|
|
79
|
-
// Note: It is possible to have multiple listeners, all listening
|
|
80
|
-
// on different channels, allowing for different handlers for different
|
|
81
|
-
// kind of events
|
|
82
|
-
const OurAnalyticsListener = ({ children }) => {
|
|
83
|
-
const onEvent = (event, channel) => {
|
|
84
|
-
// these components are agnostic to what you want to use
|
|
85
|
-
// to send events to the network
|
|
86
|
-
someClient.sendToNetwork(event);
|
|
87
|
-
|
|
88
|
-
expect(event.payload).toBe({
|
|
89
|
-
action: 'clicked'
|
|
90
|
-
component: 'button'
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
// an array of contexts (multiple context containers can be nested)
|
|
94
|
-
expect(event.context).toBe([{
|
|
95
|
-
container: 'submissionForm',
|
|
96
|
-
page: '/invite-a-friend'
|
|
97
|
-
});
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
return (
|
|
101
|
-
<AnalyticsListener channel="myTargetChannel" onEvent={onEvent}>
|
|
102
|
-
{children}
|
|
103
|
-
</AnalyticsListener>
|
|
104
|
-
)
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// ---- ALL TOGETHER ----
|
|
108
|
-
|
|
109
|
-
export const ExampleApp = () => {
|
|
110
|
-
return (
|
|
111
|
-
<OurAnalyticsListener> // Listener layer in this example
|
|
112
|
-
<AnalyticsWrappedForm> // Context layer in this example
|
|
113
|
-
<AnalyticsWrappedButton/> // Consumer component layer in this example
|
|
114
|
-
</AnalyticsWrappedForm>
|
|
115
|
-
</OurAnalyticsListener>);
|
|
62
|
+
component: 'button'
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// an array of contexts (multiple context containers can be nested)
|
|
66
|
+
expect(event.context).toBe([{
|
|
67
|
+
container: 'submissionForm',
|
|
68
|
+
page: '/invite-a-friend'
|
|
69
|
+
});
|
|
116
70
|
};
|
|
117
|
-
`}
|
|
118
71
|
|
|
119
|
-
|
|
120
|
-
|
|
72
|
+
return (
|
|
73
|
+
<AnalyticsListener channel="myTargetChannel" onEvent={onEvent}>
|
|
74
|
+
{children}
|
|
75
|
+
</AnalyticsListener>
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ---- ALL TOGETHER ----
|
|
80
|
+
|
|
81
|
+
export const ExampleApp = () => {
|
|
82
|
+
return (
|
|
83
|
+
<OurAnalyticsListener>
|
|
84
|
+
<AnalyticsWrappedForm>
|
|
85
|
+
<AnalyticsWrappedButton/>
|
|
86
|
+
</AnalyticsWrappedForm>
|
|
87
|
+
</OurAnalyticsListener>);
|
|
88
|
+
};
|
|
121
89
|
`;
|
|
122
|
-
|
|
90
|
+
|
|
91
|
+
export default function Concepts(): React.JSX.Element {
|
|
92
|
+
return (
|
|
93
|
+
<div>
|
|
94
|
+
<p>
|
|
95
|
+
There are 3 abstract layers as part of <code>@atlaskit/analytics-next</code>:
|
|
96
|
+
</p>
|
|
97
|
+
<ul>
|
|
98
|
+
<li>
|
|
99
|
+
<a href="./listeners">Analytics Listeners</a> (responsible for sending events over the
|
|
100
|
+
network)
|
|
101
|
+
</li>
|
|
102
|
+
<li>
|
|
103
|
+
<a href="./usage-with-presentational-components">Consumer Components</a> (target
|
|
104
|
+
components that are desired to be tracked. They fire events that the listener consumes)
|
|
105
|
+
</li>
|
|
106
|
+
<li>
|
|
107
|
+
<a href="./usage-for-container-components">Analytics Contexts</a> (provides contextual
|
|
108
|
+
data to consumer components about where it is embeded in the "container" they
|
|
109
|
+
are embeded in. This data is added to the event fired by all consumer components that are
|
|
110
|
+
its children.)
|
|
111
|
+
</li>
|
|
112
|
+
</ul>
|
|
113
|
+
|
|
114
|
+
<h3>React Context: !important;</h3>
|
|
115
|
+
<p>
|
|
116
|
+
These layers communicate via React context. Historically this package has used{' '}
|
|
117
|
+
<a href="https://reactjs.org/docs/legacy-context.html">legacy React Context</a>, but is now
|
|
118
|
+
transitioning to use only the new{' '}
|
|
119
|
+
<a href="https://reactjs.org/docs/context.html">React Context API</a>. This is to prepare
|
|
120
|
+
for future versions of React in which legacy Context will be dropped, but also partially for
|
|
121
|
+
performance reasons.
|
|
122
|
+
</p>
|
|
123
|
+
<p>To achieve dropping legacy context we are rolling the drop out in 2 phases:</p>
|
|
124
|
+
<h4>Phase I (version 7.1.0)</h4>
|
|
125
|
+
<p>
|
|
126
|
+
Analytics consuming components receive only modern context. Listeners and the Context layer
|
|
127
|
+
will provide both modern and legacy context by default.
|
|
128
|
+
</p>
|
|
129
|
+
<p>
|
|
130
|
+
At their own risk, package consumers can opt in to no longer supply legacy context by using
|
|
131
|
+
the environment variable ANALYTICS_NEXT_MODERN_CONTEXT=true.
|
|
132
|
+
</p>
|
|
133
|
+
<p>
|
|
134
|
+
When doing so, any analytics consumers that rely on legacy context will not receive any, and
|
|
135
|
+
events may be lost! This would happen when using old atlaskit packages that consume a
|
|
136
|
+
version of @atlaskit/analytics-next before version 7.1.0.
|
|
137
|
+
</p>
|
|
138
|
+
<h4>Phase II (future major)</h4>
|
|
139
|
+
<p>
|
|
140
|
+
In a future release (TBA) we will remove all legacy context support and clean up the
|
|
141
|
+
branching around ANALYTICS_NEXT_MODERN_CONTEXT. After this point, @atlaskit/analytics-next
|
|
142
|
+
will not work with components that use a version prior to 7.1.0.
|
|
143
|
+
</p>
|
|
144
|
+
<h3>Example</h3>
|
|
145
|
+
<CodeBlock code={exampleCode} />
|
|
146
|
+
</div>
|
|
147
|
+
);
|
|
148
|
+
}
|