Skip to content

Commit

Permalink
Support enhanced eCommerce plugin for GA (#139)
Browse files Browse the repository at this point in the history
  • Loading branch information
riavalon authored and ttmarek committed Oct 18, 2017
1 parent f6973c2 commit 4801d60
Show file tree
Hide file tree
Showing 6 changed files with 178 additions and 18 deletions.
57 changes: 55 additions & 2 deletions docs/targets/google-analytics.md
Expand Up @@ -80,13 +80,66 @@ to use a custom tracker object to track events. Ex:
**Note:**
Google Analytics will _fail silently_ if you try to use these events without adding the
require call in your initial tracking code. It is also **not** recommended to use GA's
basic analytics plugin if you're also going to use the enhanced ecommerce plugin. At
this time, we do not have support for the enhanced ecommerce plugin.
basic analytics plugin if you're also going to use the enhanced ecommerce plugin.

See the google developer docs for
[more information on creating a custom tracker](https://developers.google.com/analytics/devguides/collection/analyticsjs/creating-trackers) and
for [using the google analytics ecommerce plugin](https://developers.google.com/analytics/devguides/collection/analyticsjs/ecommerce)

### Google Analytics Enhanced Ecommerce Plugin

The Google Analytics target also has support for the enhanced ecommerce plugin.
It should be noted that it isn't recommended to use both the enhanced plugin and
basic plugin together without multiple trackers. Please refer to the [enhanced
ecommerce plugin documentation here](https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce)

To use the enhanced ecommerce plugin, first add this line to the end of your
tracking snippet: `ga('require', 'ec');` Again, this must be added after the
create call with your Google Analytics UA code.

This allows you to use these enhanced ecommerce events:

- addProduct
- addImpression
- addPromo
- addAction

The basic actions are used as well:

- ecommSend
- ecommClear
- addItem
- addTransaction

In order to tell Redux Beacon which version of the ecommerce plugin you're using,
your events should pass the `ecommType` key:

```JavaScript
const events = [
{
hitType: 'addImpression',
ecommType: 'enhanced',
// ...
},
];
```

For adding custom actions, you can specify the type of action using the key `actionName`.

```JavaScript
const events = [
{
hitType: 'addAction',
ecommType: 'enhanced',
actionName: 'click',
// ...
},
];
```

Everything else works the same as in the basic ecommerce plugin. Please see [the
developer documentation for more information on how these events work.](https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce)

### Examples
* [Google Analytics (Redux) Example](https://github.com/rangle/redux-beacon/tree/master/examples/google-analytics)
* [Google Analytics (ngrx) Example](https://github.com/rangle/redux-beacon/tree/master/examples/google-analytics-ngrx)
Expand Down
50 changes: 47 additions & 3 deletions src/targets/google-analytics/__tests__/google-analytics.test.js
Expand Up @@ -26,7 +26,7 @@ describe('GoogleAnalytics(events)', () => {
expect(window.ga).toHaveBeenCalledWith('send', events[0]);
expect(window.ga).toHaveBeenCalledWith('send', events[1]);
});

describe('with events that have trackers', () => {
it('calls window.ga("<tracker>:send", <event>) for each event', () => {
const events = [
Expand All @@ -46,10 +46,10 @@ describe('GoogleAnalytics(events)', () => {
tracker: 'customApp',
},
];

window.ga = jest.fn();
GoogleAnalytics(events);

expect(window.ga).toHaveBeenCalledWith('testHub.set', 'page', events[0].page);
expect(window.ga).toHaveBeenCalledWith('testHub.send', events[0]);
expect(window.ga).toHaveBeenCalledWith('customApp.send', events[1]);
Expand Down Expand Up @@ -163,4 +163,48 @@ describe('GoogleAnalytics(events)', () => {
expect(window.ga).toHaveBeenCalledWith('ecommerce:send');
});
});

describe('when ga enhanced ecommerce is being used', () => {
beforeEach(() => {
window.ga = jest.fn();
});

it('should call ecommerce events using enhanced prefix', () => {
const events = [
{
hitType: 'ecommClear',
ecommType: 'enhanced',
},
];

GoogleAnalytics(events);
expect(window.ga).toHaveBeenCalledWith('ec:clear');
});

['addProduct', 'addImpression', 'addPromo'].forEach((hitType) => {
test(`hitType ${hitType} should be handled by ecomm plugin`, () => {
const events = [
{
hitType,
ecommType: 'enhanced',
},
];
GoogleAnalytics(events);
expect(window.ga).toHaveBeenCalledWith(`ec:${hitType}`, {});
});
});

it('should pass in action type to addAction call', () => {
const events = [
{
hitType: 'addAction',
ecommType: 'enhanced',
actionName: 'click',
},
];

GoogleAnalytics(events);
expect(window.ga).toHaveBeenCalledWith('ec:addAction', 'click', {});
});
});
});
15 changes: 10 additions & 5 deletions src/targets/google-analytics/google-analytics.js
Expand Up @@ -10,13 +10,18 @@ function GoogleAnalytics(events) {
events.forEach((event) => {
const customTrackerId = event.customTrackerId || event.tracker;
const trackerId = !!customTrackerId && !!customTrackerId.trim() ? `${customTrackerId}.` : '';

const ecommPluginType = event.ecommType === 'enhanced' ? 'ec' : 'ecommerce';

if (isEcommEvent(event)) {
const callEvent = type => ({
addItem: () => window.ga(`${trackerId}ecommerce:addItem`, filterEcommEvents(event)),
addTransaction: () => window.ga(`${trackerId}ecommerce:addTransaction`, filterEcommEvents(event)),
ecommClear: () => window.ga(`${trackerId}ecommerce:clear`),
ecommSend: () => window.ga(`${trackerId}ecommerce:send`),
addItem: () => window.ga(`${trackerId}${ecommPluginType}:addItem`, filterEcommEvents(event)),
addTransaction: () => window.ga(`${trackerId}${ecommPluginType}:addTransaction`, filterEcommEvents(event)),
addImpression: () => window.ga(`${trackerId}${ecommPluginType}:addImpression`, filterEcommEvents(event)),
addProduct: () => window.ga(`${trackerId}${ecommPluginType}:addProduct`, filterEcommEvents(event)),
addPromo: () => window.ga(`${trackerId}${ecommPluginType}:addPromo`, filterEcommEvents(event)),
addAction: () => window.ga(`${trackerId}${ecommPluginType}:addAction`, event.actionName, filterEcommEvents(event)),
ecommClear: () => window.ga(`${trackerId}${ecommPluginType}:clear`),
ecommSend: () => window.ga(`${trackerId}${ecommPluginType}:send`),
}[type])();

callEvent(event.hitType);
Expand Down
52 changes: 47 additions & 5 deletions src/targets/google-analytics/index.d.ts
Expand Up @@ -30,6 +30,12 @@ export interface SocialInteraction {
socialTarget: string,
}

export interface Exception {
hitType: 'exception',
exDescription?: string,
exFatal?: boolean,
}

export interface EcommItem {
hitType: 'addItem',
id: string,
Expand All @@ -43,14 +49,50 @@ export interface EcommItem {
export interface EcommTransaction {
hitType: 'addTransaction',
id: string,
affliation?: string,
affilation?: string,
revenue?: number,
shipping?: number,
tax?: number,
}

export interface Exception {
hitType: 'exception',
exDescription?: string,
exFatal?: boolean,
export interface EcommImpression {
id: string,
name: string,
list?: string,
brand?: string,
category?: string,
variant?: string,
position?: number,
currency?: number,
}

export interface EcommProduct {
id: string,
name: string,
brand?: string,
category?: string,
variant?: string,
price?: number,
quantity?: number,
coupon?: string,
position?: number,
}

export interface EcommPromotion {
id: string,
name: string,
creative?: string,
position?: string,
}

export interface EcommAction {
id: string,
affiliation?: string,
revenue?: number,
tax?: number,
shipping?: number,
coupon?: string,
list?: string,
step?: number,
option?: string,
}
10 changes: 8 additions & 2 deletions src/utils/__tests__/ga-ecomm-helpers.test.js
Expand Up @@ -3,14 +3,16 @@ const { filterEcommEvents, isEcommEvent } = require('../');
describe('Util: Filter Ecommerce Events', () => {
const [name, id, revenue] = ['fooname', 'fooid', 523.55];
const baseEvent = {
hitType: 'addTransaction',
hitType: 'addAction',
customTrackerId: 'myTracker',
ecommType: 'enhanced',
actionName: 'click',
id,
name,
revenue,
};

it('should return an object with hitType and trackerId stripped out', () => {
it('should return an object with unnecessary keys stripped out', () => {
const result = filterEcommEvents(baseEvent);
const expected = {
id,
Expand All @@ -26,6 +28,10 @@ describe('Util: Filter Ecommerce Events', () => {
events = [
{ hitType: 'addTransaction', id: 'myid' },
{ hitType: 'addItem', id: 'myitemid', name: 'my item' },
{ hitType: 'addProduct', id: 'myitemid', name: 'my item' },
{ hitType: 'addImpression', id: 'myitemid', name: 'my item' },
{ hitType: 'addPromo', id: 'myitemid', name: 'my item' },
{ hitType: 'addAction', id: 'myitemid', name: 'my item' },
{ hitType: 'ecommClear' },
{ hitType: 'ecommSend' },
];
Expand Down
12 changes: 11 additions & 1 deletion src/utils/ga-ecomm-helpers.js
@@ -1,7 +1,13 @@
const filterEcommEvents = (obj) => {
const newObj = {};
const invalidKeys = [
'hitType',
'customTrackerId',
'ecommType',
'actionName',
];
Object.keys(obj).forEach((key) => {
if (key !== 'hitType' && key !== 'customTrackerId') {
if (invalidKeys.indexOf(key) === -1) {
newObj[key] = obj[key];
}
});
Expand All @@ -11,6 +17,10 @@ const filterEcommEvents = (obj) => {
const isEcommEvent = event => [
'addTransaction',
'addItem',
'addImpression',
'addProduct',
'addPromo',
'addAction',
'ecommSend',
'ecommClear',
].indexOf(event.hitType) > -1;
Expand Down

0 comments on commit 4801d60

Please sign in to comment.