Commit 6f54ea12 by James Ooi

Add support for custom dimensions and metrics

parent 00b0cb82
# Foresight # Foresight
### Foresight is an analytics library that allows for declarative event tracking. ### Foresight is an analytics library that allows for declarative event tracking.
* Lightweight (<3kb minified and gzipped) * Lightweight (<4kb minified and gzipped)
* Declarative event tracking * Declarative event tracking
* Performant * Performant
...@@ -92,6 +92,23 @@ The above markup would send the following event data when the `div` enters the v ...@@ -92,6 +92,23 @@ The above markup would send the following event data when the `div` enters the v
--- ---
### Tracking custom metrics and custom dimensions
Custom metrics and dimensions can be tracked by attaching additional HTML attributes
under the `data-track:metrics:<metric_name>` or `data-track:dimensions:<metric_name>` namespace. The value of the attribute will be the value of the metric/dimension. You can have multiple custom metrics/dimensions per element.
The follow example will send a value of `1` to the custom metric in the `metric1` index.
```html
<button data-track="clicks;register-btn" data-track:metrics:metric1="1">
Register
</button
```
Refer to Google Analytics documentation on what `metric_name` should be.
* **[analytics.js](https://developers.google.com/analytics/devguides/collection/analyticsjs/custom-dims-mets)**
* **[gtag.js](https://developers.google.com/analytics/devguides/collection/gtagjs/custom-dims-mets)**`metric_name` should be the name provided in `custom_map`
## Configuration ## Configuration
```js ```js
......
...@@ -122,7 +122,6 @@ __webpack_require__(1); ...@@ -122,7 +122,6 @@ __webpack_require__(1);
var Utils = __webpack_require__(2); var Utils = __webpack_require__(2);
/** /**
* Foresight is an analytics library that allows for declarative event tracking. * Foresight is an analytics library that allows for declarative event tracking.
* @class
*/ */
var Foresight = /** @class */ (function () { var Foresight = /** @class */ (function () {
/** /**
...@@ -220,11 +219,7 @@ var Foresight = /** @class */ (function () { ...@@ -220,11 +219,7 @@ var Foresight = /** @class */ (function () {
return this.options.sendEventFn(data); return this.options.sendEventFn(data);
} }
if (typeof window['gtag'] === 'function') { if (typeof window['gtag'] === 'function') {
return window['gtag']('event', data.action, { return window['gtag']('event', data.action, __assign({ 'event_label': data.label, 'event_category': data.category, 'non_interaction': !data.interaction }, data.metrics, data.dimensions));
'event_label': data.label,
'event_category': data.category,
'non_interaction': !data.interaction,
});
} }
if (typeof window['ga'] === 'function') { if (typeof window['ga'] === 'function') {
return window['ga']('send', { return window['ga']('send', {
...@@ -233,7 +228,7 @@ var Foresight = /** @class */ (function () { ...@@ -233,7 +228,7 @@ var Foresight = /** @class */ (function () {
eventAction: data.action, eventAction: data.action,
eventLabel: data.label, eventLabel: data.label,
nonInteraction: !data.interaction, nonInteraction: !data.interaction,
}); }, __assign({}, data.metrics, data.dimensions));
} }
throw 'No available analytics backend found. Has Google Analytics been loaded yet?'; throw 'No available analytics backend found. Has Google Analytics been loaded yet?';
}; };
...@@ -290,6 +285,31 @@ var Foresight = /** @class */ (function () { ...@@ -290,6 +285,31 @@ var Foresight = /** @class */ (function () {
Foresight.prototype._onTrackedClick = function (element, event) { Foresight.prototype._onTrackedClick = function (element, event) {
var s = element.getAttribute('data-track'); var s = element.getAttribute('data-track');
var data = this._parseEventString(s); var data = this._parseEventString(s);
// Get custom metrics
data.metrics = Utils
.toArray(element.attributes)
.filter(function (attr) { return attr.name.startsWith('data-track:metrics'); })
.reduce(function (acc, attr) {
var key = attr.name.replace(/data-track:metrics:/, '');
var value = attr.value === "" ? 1 : attr.value;
acc[key] = value;
return acc;
}, {});
// Get custom dimensions
data.dimensions = Utils
.toArray(element.attributes)
.filter(function (attr) { return attr.name.startsWith('data-track:dimensions'); })
.reduce(function (acc, attr) {
if (attr.value === "") {
console.warn("No dimension value provided for " + attr.name);
return acc;
}
var key = attr.name.replace(/data-track:dimensions:/, '');
var value = attr.value;
acc[key] = value;
return acc;
}, {});
// Get interaction flag
data.interaction = this.options.clicksAreInteractions; data.interaction = this.options.clicksAreInteractions;
if (element.getAttribute('data-track:non-interaction') !== null) { if (element.getAttribute('data-track:non-interaction') !== null) {
data.interaction = false; data.interaction = false;
...@@ -303,6 +323,31 @@ var Foresight = /** @class */ (function () { ...@@ -303,6 +323,31 @@ var Foresight = /** @class */ (function () {
Foresight.prototype._onTrackedView = function (element, observer) { Foresight.prototype._onTrackedView = function (element, observer) {
var s = element.getAttribute('data-track-view'); var s = element.getAttribute('data-track-view');
var data = this._parseEventString(s); var data = this._parseEventString(s);
// Get custom metrics
data.metrics = Utils
.toArray(element.attributes)
.filter(function (attr) { return attr.name.startsWith('data-track-view:metrics'); })
.reduce(function (acc, attr) {
var key = attr.name.replace(/data-track-view:metrics:/, '');
var value = attr.value === "" ? 1 : attr.value;
acc[key] = value;
return acc;
}, {});
// Get custom dimensions
data.dimensions = Utils
.toArray(element.attributes)
.filter(function (attr) { return attr.name.startsWith('data-track-view:dimensions'); })
.reduce(function (acc, attr) {
if (attr.value === "") {
console.warn("No dimension value provided for " + attr.name);
return acc;
}
var key = attr.name.replace(/data-track-view:dimensions:/, '');
var value = attr.value;
acc[key] = value;
return acc;
}, {});
// Get interaction flag
data.interaction = this.options.viewsAreInteractions; data.interaction = this.options.viewsAreInteractions;
if (element.getAttribute('data-track-view:interaction') !== null) { if (element.getAttribute('data-track-view:interaction') !== null) {
data.interaction = true; data.interaction = true;
......
...@@ -12,7 +12,11 @@ ...@@ -12,7 +12,11 @@
window.dataLayer = window.dataLayer || []; window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);} function gtag(){dataLayer.push(arguments);}
gtag('js', new Date()); gtag('js', new Date());
gtag('config', 'UA-93571739-3'); gtag('config', 'UA-93571739-3', {
'custom_map': {
'metric1': 'campaign-detached-fac-aug-2018'
}
});
</script> </script>
</head> </head>
...@@ -32,7 +36,12 @@ ...@@ -32,7 +36,12 @@
data-track-view="views;landing : view : facebook" data-track-view="views;landing : view : facebook"
data-track="landing : section : facebook" data-track="landing : section : facebook"
> >
<a href="https://facebook.com" class="btn btn-secondary" data-track="clicks;landing" data-track:non-interaction> <a
href="https://facebook.com"
class="btn btn-secondary"
data-track="clicks;landing"
data-track:non-interaction
data-track:metrics:campaign-detached-fac-aug-2018>
Facebook Facebook
</a> </a>
</div> </div>
......
...@@ -9,8 +9,6 @@ ...@@ -9,8 +9,6 @@
import 'intersection-observer'; import 'intersection-observer';
import * as Utils from './utils'; import * as Utils from './utils';
declare var gtag: (action: string, ...args: any[]) => any;
/** /**
* Available options for configuring Foresight. * Available options for configuring Foresight.
...@@ -32,20 +30,22 @@ interface ForesightConfig { ...@@ -32,20 +30,22 @@ interface ForesightConfig {
sendEventFn?: (data: EventData) => any sendEventFn?: (data: EventData) => any
} }
/** /**
* Represents a particular event's data. * Represents an event data.
*/ */
interface EventData { interface EventData {
category: string category: string
action: string action: string
label: string label: string
interaction: boolean interaction: boolean
metrics?: { [key: string]: any }
dimensions?: { [key: string]: any }
} }
/** /**
* Foresight is an analytics library that allows for declarative event tracking. * Foresight is an analytics library that allows for declarative event tracking.
* @class
*/ */
class Foresight { class Foresight {
...@@ -178,6 +178,8 @@ class Foresight { ...@@ -178,6 +178,8 @@ class Foresight {
'event_label': data.label, 'event_label': data.label,
'event_category': data.category, 'event_category': data.category,
'non_interaction': !data.interaction, 'non_interaction': !data.interaction,
...data.metrics,
...data.dimensions
}); });
} }
...@@ -188,7 +190,7 @@ class Foresight { ...@@ -188,7 +190,7 @@ class Foresight {
eventAction: data.action, eventAction: data.action,
eventLabel: data.label, eventLabel: data.label,
nonInteraction: !data.interaction, nonInteraction: !data.interaction,
}) }, { ...data.metrics, ...data.dimensions });
} }
throw 'No available analytics backend found. Has Google Analytics been loaded yet?'; throw 'No available analytics backend found. Has Google Analytics been loaded yet?';
...@@ -198,7 +200,7 @@ class Foresight { ...@@ -198,7 +200,7 @@ class Foresight {
* Parse an event string and returns a `EventData` object. * Parse an event string and returns a `EventData` object.
* @private * @private
*/ */
private _parseEventString(eventString: string): EventData { private _parseEventString(eventString: string): Partial<EventData> {
const split = eventString.split(';'); const split = eventString.split(';');
let [ category, action, label ] = split; let [ category, action, label ] = split;
...@@ -256,12 +258,39 @@ class Foresight { ...@@ -256,12 +258,39 @@ class Foresight {
const s = element.getAttribute('data-track'); const s = element.getAttribute('data-track');
const data = this._parseEventString(s); const data = this._parseEventString(s);
// Get custom metrics
data.metrics = Utils
.toArray<Attr>(element.attributes)
.filter(attr => attr.name.startsWith('data-track:metrics'))
.reduce((acc, attr) => {
const key = attr.name.replace(/data-track:metrics:/, '');
const value = attr.value === "" ? 1 : attr.value;
acc[key] = value;
return acc;
}, {});
// Get custom dimensions
data.dimensions = Utils
.toArray<Attr>(element.attributes)
.filter(attr => attr.name.startsWith('data-track:dimensions'))
.reduce((acc, attr) => {
if (attr.value === "") {
console.warn(`No dimension value provided for ${attr.name}`);
return acc;
}
const key = attr.name.replace(/data-track:dimensions:/, '');
const value = attr.value;
acc[key] = value;
return acc;
}, {});
// Get interaction flag
data.interaction = this.options.clicksAreInteractions; data.interaction = this.options.clicksAreInteractions;
if (element.getAttribute('data-track:non-interaction') !== null) { if (element.getAttribute('data-track:non-interaction') !== null) {
data.interaction = false; data.interaction = false;
} }
this.send(data); this.send(<EventData> data);
} }
/** /**
...@@ -272,12 +301,39 @@ class Foresight { ...@@ -272,12 +301,39 @@ class Foresight {
const s = element.getAttribute('data-track-view'); const s = element.getAttribute('data-track-view');
const data = this._parseEventString(s); const data = this._parseEventString(s);
// Get custom metrics
data.metrics = Utils
.toArray<Attr>(element.attributes)
.filter(attr => attr.name.startsWith('data-track-view:metrics'))
.reduce((acc, attr) => {
const key = attr.name.replace(/data-track-view:metrics:/, '');
const value = attr.value === "" ? 1 : attr.value;
acc[key] = value;
return acc;
}, {});
// Get custom dimensions
data.dimensions = Utils
.toArray<Attr>(element.attributes)
.filter(attr => attr.name.startsWith('data-track-view:dimensions'))
.reduce((acc, attr) => {
if (attr.value === "") {
console.warn(`No dimension value provided for ${attr.name}`);
return acc;
}
const key = attr.name.replace(/data-track-view:dimensions:/, '');
const value = attr.value;
acc[key] = value;
return acc;
}, {});
// Get interaction flag
data.interaction = this.options.viewsAreInteractions; data.interaction = this.options.viewsAreInteractions;
if (element.getAttribute('data-track-view:interaction') !== null) { if (element.getAttribute('data-track-view:interaction') !== null) {
data.interaction = true; data.interaction = true;
} }
this.send(data); this.send(<EventData> data);
} }
} }
......
...@@ -22,17 +22,22 @@ interface ForesightConfig { ...@@ -22,17 +22,22 @@ interface ForesightConfig {
sendEventFn?: (data: EventData) => any; sendEventFn?: (data: EventData) => any;
} }
/** /**
* Represents a particular event's data. * Represents an event data.
*/ */
interface EventData { interface EventData {
category: string; category: string;
action: string; action: string;
label: string; label: string;
interaction: boolean; interaction: boolean;
metrics?: {
[key: string]: any;
};
dimensions?: {
[key: string]: any;
};
} }
/** /**
* Foresight is an analytics library that allows for declarative event tracking. * Foresight is an analytics library that allows for declarative event tracking.
* @class
*/ */
declare class Foresight { declare class Foresight {
/** /**
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment