Commit 6f54ea12 by James Ooi

Add support for custom dimensions and metrics

parent 00b0cb82
# Foresight
### Foresight is an analytics library that allows for declarative event tracking.
* Lightweight (<3kb minified and gzipped)
* Lightweight (<4kb minified and gzipped)
* Declarative event tracking
* Performant
......@@ -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
```js
......
......@@ -122,7 +122,6 @@ __webpack_require__(1);
var Utils = __webpack_require__(2);
/**
* Foresight is an analytics library that allows for declarative event tracking.
* @class
*/
var Foresight = /** @class */ (function () {
/**
......@@ -220,11 +219,7 @@ var Foresight = /** @class */ (function () {
return this.options.sendEventFn(data);
}
if (typeof window['gtag'] === 'function') {
return window['gtag']('event', data.action, {
'event_label': data.label,
'event_category': data.category,
'non_interaction': !data.interaction,
});
return window['gtag']('event', data.action, __assign({ 'event_label': data.label, 'event_category': data.category, 'non_interaction': !data.interaction }, data.metrics, data.dimensions));
}
if (typeof window['ga'] === 'function') {
return window['ga']('send', {
......@@ -233,7 +228,7 @@ var Foresight = /** @class */ (function () {
eventAction: data.action,
eventLabel: data.label,
nonInteraction: !data.interaction,
});
}, __assign({}, data.metrics, data.dimensions));
}
throw 'No available analytics backend found. Has Google Analytics been loaded yet?';
};
......@@ -290,6 +285,31 @@ var Foresight = /** @class */ (function () {
Foresight.prototype._onTrackedClick = function (element, event) {
var s = element.getAttribute('data-track');
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;
if (element.getAttribute('data-track:non-interaction') !== null) {
data.interaction = false;
......@@ -303,6 +323,31 @@ var Foresight = /** @class */ (function () {
Foresight.prototype._onTrackedView = function (element, observer) {
var s = element.getAttribute('data-track-view');
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;
if (element.getAttribute('data-track-view:interaction') !== null) {
data.interaction = true;
......
......@@ -12,7 +12,11 @@
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-93571739-3');
gtag('config', 'UA-93571739-3', {
'custom_map': {
'metric1': 'campaign-detached-fac-aug-2018'
}
});
</script>
</head>
......@@ -32,7 +36,12 @@
data-track-view="views;landing : view : 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
</a>
</div>
......
......@@ -9,8 +9,6 @@
import 'intersection-observer';
import * as Utils from './utils';
declare var gtag: (action: string, ...args: any[]) => any;
/**
* Available options for configuring Foresight.
......@@ -32,20 +30,22 @@ interface ForesightConfig {
sendEventFn?: (data: EventData) => any
}
/**
* Represents a particular event's data.
* Represents an event data.
*/
interface EventData {
category: string
action: string
label: string
interaction: boolean
metrics?: { [key: string]: any }
dimensions?: { [key: string]: any }
}
/**
* Foresight is an analytics library that allows for declarative event tracking.
* @class
*/
class Foresight {
......@@ -178,6 +178,8 @@ class Foresight {
'event_label': data.label,
'event_category': data.category,
'non_interaction': !data.interaction,
...data.metrics,
...data.dimensions
});
}
......@@ -188,7 +190,7 @@ class Foresight {
eventAction: data.action,
eventLabel: data.label,
nonInteraction: !data.interaction,
})
}, { ...data.metrics, ...data.dimensions });
}
throw 'No available analytics backend found. Has Google Analytics been loaded yet?';
......@@ -198,7 +200,7 @@ class Foresight {
* Parse an event string and returns a `EventData` object.
* @private
*/
private _parseEventString(eventString: string): EventData {
private _parseEventString(eventString: string): Partial<EventData> {
const split = eventString.split(';');
let [ category, action, label ] = split;
......@@ -256,12 +258,39 @@ class Foresight {
const s = element.getAttribute('data-track');
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;
if (element.getAttribute('data-track:non-interaction') !== null) {
data.interaction = false;
}
this.send(data);
this.send(<EventData> data);
}
/**
......@@ -272,12 +301,39 @@ class Foresight {
const s = element.getAttribute('data-track-view');
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;
if (element.getAttribute('data-track-view:interaction') !== null) {
data.interaction = true;
}
this.send(data);
this.send(<EventData> data);
}
}
......
......@@ -22,17 +22,22 @@ interface ForesightConfig {
sendEventFn?: (data: EventData) => any;
}
/**
* Represents a particular event's data.
* Represents an event data.
*/
interface EventData {
category: string;
action: string;
label: string;
interaction: boolean;
metrics?: {
[key: string]: any;
};
dimensions?: {
[key: string]: any;
};
}
/**
* Foresight is an analytics library that allows for declarative event tracking.
* @class
*/
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