@financial-times/cp-content-pipeline-ui 6.12.0 → 6.13.0-beta.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 (35) hide show
  1. package/lib/client.d.ts +1 -0
  2. package/lib/client.js +3 -1
  3. package/lib/client.js.map +1 -1
  4. package/lib/components/Body/index.test.js +1 -0
  5. package/lib/components/Body/index.test.js.map +1 -1
  6. package/lib/components/Flourish/index.js +1 -1
  7. package/lib/components/Flourish/index.js.map +1 -1
  8. package/lib/components/ImageSet/index.d.ts +1 -1
  9. package/lib/components/ImageSet/index.js +10 -2
  10. package/lib/components/ImageSet/index.js.map +1 -1
  11. package/lib/components/LiveBlogWrapper/index.js +1 -1
  12. package/lib/components/LiveBlogWrapper/index.js.map +1 -1
  13. package/lib/components/Topper/Picture.js +2 -2
  14. package/lib/components/Topper/Picture.js.map +1 -1
  15. package/lib/components/Topper/client/flourish-tracking.d.ts +14 -0
  16. package/lib/components/Topper/client/flourish-tracking.js +69 -0
  17. package/lib/components/Topper/client/flourish-tracking.js.map +1 -0
  18. package/lib/components/Topper/client/index.d.ts +2 -0
  19. package/lib/components/Topper/client/index.js +6 -0
  20. package/lib/components/Topper/client/index.js.map +1 -0
  21. package/lib/components/Topper/test/client/flourish-tracking.spec.d.ts +1 -0
  22. package/lib/components/Topper/test/client/flourish-tracking.spec.js +117 -0
  23. package/lib/components/Topper/test/client/flourish-tracking.spec.js.map +1 -0
  24. package/package.json +1 -1
  25. package/src/client.ts +1 -0
  26. package/src/components/Body/index.test.tsx +1 -0
  27. package/src/components/Flourish/index.tsx +5 -0
  28. package/src/components/Flourish/test/__snapshots__/snapshot.spec.tsx.snap +18 -0
  29. package/src/components/ImageSet/index.tsx +21 -11
  30. package/src/components/LiveBlogWrapper/index.tsx +1 -1
  31. package/src/components/Topper/Picture.tsx +2 -2
  32. package/src/components/Topper/client/flourish-tracking.ts +83 -0
  33. package/src/components/Topper/client/index.ts +3 -0
  34. package/src/components/Topper/test/client/flourish-tracking.spec.ts +135 -0
  35. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,83 @@
