Skip to content

Commit 7fc290a

Browse files
committed
Implement new analytics composable with tests
1 parent 90f705f commit 7fc290a

3 files changed

Lines changed: 508 additions & 160 deletions

File tree

contentcuration/contentcuration/frontend/shared/analytics/plugin.js

Lines changed: 2 additions & 160 deletions
Original file line numberDiff line numberDiff line change
@@ -1,164 +1,6 @@
11
import Vue from 'vue';
2-
import isFunction from 'lodash/isFunction';
3-
4-
/**
5-
* Analytics class for handling anything analytics related, exposed in Vue as $analytics
6-
*/
7-
class Analytics {
8-
/**
9-
* GTM uses an array-like structure called the `dataLayer`
10-
*
11-
* @param {Array} dataLayer
12-
*/
13-
constructor(dataLayer) {
14-
this.counter = 0;
15-
this.dataLayer = dataLayer;
16-
this.lastLength = 0;
17-
18-
// add interval to trigger resets every 5 minutes
19-
this.resetInterval = setInterval(
20-
() => {
21-
this.reset();
22-
},
23-
5 * 60 * 1000,
24-
);
25-
}
26-
27-
/**
28-
* Resets the datalayer and cleans up elements
29-
*/
30-
reset() {
31-
// If the dataLayer hasn't changed since our last reset, skip
32-
if (this.dataLayer.length === this.lastLength) {
33-
if (process.env.NODE_ENV !== 'production') {
34-
// eslint-disable-next-line no-console
35-
console.info('Skipping Analytics.reset()');
36-
}
37-
return;
38-
}
39-
40-
if (process.env.NODE_ENV !== 'production') {
41-
// eslint-disable-next-line no-console
42-
console.info('Analytics.reset()');
43-
}
44-
45-
// Add local variable so the pushed reset function can access the datalayer
46-
const dataLayer = this.dataLayer;
47-
const resetCounter = this.counter;
48-
this.counter++;
49-
50-
// This can't use an arrow function, it will be called with a different
51-
// context to access `this.reset()`
52-
const dataLayerReset = function () {
53-
// See https://developers.google.com/tag-platform/devguides/datalayer#reset
54-
this.reset();
55-
56-
for (const item of dataLayer) {
57-
// Do our own reset of the `gtm.element` variable
58-
if (item['gtm.element']) {
59-
item['gtm.element'] = null;
60-
}
61-
// Be sure we stop when we reach this function's position in the dataLayer. The dataLayer
62-
// is like a FIFO queue, so by queuing this function and having the GTM execute it, we're
63-
// ensuring that everything prior to this function was processed. For this reason, we stop
64-
// once we reach the position of this function to avoid manipulating events that have been
65-
// added afterwards
66-
if (isFunction(item) && item['counter'] === resetCounter) {
67-
break;
68-
}
69-
}
70-
};
71-
dataLayerReset.counter = resetCounter;
72-
73-
this.dataLayer.push(dataLayerReset);
74-
this.lastLength = this.dataLayer.length;
75-
}
76-
77-
/**
78-
* Push data onto the datalayer
79-
* @param {object|function} args
80-
*/
81-
push(...args) {
82-
this.dataLayer.push(...args);
83-
}
84-
85-
/**
86-
* Push an event into the dataLayer
87-
*
88-
* These events could be standard GA events, or custom events that trigger tags within GTM
89-
*
90-
* @param {String} event
91-
* @param {{:*}} data
92-
*/
93-
trackEvent(event, data = {}) {
94-
this.push({
95-
...data,
96-
event,
97-
});
98-
99-
if (process.env.NODE_ENV !== 'production') {
100-
// eslint-disable-next-line no-console
101-
console.info(`Analytics.trackEvent("${event}", ${JSON.stringify(data)})`);
102-
}
103-
}
104-
105-
/**
106-
* Tracks event with specific action
107-
*
108-
* @param {String} event
109-
* @param {String} eventAction
110-
* @param {{:*}} data
111-
*/
112-
trackAction(event, eventAction, data = {}) {
113-
this.trackEvent(event, { ...data, eventAction });
114-
}
115-
116-
/**
117-
* Tracks event with click action
118-
*
119-
* @param {String} event
120-
* @param {String} eventLabel
121-
* @param {{:*}} data
122-
*/
123-
trackClick(event, eventLabel, data = {}) {
124-
this.trackAction(event, 'Click', { ...data, eventLabel });
125-
}
126-
127-
/**
128-
* Pushes current channel data into the data layer for GTM to associate with events
129-
*
130-
* @param {Object} channel
131-
* @param {Boolean} staging
132-
*/
133-
trackCurrentChannel(channel, staging = false) {
134-
// We only want to track once, since a page reload is required to open a new channel
135-
const hasCurrentChannel = this.dataLayer.find(data => Boolean(data['currentChannel']));
136-
if (hasCurrentChannel) {
137-
return;
138-
}
139-
140-
this.push({
141-
currentChannel: {
142-
id: channel.id,
143-
name: channel.name,
144-
lastPublished: channel.last_published,
145-
isPublic: channel.public,
146-
allowEdit: channel.edit,
147-
staging,
148-
// Skipping this field for now as we don't have this info on the frontend by default
149-
// hasEditors:
150-
},
151-
});
152-
}
153-
154-
/**
155-
* Cleans up the datalayer and removes the interval to call dataLayer.reset()
156-
*/
157-
destroy() {
158-
clearInterval(this.resetInterval);
159-
this.dataLayer = null;
160-
}
161-
}
2+
// the new home for analytics
3+
import { Analytics } from 'shared/composables/useAnalytics';
1624

1635
/**
1646
* @param Vue

0 commit comments

Comments
 (0)