@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.
Files changed (41) hide show
  1. package/CHANGELOG.md +1 -0
  2. package/LICENSE.md +11 -0
  3. package/README.md +189 -0
  4. package/analytics.spec.yaml +42 -0
  5. package/dist/cjs/common/test-utils/index.js +102 -0
  6. package/dist/cjs/common/utils/constants.js +7 -0
  7. package/dist/cjs/common/utils/generated/analytics.types.js +1 -0
  8. package/dist/cjs/common/utils/generated/create-event-payload.js +45 -0
  9. package/dist/cjs/common/utils/generated/use-analytics-events.js +31 -0
  10. package/dist/cjs/index.js +19 -0
  11. package/dist/cjs/ui/analytics-context/index.js +33 -0
  12. package/dist/es2019/common/test-utils/index.js +89 -0
  13. package/dist/es2019/common/utils/constants.js +1 -0
  14. package/dist/es2019/common/utils/generated/analytics.types.js +0 -0
  15. package/dist/es2019/common/utils/generated/create-event-payload.js +28 -0
  16. package/dist/es2019/common/utils/generated/use-analytics-events.js +24 -0
  17. package/dist/es2019/index.js +2 -0
  18. package/dist/es2019/ui/analytics-context/index.js +23 -0
  19. package/dist/esm/common/test-utils/index.js +93 -0
  20. package/dist/esm/common/utils/constants.js +1 -0
  21. package/dist/esm/common/utils/generated/analytics.types.js +0 -0
  22. package/dist/esm/common/utils/generated/create-event-payload.js +38 -0
  23. package/dist/esm/common/utils/generated/use-analytics-events.js +23 -0
  24. package/dist/esm/index.js +2 -0
  25. package/dist/esm/ui/analytics-context/index.js +23 -0
  26. package/dist/types/common/test-utils/index.d.ts +107 -0
  27. package/dist/types/common/utils/constants.d.ts +1 -0
  28. package/dist/types/common/utils/generated/analytics.types.d.ts +39 -0
  29. package/dist/types/common/utils/generated/create-event-payload.d.ts +27 -0
  30. package/dist/types/common/utils/generated/use-analytics-events.d.ts +4 -0
  31. package/dist/types/index.d.ts +2 -0
  32. package/dist/types/ui/analytics-context/index.d.ts +12 -0
  33. package/dist/types-ts4.5/common/test-utils/index.d.ts +107 -0
  34. package/dist/types-ts4.5/common/utils/constants.d.ts +1 -0
  35. package/dist/types-ts4.5/common/utils/generated/analytics.types.d.ts +39 -0
  36. package/dist/types-ts4.5/common/utils/generated/create-event-payload.d.ts +31 -0
  37. package/dist/types-ts4.5/common/utils/generated/use-analytics-events.d.ts +8 -0
  38. package/dist/types-ts4.5/index.d.ts +2 -0
  39. package/dist/types-ts4.5/ui/analytics-context/index.d.ts +12 -0
  40. package/package.json +102 -0
  41. 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,7 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.EVENT_CHANNEL = void 0;
7
+ var EVENT_CHANNEL = exports.EVENT_CHANNEL = 'peopleTeams';
@@ -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';
@@ -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,2 @@
1
+ export { TeamsAppAnalyticsContext } from './ui/analytics-context';
2
+ export { useAnalyticsEvents } from './common/utils/generated/use-analytics-events';
@@ -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
+ }