@financial-times/cp-content-pipeline-ui 7.4.1 → 7.4.2

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 CHANGED
@@ -574,6 +574,13 @@
574
574
  * @financial-times/cp-content-pipeline-client bumped from ^3.7.2 to ^3.7.3
575
575
  * @financial-times/cp-content-pipeline-schema bumped from ^2.10.1 to ^2.10.2
576
576
 
577
+ ## [7.4.2](https://github.com/Financial-Times/cp-content-pipeline/compare/cp-content-pipeline-ui-v7.4.1...cp-content-pipeline-ui-v7.4.2) (2024-10-30)
578
+
579
+
580
+ ### Bug Fixes
581
+
582
+ * only send errors when the value has changed not been removed ([4ff2bbc](https://github.com/Financial-Times/cp-content-pipeline/commit/4ff2bbc03f4b8c824549bb4a7529e4d56b758aa6))
583
+
577
584
  ## [7.4.1](https://github.com/Financial-Times/cp-content-pipeline/compare/cp-content-pipeline-ui-v7.4.0...cp-content-pipeline-ui-v7.4.1) (2024-10-29)
578
585
 
579
586
 
@@ -2,9 +2,8 @@ type Component = {
2
2
  id: string;
3
3
  name: string;
4
4
  type: string;
5
- timeElapsedSeconds?: number;
6
5
  };
7
- type TrackingData = {
6
+ export type TrackingData = {
8
7
  component: Component;
9
8
  };
10
9
  /**
@@ -61,9 +61,11 @@ class CustomCodeComponentTracker {
61
61
  this.dispatchEvent('act', { trigger_action: 'success' });
62
62
  }
63
63
  onCccError() {
64
- this.dispatchEvent('act', {
65
- trigger_action: `error: ${this.component.dataset.cccError}`,
66
- });
64
+ if (this.component.dataset.cccError) {
65
+ this.dispatchEvent('act', {
66
+ trigger_action: `error: ${this.component.dataset.cccError}`,
67
+ });
68
+ }
67
69
  }
68
70
  onView() {
69
71
  this.dispatchEvent('view');
@@ -1 +1 @@
1
- {"version":3,"file":"tracking.js","sourceRoot":"","sources":["../../../../../src/components/content-tree/CustomCodeComponent/client/tracking.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tracking.js","sourceRoot":"","sources":["../../../../../src/components/content-tree/CustomCodeComponent/client/tracking.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,195 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tracking_1 = require("./tracking");
4
+ describe('Custom Code Component Tracker', () => {
5
+ let mockIntersectionObserver;
6
+ let mockObserve;
7
+ let mockUnobserve;
8
+ let mockDisconnectIntersection;
9
+ let mockComponent;
10
+ let mockTrackingData;
11
+ let dispatchEventSpy;
12
+ beforeEach(() => {
13
+ mockObserve = jest.fn();
14
+ mockUnobserve = jest.fn();
15
+ mockDisconnectIntersection = jest.fn();
16
+ mockIntersectionObserver = jest.fn(() => {
17
+ return {
18
+ observe: mockObserve,
19
+ unobserve: mockUnobserve,
20
+ disconnect: mockDisconnectIntersection,
21
+ };
22
+ });
23
+ window.IntersectionObserver = mockIntersectionObserver;
24
+ window.MutationObserver = MutationObserver;
25
+ mockComponent = document.createElement('custom-code-component');
26
+ mockTrackingData = {
27
+ component: {
28
+ id: '123',
29
+ name: 'test-component',
30
+ type: 'custom-code-component',
31
+ },
32
+ };
33
+ document.body.appendChild(mockComponent);
34
+ dispatchEventSpy = jest.spyOn(document.body, 'dispatchEvent');
35
+ });
36
+ afterEach(() => {
37
+ jest.clearAllMocks();
38
+ document.body.removeChild(mockComponent);
39
+ });
40
+ describe('tracking initialisation and teardown', () => {
41
+ it('should initialise the component tracking', () => {
42
+ new tracking_1.CustomCodeComponentTracker(mockComponent, mockTrackingData);
43
+ const mountEvent = dispatchEventSpy.mock.calls[0][0];
44
+ expect(mountEvent.type).toBe('oTracking.event');
45
+ expect(mountEvent.detail).toEqual({
46
+ category: 'component',
47
+ action: 'mount',
48
+ component: mockTrackingData.component,
49
+ });
50
+ });
51
+ it('should send an error event on initialisation if the component has errored', () => {
52
+ mockComponent.dataset.cccError = 'import-failure';
53
+ new tracking_1.CustomCodeComponentTracker(mockComponent, mockTrackingData);
54
+ const errorEvent = dispatchEventSpy.mock.calls[1][0];
55
+ expect(errorEvent.type).toBe('oTracking.event');
56
+ expect(errorEvent.detail).toEqual({
57
+ category: 'component',
58
+ action: 'act',
59
+ component: mockTrackingData.component,
60
+ trigger_action: 'error: import-failure',
61
+ });
62
+ });
63
+ it('should send a success event on initialisation if the component has already loaded', () => {
64
+ mockComponent.dataset.cccReady = 'true';
65
+ new tracking_1.CustomCodeComponentTracker(mockComponent, mockTrackingData);
66
+ const successEvent = dispatchEventSpy.mock.calls[1][0];
67
+ expect(successEvent.type).toBe('oTracking.event');
68
+ expect(successEvent.detail).toEqual({
69
+ category: 'component',
70
+ action: 'act',
71
+ component: mockTrackingData.component,
72
+ trigger_action: 'success',
73
+ });
74
+ });
75
+ it('should set the component as initialised', () => {
76
+ new tracking_1.CustomCodeComponentTracker(mockComponent, mockTrackingData);
77
+ expect(mockComponent.getAttribute('data-initialised')).toBe('true');
78
+ });
79
+ it('should teardown the instance on destroy', () => {
80
+ const tracker = new tracking_1.CustomCodeComponentTracker(mockComponent, mockTrackingData);
81
+ tracker.destroy();
82
+ expect(mockComponent.getAttribute('data-initialised')).toBeNull();
83
+ expect(mockDisconnectIntersection).toHaveBeenCalledTimes(1);
84
+ });
85
+ });
86
+ describe('full component initalisation events', () => {
87
+ it('should send a success event when the component has loaded', (done) => {
88
+ new tracking_1.CustomCodeComponentTracker(mockComponent, mockTrackingData);
89
+ Promise.resolve((mockComponent.dataset.cccReady = 'true')).then(() => {
90
+ const successEvent = dispatchEventSpy.mock.calls[1][0];
91
+ expect(successEvent.type).toBe('oTracking.event');
92
+ expect(successEvent.detail).toEqual({
93
+ category: 'component',
94
+ action: 'act',
95
+ component: mockTrackingData.component,
96
+ trigger_action: 'success',
97
+ });
98
+ done();
99
+ });
100
+ });
101
+ it('should send an error import-timeout event when the component has not loaded', (done) => {
102
+ new tracking_1.CustomCodeComponentTracker(mockComponent, mockTrackingData);
103
+ Promise.resolve((mockComponent.dataset.cccError = 'import-timeout')).then(() => {
104
+ const successEvent = dispatchEventSpy.mock.calls[1][0];
105
+ expect(successEvent.type).toBe('oTracking.event');
106
+ expect(successEvent.detail).toEqual({
107
+ category: 'component',
108
+ action: 'act',
109
+ component: mockTrackingData.component,
110
+ trigger_action: 'error: import-timeout',
111
+ });
112
+ done();
113
+ });
114
+ });
115
+ it('should send an error import-failure event when the component has errored', (done) => {
116
+ new tracking_1.CustomCodeComponentTracker(mockComponent, mockTrackingData);
117
+ Promise.resolve((mockComponent.dataset.cccError = 'import-failure')).then(() => {
118
+ const successEvent = dispatchEventSpy.mock.calls[1][0];
119
+ expect(successEvent.type).toBe('oTracking.event');
120
+ expect(successEvent.detail).toEqual({
121
+ category: 'component',
122
+ action: 'act',
123
+ component: mockTrackingData.component,
124
+ trigger_action: 'error: import-failure',
125
+ });
126
+ done();
127
+ });
128
+ });
129
+ it('should send a timeout then success if the component loads slowly, after a time threshold defined as timeout', (done) => {
130
+ new tracking_1.CustomCodeComponentTracker(mockComponent, mockTrackingData);
131
+ Promise.resolve((mockComponent.dataset.cccError = 'import-timeout'))
132
+ .then(() => (mockComponent.dataset.cccReady = 'true'))
133
+ .then(() => {
134
+ const successEvent = dispatchEventSpy.mock.calls[2][0];
135
+ expect(successEvent.type).toBe('oTracking.event');
136
+ expect(successEvent.detail).toEqual({
137
+ category: 'component',
138
+ action: 'act',
139
+ component: mockTrackingData.component,
140
+ trigger_action: 'success',
141
+ });
142
+ done();
143
+ });
144
+ });
145
+ });
146
+ describe('component visibility changes', () => {
147
+ it('should send a view event when the component enters and exits the viewport', () => {
148
+ const tracker = new tracking_1.CustomCodeComponentTracker(mockComponent, mockTrackingData);
149
+ const mockEnterView = [
150
+ {
151
+ target: mockComponent,
152
+ isIntersecting: true,
153
+ intersectionRatio: 1.0,
154
+ boundingClientRect: {
155
+ height: 100,
156
+ },
157
+ intersectionRect: {},
158
+ rootBounds: null,
159
+ time: 1,
160
+ },
161
+ ];
162
+ tracker['onChange'](mockEnterView);
163
+ const viewEvent = dispatchEventSpy.mock.calls[1][0];
164
+ expect(viewEvent.type).toBe('oTracking.event');
165
+ expect(viewEvent.detail).toEqual({
166
+ category: 'component',
167
+ action: 'view',
168
+ component: mockTrackingData.component,
169
+ });
170
+ const mockExitView = [
171
+ {
172
+ target: mockComponent,
173
+ isIntersecting: false,
174
+ intersectionRatio: 0,
175
+ boundingClientRect: {
176
+ height: 100,
177
+ },
178
+ intersectionRect: {},
179
+ rootBounds: null,
180
+ time: 1,
181
+ },
182
+ ];
183
+ tracker['onChange'](mockExitView);
184
+ const exitEvent = dispatchEventSpy.mock.calls[2][0];
185
+ expect(exitEvent.type).toBe('oTracking.event');
186
+ expect(exitEvent.detail).toEqual({
187
+ category: 'component',
188
+ action: 'act',
189
+ trigger_action: 'exit-view',
190
+ component: mockTrackingData.component,
191
+ });
192
+ });
193
+ });
194
+ });
195
+ //# sourceMappingURL=tracking.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tracking.spec.js","sourceRoot":"","sources":["../../../../../src/components/content-tree/CustomCodeComponent/client/tracking.spec.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@financial-times/cp-content-pipeline-ui",
3
- "version": "7.4.1",
3
+ "version": "7.4.2",
4
4
  "description": "",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {
@@ -0,0 +1,232 @@
1
+ import { TrackingData, CustomCodeComponentTracker } from './tracking'
2
+
3
+ describe('Custom Code Component Tracker', () => {
4
+ let mockIntersectionObserver: jest.Mock
5
+ let mockObserve: jest.Mock
6
+ let mockUnobserve: jest.Mock
7
+ let mockDisconnectIntersection: jest.Mock
8
+ let mockComponent: HTMLElement
9
+ let mockTrackingData: TrackingData
10
+ let dispatchEventSpy: jest.SpyInstance
11
+
12
+ beforeEach(() => {
13
+ mockObserve = jest.fn()
14
+ mockUnobserve = jest.fn()
15
+ mockDisconnectIntersection = jest.fn()
16
+ mockIntersectionObserver = jest.fn(() => {
17
+ return {
18
+ observe: mockObserve,
19
+ unobserve: mockUnobserve,
20
+ disconnect: mockDisconnectIntersection,
21
+ }
22
+ })
23
+
24
+ window.IntersectionObserver = mockIntersectionObserver
25
+ window.MutationObserver = MutationObserver
26
+
27
+ mockComponent = document.createElement('custom-code-component')
28
+ mockTrackingData = {
29
+ component: {
30
+ id: '123',
31
+ name: 'test-component',
32
+ type: 'custom-code-component',
33
+ },
34
+ }
35
+ document.body.appendChild(mockComponent)
36
+ dispatchEventSpy = jest.spyOn(document.body, 'dispatchEvent')
37
+ })
38
+
39
+ afterEach(() => {
40
+ jest.clearAllMocks()
41
+ document.body.removeChild(mockComponent)
42
+ })
43
+
44
+ describe('tracking initialisation and teardown', () => {
45
+ it('should initialise the component tracking', () => {
46
+ new CustomCodeComponentTracker(mockComponent, mockTrackingData)
47
+ const mountEvent = dispatchEventSpy.mock.calls[0][0] as CustomEvent
48
+
49
+ expect(mountEvent.type).toBe('oTracking.event')
50
+ expect(mountEvent.detail).toEqual({
51
+ category: 'component',
52
+ action: 'mount',
53
+ component: mockTrackingData.component,
54
+ })
55
+ })
56
+
57
+ it('should send an error event on initialisation if the component has errored', () => {
58
+ mockComponent.dataset.cccError = 'import-failure'
59
+ new CustomCodeComponentTracker(mockComponent, mockTrackingData)
60
+ const errorEvent = dispatchEventSpy.mock.calls[1][0] as CustomEvent
61
+
62
+ expect(errorEvent.type).toBe('oTracking.event')
63
+ expect(errorEvent.detail).toEqual({
64
+ category: 'component',
65
+ action: 'act',
66
+ component: mockTrackingData.component,
67
+ trigger_action: 'error: import-failure',
68
+ })
69
+ })
70
+
71
+ it('should send a success event on initialisation if the component has already loaded', () => {
72
+ mockComponent.dataset.cccReady = 'true'
73
+ new CustomCodeComponentTracker(mockComponent, mockTrackingData)
74
+ const successEvent = dispatchEventSpy.mock.calls[1][0] as CustomEvent
75
+
76
+ expect(successEvent.type).toBe('oTracking.event')
77
+ expect(successEvent.detail).toEqual({
78
+ category: 'component',
79
+ action: 'act',
80
+ component: mockTrackingData.component,
81
+ trigger_action: 'success',
82
+ })
83
+ })
84
+
85
+ it('should set the component as initialised', () => {
86
+ new CustomCodeComponentTracker(mockComponent, mockTrackingData)
87
+ expect(mockComponent.getAttribute('data-initialised')).toBe('true')
88
+ })
89
+
90
+ it('should teardown the instance on destroy', () => {
91
+ const tracker = new CustomCodeComponentTracker(
92
+ mockComponent,
93
+ mockTrackingData
94
+ )
95
+ tracker.destroy()
96
+
97
+ expect(mockComponent.getAttribute('data-initialised')).toBeNull()
98
+ expect(mockDisconnectIntersection).toHaveBeenCalledTimes(1)
99
+ })
100
+ })
101
+
102
+ describe('full component initalisation events', () => {
103
+ it('should send a success event when the component has loaded', (done) => {
104
+ new CustomCodeComponentTracker(mockComponent, mockTrackingData)
105
+
106
+ Promise.resolve((mockComponent.dataset.cccReady = 'true')).then(() => {
107
+ const successEvent = dispatchEventSpy.mock.calls[1][0] as CustomEvent
108
+
109
+ expect(successEvent.type).toBe('oTracking.event')
110
+ expect(successEvent.detail).toEqual({
111
+ category: 'component',
112
+ action: 'act',
113
+ component: mockTrackingData.component,
114
+ trigger_action: 'success',
115
+ })
116
+ done()
117
+ })
118
+ })
119
+
120
+ it('should send an error import-timeout event when the component has not loaded', (done) => {
121
+ new CustomCodeComponentTracker(mockComponent, mockTrackingData)
122
+
123
+ Promise.resolve((mockComponent.dataset.cccError = 'import-timeout')).then(
124
+ () => {
125
+ const successEvent = dispatchEventSpy.mock.calls[1][0] as CustomEvent
126
+
127
+ expect(successEvent.type).toBe('oTracking.event')
128
+ expect(successEvent.detail).toEqual({
129
+ category: 'component',
130
+ action: 'act',
131
+ component: mockTrackingData.component,
132
+ trigger_action: 'error: import-timeout',
133
+ })
134
+ done()
135
+ }
136
+ )
137
+ })
138
+
139
+ it('should send an error import-failure event when the component has errored', (done) => {
140
+ new CustomCodeComponentTracker(mockComponent, mockTrackingData)
141
+
142
+ Promise.resolve((mockComponent.dataset.cccError = 'import-failure')).then(
143
+ () => {
144
+ const successEvent = dispatchEventSpy.mock.calls[1][0] as CustomEvent
145
+
146
+ expect(successEvent.type).toBe('oTracking.event')
147
+ expect(successEvent.detail).toEqual({
148
+ category: 'component',
149
+ action: 'act',
150
+ component: mockTrackingData.component,
151
+ trigger_action: 'error: import-failure',
152
+ })
153
+ done()
154
+ }
155
+ )
156
+ })
157
+
158
+ it('should send a timeout then success if the component loads slowly, after a time threshold defined as timeout', (done) => {
159
+ new CustomCodeComponentTracker(mockComponent, mockTrackingData)
160
+
161
+ Promise.resolve((mockComponent.dataset.cccError = 'import-timeout'))
162
+ .then(() => (mockComponent.dataset.cccReady = 'true'))
163
+ .then(() => {
164
+ const successEvent = dispatchEventSpy.mock.calls[2][0] as CustomEvent
165
+
166
+ expect(successEvent.type).toBe('oTracking.event')
167
+ expect(successEvent.detail).toEqual({
168
+ category: 'component',
169
+ action: 'act',
170
+ component: mockTrackingData.component,
171
+ trigger_action: 'success',
172
+ })
173
+ done()
174
+ })
175
+ })
176
+ })
177
+
178
+ describe('component visibility changes', () => {
179
+ it('should send a view event when the component enters and exits the viewport', () => {
180
+ const tracker = new CustomCodeComponentTracker(
181
+ mockComponent,
182
+ mockTrackingData
183
+ )
184
+ const mockEnterView: IntersectionObserverEntry[] = [
185
+ {
186
+ target: mockComponent,
187
+ isIntersecting: true,
188
+ intersectionRatio: 1.0,
189
+ boundingClientRect: {
190
+ height: 100,
191
+ } as unknown as DOMRectReadOnly,
192
+ intersectionRect: {} as DOMRectReadOnly,
193
+ rootBounds: null,
194
+ time: 1,
195
+ },
196
+ ]
197
+ tracker['onChange'](mockEnterView)
198
+ const viewEvent = dispatchEventSpy.mock.calls[1][0] as CustomEvent
199
+
200
+ expect(viewEvent.type).toBe('oTracking.event')
201
+ expect(viewEvent.detail).toEqual({
202
+ category: 'component',
203
+ action: 'view',
204
+ component: mockTrackingData.component,
205
+ })
206
+
207
+ const mockExitView: IntersectionObserverEntry[] = [
208
+ {
209
+ target: mockComponent,
210
+ isIntersecting: false,
211
+ intersectionRatio: 0,
212
+ boundingClientRect: {
213
+ height: 100,
214
+ } as unknown as DOMRectReadOnly,
215
+ intersectionRect: {} as DOMRectReadOnly,
216
+ rootBounds: null,
217
+ time: 1,
218
+ },
219
+ ]
220
+ tracker['onChange'](mockExitView)
221
+ const exitEvent = dispatchEventSpy.mock.calls[2][0] as CustomEvent
222
+
223
+ expect(exitEvent.type).toBe('oTracking.event')
224
+ expect(exitEvent.detail).toEqual({
225
+ category: 'component',
226
+ action: 'act',
227
+ trigger_action: 'exit-view',
228
+ component: mockTrackingData.component,
229
+ })
230
+ })
231
+ })
232
+ })
@@ -2,10 +2,9 @@ type Component = {
2
2
  id: string
3
3
  name: string
4
4
  type: string
5
- timeElapsedSeconds?: number
6
5
  }
7
6
 
8
- type TrackingData = {
7
+ export type TrackingData = {
9
8
  component: Component
10
9
  }
11
10
 
@@ -80,9 +79,11 @@ class CustomCodeComponentTracker {
80
79
  }
81
80
 
82
81
  private onCccError(): void {
83
- this.dispatchEvent('act', {
84
- trigger_action: `error: ${this.component.dataset.cccError}`,
85
- })
82
+ if (this.component.dataset.cccError) {
83
+ this.dispatchEvent('act', {
84
+ trigger_action: `error: ${this.component.dataset.cccError}`,
85
+ })
86
+ }
86
87
  }
87
88
 
88
89
  private onView(): void {