1
+ class FlourishTopperTracker {
2
+ private startTime: number
3
+ private totalVisibleTime: number
4
+ private timeElapsedSeconds: number | null
5
+ private component: Element | null
6
+ private id: string | null
7
+ private observer: IntersectionObserver | null
8
+
9
+ constructor() {
10
+ this.startTime = 0
11
+ this.totalVisibleTime = 0
12
+ this.timeElapsedSeconds = null
13
+ this.component = document.querySelector(
14
+ '[data-component-type="flourish-topper"]'
15
+ )
16
+ this.id = this.component?.getAttribute('data-component-id') || null
17
+ this.observer = null
18
+ }
19
+
20
+ init(): void {
21
+ if (!window.IntersectionObserver || !this.component) {
22
+ return
23
+ }
24
+
25
+ if (this.component) {
26
+ this.dispatchEvent('mount')
27
+ }
28
+
29
+ this.observer = new IntersectionObserver(this.onChange.bind(this), {
30
+ threshold: [1.0],
31
+ })
32
+ this.observer.observe(this.component)
33
+ }
34
+
35
+ private dispatchEvent(action: string): void {
36
+ let component: object = {
37
+ name: 'flourish-topper',
38
+ id: this.id,
39
+ }
40
+ if (this.timeElapsedSeconds) {
41
+ component = { ...component, timeElapsedSeconds: this.timeElapsedSeconds }
42
+ }
43
+ const event = new CustomEvent('oTracking.event', {
44
+ detail: {
45
+ category: 'component',
46
+ action: action,
47
+ component,
48
+ },
49
+ bubbles: true,
50
+ })
51
+ document.body.dispatchEvent(event)
52
+ }
53
+
54
+ private onChange(changes: IntersectionObserverEntry[]): void {
55
+ changes.forEach((change) => {
56
+ if (change.target !== this.component) {
57
+ return
58
+ }
59
+ if (change.isIntersecting || change.intersectionRatio >= 1) {
60
+ this.dispatchEvent('view')
61
+ this.startTime = performance.now()
62
+ }
63
+ if (!change.isIntersecting || change.intersectionRatio === 0) {
64
+ this.totalVisibleTime = performance.now() - this.startTime
65
+ this.timeElapsedSeconds = parseFloat(
66
+ (this.totalVisibleTime / 1000).toFixed(2)
67
+ )
68
+ this.dispatchEvent('stop-view')
69
+ this.totalVisibleTime = 0
70
+ this.timeElapsedSeconds = null
71
+ }
72
+ })
73
+ }
74
+
75
+ disconnect(): void {
76
+ if (this.observer && this.component) {
77
+ this.observer.unobserve(this.component)
78
+ this.observer.disconnect()
79
+ }
80
+ }
81
+ }
82
+
83
+ export { FlourishTopperTracker }
@@ -0,0 +1,3 @@
1
+ import { FlourishTopperTracker } from './flourish-tracking'
2
+
3
+ export { FlourishTopperTracker }
@@ -0,0 +1,135 @@
1
+ import { FlourishTopperTracker } from '../../client'
2
+
3
+ // const CustomEventMock = jest.fn()
4
+ // global.CustomEvent = CustomEventMock
5
+
6
+ describe('FlourishTopperTracker', () => {
7
+ let mockIntersectionObserver: jest.Mock
8
+ let mockObserve: jest.Mock
9
+ let mockUnobserve: jest.Mock
10
+ let mockDisconnect: jest.Mock
11
+ let mockComponent: HTMLElement
12
+ let dispatchEventSpy: jest.SpyInstance
13
+
14
+ beforeEach(() => {
15
+ mockObserve = jest.fn()
16
+ mockUnobserve = jest.fn()
17
+ mockDisconnect = jest.fn()
18
+
19
+ mockIntersectionObserver = jest.fn(() => {
20
+ return {
21
+ observe: mockObserve,
22
+ unobserve: mockUnobserve,
23
+ disconnect: mockDisconnect,
24
+ }
25
+ })
26
+
27
+ window.IntersectionObserver = mockIntersectionObserver
28
+
29
+ mockComponent = document.createElement('div')
30
+ mockComponent.setAttribute('data-component-type', 'flourish-topper')
31
+ mockComponent.setAttribute('data-component-id', 'test-id')
32
+ document.body.appendChild(mockComponent)
33
+
34
+ jest.spyOn(document, 'querySelector').mockReturnValue(mockComponent)
35
+ jest
36
+ .spyOn(performance, 'now')
37
+ .mockImplementationOnce(() => 1)
38
+ .mockImplementationOnce(() => 12344)
39
+ dispatchEventSpy = jest.spyOn(document.body, 'dispatchEvent')
40
+ })
41
+
42
+ afterEach(() => {
43
+ jest.clearAllMocks()
44
+ document.body.removeChild(mockComponent)
45
+ })
46
+
47
+ it('should initialise and observe the component', () => {
48
+ const tracker = new FlourishTopperTracker()
49
+ tracker.init()
50
+
51
+ expect(mockIntersectionObserver).toHaveBeenCalled()
52
+ expect(mockObserve).toHaveBeenCalledWith(mockComponent)
53
+ })
54
+
55
+ it('should dispatch a mount event on intialisation', () => {
56
+ const tracker = new FlourishTopperTracker()
57
+ tracker.init()
58
+
59
+ const mountEvent = dispatchEventSpy.mock.calls[0][0] as CustomEvent
60
+ expect(mountEvent.type).toBe('oTracking.event')
61
+ expect(mountEvent.detail).toEqual({
62
+ category: 'component',
63
+ action: 'mount',
64
+ component: {
65
+ name: 'flourish-topper',
66
+ id: 'test-id',
67
+ },
68
+ })
69
+ })
70
+
71
+ it('should handle component visibility changes', () => {
72
+ const tracker = new FlourishTopperTracker()
73
+ tracker.init()
74
+
75
+ const mockChanges: IntersectionObserverEntry[] = [
76
+ {
77
+ target: mockComponent,
78
+ isIntersecting: true,
79
+ intersectionRatio: 1.0,
80
+ boundingClientRect: {} as DOMRectReadOnly,
81
+ intersectionRect: {} as DOMRectReadOnly,
82
+ rootBounds: null,
83
+ time: 1,
84
+ },
85
+ ]
86
+
87
+ tracker['onChange'](mockChanges)
88
+
89
+ const viewEvent = dispatchEventSpy.mock.calls[1][0] as CustomEvent
90
+ expect(viewEvent.type).toBe('oTracking.event')
91
+ expect(viewEvent.detail).toEqual({
92
+ category: 'component',
93
+ action: 'view',
94
+ component: {
95
+ name: 'flourish-topper',
96
+ id: 'test-id',
97
+ },
98
+ })
99
+
100
+ const mockChangesAfter: IntersectionObserverEntry[] = [
101
+ {
102
+ target: mockComponent,
103
+ isIntersecting: false,
104
+ intersectionRatio: 0.0,
105
+ boundingClientRect: {} as DOMRectReadOnly,
106
+ intersectionRect: {} as DOMRectReadOnly,
107
+ rootBounds: null,
108
+ time: 12.3444,
109
+ },
110
+ ]
111
+
112
+ tracker['onChange'](mockChangesAfter)
113
+
114
+ const stopViewEvent = dispatchEventSpy.mock.calls[2][0] as CustomEvent
115
+ expect(stopViewEvent.type).toBe('oTracking.event')
116
+ expect(stopViewEvent.detail).toEqual({
117
+ category: 'component',
118
+ action: 'stop-view',
119
+ component: {
120
+ name: 'flourish-topper',
121
+ id: 'test-id',
122
+ timeElapsedSeconds: 12.34,
123
+ },
124
+ })
125
+ })
126
+
127
+ it('should disconnect the observer', () => {
128
+ const tracker = new FlourishTopperTracker()
129
+ tracker.init()
130
+ tracker.disconnect()
131
+
132
+ expect(mockUnobserve).toHaveBeenCalledWith(mockComponent)
133
+ expect(mockDisconnect).toHaveBeenCalled()
134
+ })
135
+ })