Skip to content
This repository was archived by the owner on May 6, 2024. It is now read-only.

Commit a0de92d

Browse files
feat: add plugin for flushing datalayer event (#78)
* feat: wIP: add plugin for flushing data layer properties * feat: wIP - exporting the flushing data layer properties plugin * test(plugins/getflushedpayload/*): add tests to getflushedpayload Add unit test for getFlushedPayload with small refactoring of function code * chore: move getFlushedPayload to top-level export * docs(readme): fix wrong example code import getFlushedPayload from top level Co-authored-by: wilfernandesjr <[email protected]>
1 parent 44eb9a2 commit a0de92d

File tree

5 files changed

+205
-1
lines changed

5 files changed

+205
-1
lines changed

README.md

+31
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ Collector is a library of React components and hooks that facilitates contextual
2323
- [useClickTrigger](#useclicktrigger)
2424
- [useSubmitTrigger](#usesubmittrigger)
2525
- [usePageViewTrigger](#usepageviewtrigger)
26+
- [Plugin](#plugin)
27+
- [getFlushedPayload](#getflushedpayload)
2628
- [Code of Conduct (CoC)](#code-of-conduct-coc)
2729
- [Maintainers](#maintainers)
2830
- [About SumUp](#about-sumup)
@@ -373,6 +375,35 @@ function PageActive({ location, children }: Props) {
373375
}
374376
```
375377

378+
## Plugin
379+
380+
Helpers for specific issue.
381+
382+
### getFlushedPayLoad
383+
384+
If you are using Google Tag Manager(GTM) as your dispatch consumer, there is a known behaviour that GTM persists variables until they got flushed. For a non-nested event, a fixed schema with default undefined value flushes unused variable thus they don't pollute states for the next event. For a designed nested variable, eg, `customParameters` in Collector, a nested flush helps to keep states clean. In this plugin, an aggregated custom parameters based on payload history will be set as undefined and flushed by GTM.
385+
386+
You can find an example code here.
387+
388+
```jsx
389+
import React from 'react';
390+
import { getFlushedPayload } from '@sumup/collector';
391+
392+
function App() {
393+
const handleDispatch = React.useCallback((event) => {
394+
// getFlushedPayload return a new event with flushed payload
395+
const flushedEvent = getFlushedPayload(window.dataLayer, event);
396+
window.dataLayer.push(flushedEvent)
397+
}, []);
398+
399+
return (
400+
<TrackingRoot name="app" onDispatch={handleDispatch}>
401+
...
402+
<TrackingRoot>
403+
);
404+
}
405+
```
406+
376407
## Code of Conduct (CoC)
377408

378409
We want to foster an inclusive and friendly community around our Open Source efforts. Like all SumUp Open Source projects, this project follows the Contributor Covenant Code of Conduct. Please, [read it and follow it](CODE_OF_CONDUCT.md).

src/index.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import useClickTrigger from './hooks/useClickTrigger';
2020
import useSubmitTrigger from './hooks/useSubmitTrigger';
2121
import usePageViewTrigger from './hooks/usePageViewTrigger';
2222
import usePageActiveTrigger from './hooks/usePageActiveTrigger';
23+
import getFlushedPayload from './plugins/getFlushedPayload';
2324
import * as Types from './types';
2425

2526
export {
@@ -29,7 +30,8 @@ export {
2930
useClickTrigger,
3031
useSubmitTrigger,
3132
usePageViewTrigger,
32-
usePageActiveTrigger
33+
usePageActiveTrigger,
34+
getFlushedPayload
3335
};
3436

3537
export type Dispatch = Types.Dispatch;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/**
2+
* Copyright 2020, SumUp Ltd.
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
import { Events, Payload } from '../../types';
17+
18+
import getFlushedPayload from './getFlushedPayload';
19+
20+
const baseEvent1: Payload = {
21+
app: 'app',
22+
view: 'view',
23+
elementTree: [],
24+
event: Events.click,
25+
timestamp: 1,
26+
customParameters: { A: 'A', B: true, C: [1, 2, 3] }
27+
};
28+
29+
const baseEvent2: Payload = {
30+
app: 'app',
31+
view: 'view',
32+
elementTree: [],
33+
event: Events.click,
34+
timestamp: 2,
35+
customParameters: { D: { A: 'D' } }
36+
};
37+
38+
describe('getFlushedPayload', () => {
39+
it('should set unused custom parameters to undefined based on payloads history', () => {
40+
const previousPayloads = [baseEvent1, baseEvent2];
41+
const payload = {
42+
...baseEvent1,
43+
customParameters: { B: false, C: 'string' }
44+
};
45+
const result = getFlushedPayload(previousPayloads, payload);
46+
expect(result).toStrictEqual({
47+
...baseEvent1,
48+
_clear: true,
49+
customParameters: { A: undefined, B: false, C: 'string', D: undefined }
50+
});
51+
});
52+
53+
it('should be an aggregated object when no new custom parameters present', () => {
54+
const previousPayloads = [baseEvent1, baseEvent2];
55+
const payload = {
56+
app: 'app',
57+
view: 'view',
58+
elementTree: [],
59+
event: Events.click,
60+
timestamp: 3
61+
};
62+
const result = getFlushedPayload(previousPayloads, payload);
63+
expect(result).toStrictEqual({
64+
...payload,
65+
_clear: true,
66+
customParameters: {
67+
A: undefined,
68+
B: undefined,
69+
C: undefined,
70+
D: undefined
71+
}
72+
});
73+
});
74+
75+
it('should be new payload when no previous custom paramaters is found', () => {
76+
const payload = {
77+
app: 'app',
78+
view: 'view',
79+
elementTree: [],
80+
event: Events.click,
81+
timestamp: 4
82+
};
83+
const previousPayloads = [payload];
84+
const result = getFlushedPayload(previousPayloads, baseEvent1);
85+
expect(result).toStrictEqual({
86+
...baseEvent1,
87+
_clear: true
88+
});
89+
});
90+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/**
2+
* Copyright 2021, SumUp Ltd.
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
import { Payload } from '../../types';
17+
18+
/**
19+
* Reference:
20+
* https://www.simoahava.com/gtm-tips/remember-to-flush-unused-data-layer-variables/
21+
*
22+
* GetFlushedPayload will aggregate custom parameters based on previous payload history
23+
* and set all previous value to undefined. By setting to undefined, GTM will flush the
24+
* variable and not polluting next states of datalayer.
25+
* @param previousPayloads payload history
26+
* @param payload new payload
27+
*/
28+
const getFlushedPayload = (
29+
previousPayloads: Payload[],
30+
payload: Payload
31+
): Payload => {
32+
const aggregatedParameters = previousPayloads.reduce((accu, current) => {
33+
const customParams = current.customParameters || {};
34+
return { ...accu, ...customParams };
35+
}, {});
36+
37+
// reset values to undefined in an object
38+
const resettedParameters = Object.keys(aggregatedParameters).reduce(
39+
(accu, key) => ({
40+
...accu,
41+
[key]: undefined
42+
}),
43+
{}
44+
);
45+
46+
const flushedPayload = {
47+
...payload,
48+
customParameters: {
49+
...resettedParameters,
50+
...payload.customParameters
51+
},
52+
/**
53+
* GTM property to prevent recursive merge of nested object/array
54+
* inside customParameters.
55+
* Ref: https://www.simoahava.com/analytics/two-simple-data-model-tricks/
56+
* */
57+
_clear: true
58+
};
59+
60+
return flushedPayload;
61+
};
62+
63+
export default getFlushedPayload;
+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/**
2+
* Copyright 2020, SumUp Ltd.
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
import getFlushedPayload from './getFlushedPayload';
17+
18+
export default getFlushedPayload;

0 commit comments

Comments
 (0)