@atlaskit/teams-app-internal-analytics 1.0.0
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 +1 -0
- package/LICENSE.md +11 -0
- package/README.md +189 -0
- package/analytics.spec.yaml +42 -0
- package/dist/cjs/common/test-utils/index.js +102 -0
- package/dist/cjs/common/utils/constants.js +7 -0
- package/dist/cjs/common/utils/generated/analytics.types.js +1 -0
- package/dist/cjs/common/utils/generated/create-event-payload.js +45 -0
- package/dist/cjs/common/utils/generated/use-analytics-events.js +31 -0
- package/dist/cjs/index.js +19 -0
- package/dist/cjs/ui/analytics-context/index.js +33 -0
- package/dist/es2019/common/test-utils/index.js +89 -0
- package/dist/es2019/common/utils/constants.js +1 -0
- package/dist/es2019/common/utils/generated/analytics.types.js +0 -0
- package/dist/es2019/common/utils/generated/create-event-payload.js +28 -0
- package/dist/es2019/common/utils/generated/use-analytics-events.js +24 -0
- package/dist/es2019/index.js +2 -0
- package/dist/es2019/ui/analytics-context/index.js +23 -0
- package/dist/esm/common/test-utils/index.js +93 -0
- package/dist/esm/common/utils/constants.js +1 -0
- package/dist/esm/common/utils/generated/analytics.types.js +0 -0
- package/dist/esm/common/utils/generated/create-event-payload.js +38 -0
- package/dist/esm/common/utils/generated/use-analytics-events.js +23 -0
- package/dist/esm/index.js +2 -0
- package/dist/esm/ui/analytics-context/index.js +23 -0
- package/dist/types/common/test-utils/index.d.ts +107 -0
- package/dist/types/common/utils/constants.d.ts +1 -0
- package/dist/types/common/utils/generated/analytics.types.d.ts +39 -0
- package/dist/types/common/utils/generated/create-event-payload.d.ts +27 -0
- package/dist/types/common/utils/generated/use-analytics-events.d.ts +4 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/ui/analytics-context/index.d.ts +12 -0
- package/dist/types-ts4.5/common/test-utils/index.d.ts +107 -0
- package/dist/types-ts4.5/common/utils/constants.d.ts +1 -0
- package/dist/types-ts4.5/common/utils/generated/analytics.types.d.ts +39 -0
- package/dist/types-ts4.5/common/utils/generated/create-event-payload.d.ts +31 -0
- package/dist/types-ts4.5/common/utils/generated/use-analytics-events.d.ts +8 -0
- package/dist/types-ts4.5/index.d.ts +2 -0
- package/dist/types-ts4.5/ui/analytics-context/index.d.ts +12 -0
- package/package.json +102 -0
- package/test-utils/package.json +17 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# @atlaskit/teams-app-internal-analytics
|
package/LICENSE.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
Copyright 2023 Atlassian Pty Ltd
|
|
2
|
+
|
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
|
|
4
|
+
compliance with the License. You may obtain a copy of the License at
|
|
5
|
+
|
|
6
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
7
|
+
|
|
8
|
+
Unless required by applicable law or agreed to in writing, software distributed under the License is
|
|
9
|
+
distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
10
|
+
implied. See the License for the specific language governing permissions and limitations under the
|
|
11
|
+
License.
|
package/README.md
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# Teams App Internal Analytics
|
|
2
|
+
|
|
3
|
+
A comprehensive analytics package for the Teams app and People & Teams platform packages. This
|
|
4
|
+
package provides type-safe analytics event definitions, context providers, and testing utilities for
|
|
5
|
+
tracking user interactions and system behaviors.
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
- [Overview](#overview)
|
|
10
|
+
- [Architecture](#architecture)
|
|
11
|
+
- [Adding New Events](#adding-new-events)
|
|
12
|
+
- [Analytics Context](#analytics-context)
|
|
13
|
+
- [Testing Analytics](#testing-analytics)
|
|
14
|
+
|
|
15
|
+
## Overview
|
|
16
|
+
|
|
17
|
+
This package implements a structured analytics system that is:
|
|
18
|
+
|
|
19
|
+
- **Type-safe**: All events and attributes are strongly typed
|
|
20
|
+
- **Declarative**: Events are defined in YAML specification files
|
|
21
|
+
- **Hierarchical**: Supports nested analytics contexts for rich metadata
|
|
22
|
+
- **Testable**: Comprehensive testing utilities included
|
|
23
|
+
- **Channel-specific**: Events are fired to the dedicated `peopleTeams` analytics channel
|
|
24
|
+
|
|
25
|
+
## Architecture
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
analytics.spec.yaml ← Event definitions (YAML)
|
|
29
|
+
↓
|
|
30
|
+
Code Generation ← Generates TypeScript types & hooks
|
|
31
|
+
↓
|
|
32
|
+
Generated Files:
|
|
33
|
+
├── analytics.types.ts ← Type definitions for events
|
|
34
|
+
├── create-event-payload.ts ← Event payload creation logic
|
|
35
|
+
└── use-analytics-events.ts ← React hook for firing events
|
|
36
|
+
↓
|
|
37
|
+
Consumers ← Use hook to fire analytics events
|
|
38
|
+
↓
|
|
39
|
+
Analytics Listeners ← Events sent to 'peopleTeams' channel
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Adding New Events
|
|
43
|
+
|
|
44
|
+
1. **Define the event** in `analytics.spec.yaml`:
|
|
45
|
+
|
|
46
|
+
[Event definition file specification](https://hello.atlassian.net/wiki/spaces/SMRT/pages/742668844/RFC+-+Analytic+Definition+File#Event-definition----Context)
|
|
47
|
+
|
|
48
|
+
```yaml
|
|
49
|
+
- button clicked (addUserToTeam):
|
|
50
|
+
type: ui
|
|
51
|
+
description: description of when this event fires
|
|
52
|
+
attributes:
|
|
53
|
+
<<: *PackageMetaDataContext
|
|
54
|
+
newAttribute:
|
|
55
|
+
type: string
|
|
56
|
+
description: description of the new attribute
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
2. **Regenerate types**:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
yarn analytics:codegen
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
3. **Use the new event**:
|
|
66
|
+
|
|
67
|
+
```tsx
|
|
68
|
+
import React from 'react';
|
|
69
|
+
import { useAnalyticsEvents } from '@atlaskit/teams-app-internal-analytics';
|
|
70
|
+
|
|
71
|
+
function TeamMemberButton() {
|
|
72
|
+
const { fireEvent } = useAnalyticsEvents();
|
|
73
|
+
|
|
74
|
+
const handleClick = () => {
|
|
75
|
+
fireEvent('ui.button.clicked.addUserToTeam', {
|
|
76
|
+
testAttribute: 'memberInvite',
|
|
77
|
+
});
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
return <button onClick={handleClick}>Invite Member</button>;
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Event Naming Convention
|
|
85
|
+
|
|
86
|
+
Events follow this naming pattern:
|
|
87
|
+
|
|
88
|
+
- **UI Events**: `ui.[actionSubject].[action].[actionSubjectId]`
|
|
89
|
+
- **Track Events**: `track.[actionSubject].[action].[actionSubjectId]`
|
|
90
|
+
- **Operational Events**: `operational.[actionSubject].[action].[actionSubjectId]`
|
|
91
|
+
- **Screen Events**: `screen.[screenName].viewed`
|
|
92
|
+
|
|
93
|
+
## Analytics Context
|
|
94
|
+
|
|
95
|
+
The `PeopleTeamsAnalyticsContext` provides hierarchical metadata that is automatically included in
|
|
96
|
+
all events fired by child components.
|
|
97
|
+
|
|
98
|
+
### Nested Contexts
|
|
99
|
+
|
|
100
|
+
```tsx
|
|
101
|
+
<PeopleTeamsAnalyticsContext
|
|
102
|
+
data={{
|
|
103
|
+
source: 'teamsApp',
|
|
104
|
+
attributes: { consumer: 'web' },
|
|
105
|
+
}}
|
|
106
|
+
>
|
|
107
|
+
<PeopleTeamsAnalyticsContext
|
|
108
|
+
data={{
|
|
109
|
+
source: 'teamsProfilePage',
|
|
110
|
+
attributes: { teamId: 'team-123' },
|
|
111
|
+
}}
|
|
112
|
+
>
|
|
113
|
+
<TeamProfile />
|
|
114
|
+
</PeopleTeamsAnalyticsContext>
|
|
115
|
+
</PeopleTeamsAnalyticsContext>
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
**Resulting event payload:**
|
|
119
|
+
|
|
120
|
+
```json
|
|
121
|
+
{
|
|
122
|
+
"eventType": "ui",
|
|
123
|
+
"actionSubject": "button",
|
|
124
|
+
"action": "clicked",
|
|
125
|
+
"actionSubjectId": "analyticsExample",
|
|
126
|
+
"source": "teamsProfilePage",
|
|
127
|
+
"attributes": {
|
|
128
|
+
"packageName": "@atlassian/team-profile",
|
|
129
|
+
"packageVersion": "X.X.X",
|
|
130
|
+
"consumer": "web",
|
|
131
|
+
"teamId": "team-123",
|
|
132
|
+
"testAttribute": "yourValue",
|
|
133
|
+
"sourceHierarchy": "teamsApp.teamsProfilePage"
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Context Priority
|
|
139
|
+
|
|
140
|
+
When contexts are nested, there is a priority system to ensure the correct metadata is appended to
|
|
141
|
+
events sent to GASv3. Duplicate data is resolved in the following order:
|
|
142
|
+
|
|
143
|
+
1. **Event data** (highest priority)
|
|
144
|
+
2. **Inner context data**
|
|
145
|
+
3. **Outer context data**
|
|
146
|
+
|
|
147
|
+
**Note**: Context `attributes` are only appended to events when they come from the `peopleTeamsCtx`
|
|
148
|
+
namespace, such as the `TeamsAppAnalyticsContext` from this package. Other event payload properties
|
|
149
|
+
like `source` will append to all events regardless of the context namespace.
|
|
150
|
+
|
|
151
|
+
## Testing Analytics
|
|
152
|
+
|
|
153
|
+
This package includes testing utilities that help you verify analytics events are sent correctly to
|
|
154
|
+
GASv3. These utilities use the processing logic from `PeopleTeamsAnalyticsListener`, ensuring that
|
|
155
|
+
event payloads (including all provided contexts) match the expected outcome.
|
|
156
|
+
|
|
157
|
+
### Basic Test Setup
|
|
158
|
+
|
|
159
|
+
```tsx
|
|
160
|
+
import {
|
|
161
|
+
createMockAnalyticsClient,
|
|
162
|
+
renderWithAnalyticsListener,
|
|
163
|
+
} from '@atlaskit/teams-app-internal-analytics/test-utils';
|
|
164
|
+
|
|
165
|
+
const mockClient = createMockAnalyticsClient();
|
|
166
|
+
|
|
167
|
+
test('should fire analytics event on button click', async () => {
|
|
168
|
+
const { user, mockClient, expectEventToBeFired } = renderWithAnalyticsListener(
|
|
169
|
+
<PeopleTeamsAnalyticsContext data={{ source: 'teamsApp' }}>
|
|
170
|
+
<ButtonWithAnalytics testId="analytics-button" />
|
|
171
|
+
</PeopleTeamsAnalyticsContext>,
|
|
172
|
+
{ mockClient },
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
// Trigger the event
|
|
176
|
+
await user.click(screen.getByTestId('analytics-button'));
|
|
177
|
+
|
|
178
|
+
// Verify the event was fired correctly
|
|
179
|
+
expectEventToBeFired('ui', {
|
|
180
|
+
actionSubject: 'button',
|
|
181
|
+
action: 'clicked',
|
|
182
|
+
actionSubjectId: 'analyticsExample',
|
|
183
|
+
source: 'teamsApp',
|
|
184
|
+
attributes: expect.objectContaining({
|
|
185
|
+
testAttribute: 'expectedValue',
|
|
186
|
+
}),
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
```
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
context:
|
|
2
|
+
PackageMetaData: &PackageMetaDataContext
|
|
3
|
+
packageName:
|
|
4
|
+
type: string
|
|
5
|
+
description: name of the package the event was fired from
|
|
6
|
+
packageVersion:
|
|
7
|
+
type: string
|
|
8
|
+
description: version of the package the event was fired from
|
|
9
|
+
|
|
10
|
+
events:
|
|
11
|
+
- button clicked (analyticsExample):
|
|
12
|
+
type: ui
|
|
13
|
+
description: fired when the teams-app-internal-analytics example button is clicked
|
|
14
|
+
attributes:
|
|
15
|
+
<<: *PackageMetaDataContext
|
|
16
|
+
testAttribute:
|
|
17
|
+
type: string
|
|
18
|
+
description: test attribute
|
|
19
|
+
- automation triggered (analyticsExample):
|
|
20
|
+
type: track
|
|
21
|
+
description: fired when the teams-app-internal-analytics example button is clicked
|
|
22
|
+
attributes:
|
|
23
|
+
<<: *PackageMetaDataContext
|
|
24
|
+
testAttribute:
|
|
25
|
+
type: string
|
|
26
|
+
description: test attribute
|
|
27
|
+
- automation fired (analyticsExample):
|
|
28
|
+
type: operational
|
|
29
|
+
description: fired when the teams-app-internal-analytics example button is clicked
|
|
30
|
+
attributes:
|
|
31
|
+
<<: *PackageMetaDataContext
|
|
32
|
+
testAttribute:
|
|
33
|
+
type: string
|
|
34
|
+
description: test attribute
|
|
35
|
+
- analyticsExampleScreen viewed:
|
|
36
|
+
type: screen
|
|
37
|
+
description: fired when the teams-app-internal-analytics example is viewed
|
|
38
|
+
attributes:
|
|
39
|
+
<<: *PackageMetaDataContext
|
|
40
|
+
testAttribute:
|
|
41
|
+
type: string
|
|
42
|
+
description: test attribute
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
var _typeof = require("@babel/runtime/helpers/typeof");
|
|
5
|
+
Object.defineProperty(exports, "__esModule", {
|
|
6
|
+
value: true
|
|
7
|
+
});
|
|
8
|
+
exports.renderWithAnalyticsListener = exports.createMockAnalyticsClient = void 0;
|
|
9
|
+
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
10
|
+
var _react = _interopRequireDefault(require("react"));
|
|
11
|
+
var _react2 = require("@testing-library/react");
|
|
12
|
+
var _userEvent = _interopRequireDefault(require("@testing-library/user-event"));
|
|
13
|
+
var _analyticsListeners = _interopRequireWildcard(require("@atlaskit/analytics-listeners"));
|
|
14
|
+
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
|
|
15
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
16
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
17
|
+
/**
|
|
18
|
+
* Creates a mock analytics client for testing
|
|
19
|
+
*/
|
|
20
|
+
var createMockAnalyticsClient = exports.createMockAnalyticsClient = function createMockAnalyticsClient() {
|
|
21
|
+
return {
|
|
22
|
+
sendUIEvent: jest.fn(),
|
|
23
|
+
sendOperationalEvent: jest.fn(),
|
|
24
|
+
sendTrackEvent: jest.fn(),
|
|
25
|
+
sendScreenEvent: jest.fn()
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Test utility for testing analytics payloads end-to-end.
|
|
31
|
+
* Renders children with analytics listener and provides mock client for event verification.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```tsx
|
|
35
|
+
* const { user, mockClient, expectEventToBeFired } = renderWithAnalyticsListener({
|
|
36
|
+
* children: (
|
|
37
|
+
* <PeopleTeamsAnalyticsContext data={{ source: 'teamsApp' }}>
|
|
38
|
+
* <ButtonWithAnalytics eventType="ui" testId="button-with-analytics"/>
|
|
39
|
+
* </PeopleTeamsAnalyticsContext>
|
|
40
|
+
* )
|
|
41
|
+
* });
|
|
42
|
+
*
|
|
43
|
+
* // Test triggers event however it needs to
|
|
44
|
+
* const button = screen.getByTestId('button-with-analytics');
|
|
45
|
+
* await act(() => user.click(button));
|
|
46
|
+
*
|
|
47
|
+
* // Option 1: Use the helper function
|
|
48
|
+
* expectEventToBeFired('ui', {
|
|
49
|
+
* action: 'clicked',
|
|
50
|
+
* source: 'teamsApp',
|
|
51
|
+
* attributes: expect.objectContaining({
|
|
52
|
+
* packageName: expect.any(String)
|
|
53
|
+
* })
|
|
54
|
+
* });
|
|
55
|
+
*
|
|
56
|
+
* // Option 2: Use mockClient directly for more control
|
|
57
|
+
* expect(mockClient.sendUIEvent).toHaveBeenCalledWith(
|
|
58
|
+
* expect.objectContaining({ source: 'teamsApp' })
|
|
59
|
+
* );
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
var renderWithAnalyticsListener = exports.renderWithAnalyticsListener = function renderWithAnalyticsListener(ui, options) {
|
|
63
|
+
var _ref = options || {},
|
|
64
|
+
setup = _ref.setup,
|
|
65
|
+
providedMockClient = _ref.mockClient;
|
|
66
|
+
if (setup) {
|
|
67
|
+
setup();
|
|
68
|
+
}
|
|
69
|
+
var mockClient = providedMockClient || createMockAnalyticsClient();
|
|
70
|
+
var user = _userEvent.default.setup();
|
|
71
|
+
var renderResult = (0, _react2.render)( /*#__PURE__*/_react.default.createElement(_analyticsListeners.default, {
|
|
72
|
+
excludedChannels: Object.values(_analyticsListeners.FabricChannel).filter(function (channel) {
|
|
73
|
+
return channel !== _analyticsListeners.FabricChannel.peopleTeams;
|
|
74
|
+
}),
|
|
75
|
+
client: mockClient
|
|
76
|
+
}, ui));
|
|
77
|
+
var expectEventToBeFired = function expectEventToBeFired(eventType, expectedPayload) {
|
|
78
|
+
var getMockForEventType = function getMockForEventType(type) {
|
|
79
|
+
switch (type) {
|
|
80
|
+
case 'ui':
|
|
81
|
+
return mockClient.sendUIEvent;
|
|
82
|
+
case 'operational':
|
|
83
|
+
return mockClient.sendOperationalEvent;
|
|
84
|
+
case 'track':
|
|
85
|
+
return mockClient.sendTrackEvent;
|
|
86
|
+
case 'screen':
|
|
87
|
+
return mockClient.sendScreenEvent;
|
|
88
|
+
default:
|
|
89
|
+
throw new Error("Unsupported event type: ".concat(type));
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
var targetMock = getMockForEventType(eventType);
|
|
93
|
+
expect(targetMock).toHaveBeenCalledWith(expect.objectContaining(_objectSpread(_objectSpread({}, expectedPayload), {}, {
|
|
94
|
+
attributes: expect.objectContaining(expectedPayload.attributes || {})
|
|
95
|
+
})));
|
|
96
|
+
};
|
|
97
|
+
return _objectSpread(_objectSpread({}, renderResult), {}, {
|
|
98
|
+
user: user,
|
|
99
|
+
mockClient: mockClient,
|
|
100
|
+
expectEventToBeFired: expectEventToBeFired
|
|
101
|
+
});
|
|
102
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.default = void 0;
|
|
8
|
+
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
|
|
9
|
+
/**
|
|
10
|
+
* THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
|
|
11
|
+
*
|
|
12
|
+
* Generates Typescript types for analytics events from analytics.spec.yaml
|
|
13
|
+
*
|
|
14
|
+
* @codegen <<SignedSource::050566410df453c9b1ba071c6805ead2>>
|
|
15
|
+
* @codegenCommand yarn workspace @atlassian/analytics-tooling run analytics:codegen teams-app-internal-analytics
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
var createEventPayload = function createEventPayload(eventKey) {
|
|
19
|
+
for (var _len = arguments.length, _ref = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
|
|
20
|
+
_ref[_key - 1] = arguments[_key];
|
|
21
|
+
}
|
|
22
|
+
var attributes = _ref[0];
|
|
23
|
+
var _eventKey$split = eventKey.split('.'),
|
|
24
|
+
_eventKey$split2 = (0, _slicedToArray2.default)(_eventKey$split, 4),
|
|
25
|
+
eventType = _eventKey$split2[0],
|
|
26
|
+
actionSubject = _eventKey$split2[1],
|
|
27
|
+
action = _eventKey$split2[2],
|
|
28
|
+
actionSubjectId = _eventKey$split2[3];
|
|
29
|
+
if (eventType === 'screen') {
|
|
30
|
+
return {
|
|
31
|
+
eventType: eventType,
|
|
32
|
+
name: actionSubject,
|
|
33
|
+
action: 'viewed',
|
|
34
|
+
attributes: attributes
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
eventType: eventType,
|
|
39
|
+
actionSubject: actionSubject,
|
|
40
|
+
action: action,
|
|
41
|
+
actionSubjectId: actionSubjectId,
|
|
42
|
+
attributes: attributes
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
var _default = exports.default = createEventPayload;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.useAnalyticsEvents = void 0;
|
|
8
|
+
var _react = require("react");
|
|
9
|
+
var _analyticsNext = require("@atlaskit/analytics-next");
|
|
10
|
+
var _constants = require("../constants");
|
|
11
|
+
var _createEventPayload = _interopRequireDefault(require("./create-event-payload"));
|
|
12
|
+
/**
|
|
13
|
+
* THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
|
|
14
|
+
*
|
|
15
|
+
* Generates Typescript types for analytics events from analytics.spec.yaml
|
|
16
|
+
*
|
|
17
|
+
* @codegen <<SignedSource::d01fef63886f2825ffbc3a0ec1ca7534>>
|
|
18
|
+
* @codegenCommand yarn workspace @atlassian/analytics-tooling run analytics:codegen teams-app-internal-analytics
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
var useAnalyticsEvents = exports.useAnalyticsEvents = function useAnalyticsEvents() {
|
|
22
|
+
var _useAnalyticsNextEven = (0, _analyticsNext.useAnalyticsEvents)(),
|
|
23
|
+
createAnalyticsEvent = _useAnalyticsNextEven.createAnalyticsEvent;
|
|
24
|
+
var fireEvent = (0, _react.useCallback)(function () {
|
|
25
|
+
var event = createAnalyticsEvent(_createEventPayload.default.apply(void 0, arguments));
|
|
26
|
+
event.fire(_constants.EVENT_CHANNEL);
|
|
27
|
+
}, [createAnalyticsEvent]);
|
|
28
|
+
return {
|
|
29
|
+
fireEvent: fireEvent
|
|
30
|
+
};
|
|
31
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
Object.defineProperty(exports, "TeamsAppAnalyticsContext", {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: function get() {
|
|
9
|
+
return _analyticsContext.TeamsAppAnalyticsContext;
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
Object.defineProperty(exports, "useAnalyticsEvents", {
|
|
13
|
+
enumerable: true,
|
|
14
|
+
get: function get() {
|
|
15
|
+
return _useAnalyticsEvents.useAnalyticsEvents;
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
var _analyticsContext = require("./ui/analytics-context");
|
|
19
|
+
var _useAnalyticsEvents = require("./common/utils/generated/use-analytics-events");
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
var _typeof3 = require("@babel/runtime/helpers/typeof");
|
|
5
|
+
Object.defineProperty(exports, "__esModule", {
|
|
6
|
+
value: true
|
|
7
|
+
});
|
|
8
|
+
exports.TeamsAppAnalyticsContext = TeamsAppAnalyticsContext;
|
|
9
|
+
exports.defaultAnalyticsContextData = void 0;
|
|
10
|
+
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
11
|
+
var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
|
|
12
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
13
|
+
var _analyticsNamespacedContext = require("@atlaskit/analytics-namespaced-context");
|
|
14
|
+
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof3(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
|
|
15
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
16
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
17
|
+
var defaultAnalyticsContextData = exports.defaultAnalyticsContextData = {
|
|
18
|
+
packageName: "@atlaskit/teams-app-internal-analytics",
|
|
19
|
+
packageVersion: "0.0.0"
|
|
20
|
+
};
|
|
21
|
+
function TeamsAppAnalyticsContext(_ref) {
|
|
22
|
+
var data = _ref.data,
|
|
23
|
+
children = _ref.children;
|
|
24
|
+
var analyticsContextData = (0, _react.useMemo)(function () {
|
|
25
|
+
if ((0, _typeof2.default)(data) === 'object') {
|
|
26
|
+
return _objectSpread(_objectSpread({}, defaultAnalyticsContextData), data);
|
|
27
|
+
}
|
|
28
|
+
return defaultAnalyticsContextData;
|
|
29
|
+
}, [data]);
|
|
30
|
+
return /*#__PURE__*/_react.default.createElement(_analyticsNamespacedContext.PeopleTeamsAnalyticsContext, {
|
|
31
|
+
data: analyticsContextData
|
|
32
|
+
}, children);
|
|
33
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render } from '@testing-library/react';
|
|
3
|
+
import userEvent from '@testing-library/user-event';
|
|
4
|
+
import FabricAnalyticsListeners, { FabricChannel } from '@atlaskit/analytics-listeners';
|
|
5
|
+
/**
|
|
6
|
+
* Creates a mock analytics client for testing
|
|
7
|
+
*/
|
|
8
|
+
export const createMockAnalyticsClient = () => ({
|
|
9
|
+
sendUIEvent: jest.fn(),
|
|
10
|
+
sendOperationalEvent: jest.fn(),
|
|
11
|
+
sendTrackEvent: jest.fn(),
|
|
12
|
+
sendScreenEvent: jest.fn()
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Test utility for testing analytics payloads end-to-end.
|
|
17
|
+
* Renders children with analytics listener and provides mock client for event verification.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```tsx
|
|
21
|
+
* const { user, mockClient, expectEventToBeFired } = renderWithAnalyticsListener({
|
|
22
|
+
* children: (
|
|
23
|
+
* <PeopleTeamsAnalyticsContext data={{ source: 'teamsApp' }}>
|
|
24
|
+
* <ButtonWithAnalytics eventType="ui" testId="button-with-analytics"/>
|
|
25
|
+
* </PeopleTeamsAnalyticsContext>
|
|
26
|
+
* )
|
|
27
|
+
* });
|
|
28
|
+
*
|
|
29
|
+
* // Test triggers event however it needs to
|
|
30
|
+
* const button = screen.getByTestId('button-with-analytics');
|
|
31
|
+
* await act(() => user.click(button));
|
|
32
|
+
*
|
|
33
|
+
* // Option 1: Use the helper function
|
|
34
|
+
* expectEventToBeFired('ui', {
|
|
35
|
+
* action: 'clicked',
|
|
36
|
+
* source: 'teamsApp',
|
|
37
|
+
* attributes: expect.objectContaining({
|
|
38
|
+
* packageName: expect.any(String)
|
|
39
|
+
* })
|
|
40
|
+
* });
|
|
41
|
+
*
|
|
42
|
+
* // Option 2: Use mockClient directly for more control
|
|
43
|
+
* expect(mockClient.sendUIEvent).toHaveBeenCalledWith(
|
|
44
|
+
* expect.objectContaining({ source: 'teamsApp' })
|
|
45
|
+
* );
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
export const renderWithAnalyticsListener = (ui, options) => {
|
|
49
|
+
const {
|
|
50
|
+
setup,
|
|
51
|
+
mockClient: providedMockClient
|
|
52
|
+
} = options || {};
|
|
53
|
+
if (setup) {
|
|
54
|
+
setup();
|
|
55
|
+
}
|
|
56
|
+
const mockClient = providedMockClient || createMockAnalyticsClient();
|
|
57
|
+
const user = userEvent.setup();
|
|
58
|
+
const renderResult = render( /*#__PURE__*/React.createElement(FabricAnalyticsListeners, {
|
|
59
|
+
excludedChannels: Object.values(FabricChannel).filter(channel => channel !== FabricChannel.peopleTeams),
|
|
60
|
+
client: mockClient
|
|
61
|
+
}, ui));
|
|
62
|
+
const expectEventToBeFired = (eventType, expectedPayload) => {
|
|
63
|
+
const getMockForEventType = type => {
|
|
64
|
+
switch (type) {
|
|
65
|
+
case 'ui':
|
|
66
|
+
return mockClient.sendUIEvent;
|
|
67
|
+
case 'operational':
|
|
68
|
+
return mockClient.sendOperationalEvent;
|
|
69
|
+
case 'track':
|
|
70
|
+
return mockClient.sendTrackEvent;
|
|
71
|
+
case 'screen':
|
|
72
|
+
return mockClient.sendScreenEvent;
|
|
73
|
+
default:
|
|
74
|
+
throw new Error(`Unsupported event type: ${type}`);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
const targetMock = getMockForEventType(eventType);
|
|
78
|
+
expect(targetMock).toHaveBeenCalledWith(expect.objectContaining({
|
|
79
|
+
...expectedPayload,
|
|
80
|
+
attributes: expect.objectContaining(expectedPayload.attributes || {})
|
|
81
|
+
}));
|
|
82
|
+
};
|
|
83
|
+
return {
|
|
84
|
+
...renderResult,
|
|
85
|
+
user,
|
|
86
|
+
mockClient,
|
|
87
|
+
expectEventToBeFired
|
|
88
|
+
};
|
|
89
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const EVENT_CHANNEL = 'peopleTeams';
|
|
File without changes
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
|
|
3
|
+
*
|
|
4
|
+
* Generates Typescript types for analytics events from analytics.spec.yaml
|
|
5
|
+
*
|
|
6
|
+
* @codegen <<SignedSource::050566410df453c9b1ba071c6805ead2>>
|
|
7
|
+
* @codegenCommand yarn workspace @atlassian/analytics-tooling run analytics:codegen teams-app-internal-analytics
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const createEventPayload = (eventKey, ...[attributes]) => {
|
|
11
|
+
const [eventType, actionSubject, action, actionSubjectId] = eventKey.split('.');
|
|
12
|
+
if (eventType === 'screen') {
|
|
13
|
+
return {
|
|
14
|
+
eventType,
|
|
15
|
+
name: actionSubject,
|
|
16
|
+
action: 'viewed',
|
|
17
|
+
attributes: attributes
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
return {
|
|
21
|
+
eventType,
|
|
22
|
+
actionSubject,
|
|
23
|
+
action,
|
|
24
|
+
actionSubjectId,
|
|
25
|
+
attributes: attributes
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
export default createEventPayload;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
|
|
3
|
+
*
|
|
4
|
+
* Generates Typescript types for analytics events from analytics.spec.yaml
|
|
5
|
+
*
|
|
6
|
+
* @codegen <<SignedSource::d01fef63886f2825ffbc3a0ec1ca7534>>
|
|
7
|
+
* @codegenCommand yarn workspace @atlassian/analytics-tooling run analytics:codegen teams-app-internal-analytics
|
|
8
|
+
*/
|
|
9
|
+
import { useCallback } from 'react';
|
|
10
|
+
import { useAnalyticsEvents as useAnalyticsNextEvents } from '@atlaskit/analytics-next';
|
|
11
|
+
import { EVENT_CHANNEL } from '../constants';
|
|
12
|
+
import createEventPayload from './create-event-payload';
|
|
13
|
+
export const useAnalyticsEvents = () => {
|
|
14
|
+
const {
|
|
15
|
+
createAnalyticsEvent
|
|
16
|
+
} = useAnalyticsNextEvents();
|
|
17
|
+
const fireEvent = useCallback((...params) => {
|
|
18
|
+
const event = createAnalyticsEvent(createEventPayload(...params));
|
|
19
|
+
event.fire(EVENT_CHANNEL);
|
|
20
|
+
}, [createAnalyticsEvent]);
|
|
21
|
+
return {
|
|
22
|
+
fireEvent
|
|
23
|
+
};
|
|
24
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
|
+
import { PeopleTeamsAnalyticsContext } from '@atlaskit/analytics-namespaced-context';
|
|
3
|
+
export const defaultAnalyticsContextData = {
|
|
4
|
+
packageName: "@atlaskit/teams-app-internal-analytics",
|
|
5
|
+
packageVersion: "0.0.0"
|
|
6
|
+
};
|
|
7
|
+
export function TeamsAppAnalyticsContext({
|
|
8
|
+
data,
|
|
9
|
+
children
|
|
10
|
+
}) {
|
|
11
|
+
const analyticsContextData = useMemo(() => {
|
|
12
|
+
if (typeof data === 'object') {
|
|
13
|
+
return {
|
|
14
|
+
...defaultAnalyticsContextData,
|
|
15
|
+
...data
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
return defaultAnalyticsContextData;
|
|
19
|
+
}, [data]);
|
|
20
|
+
return /*#__PURE__*/React.createElement(PeopleTeamsAnalyticsContext, {
|
|
21
|
+
data: analyticsContextData
|
|
22
|
+
}, children);
|
|
23
|
+
}
|