@ordergroove/offers 2.28.7-alpha-PR-690-14.11 → 2.28.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/README.md +122 -117
- package/dist/bundle-report.html +10 -10
- package/dist/examples.js +10 -22
- package/dist/examples.js.map +1 -1
- package/dist/offers.js +30 -35
- package/dist/offers.js.map +3 -3
- package/examples/index.html +0 -2
- package/package.json +2 -2
- package/src/components/OptinStatus.js +2 -7
- package/src/components/OptinToggle.js +2 -2
- package/src/shopify/__tests__/shopifyMiddleware.spec.js +42 -1
- package/src/shopify/__tests__/shopifyTrackingMiddleware.spec.js +125 -0
- package/src/shopify/shopifyMiddleware.ts +9 -3
- package/src/shopify/shopifyTrackingMiddleware.ts +26 -2
package/examples/index.html
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ordergroove/offers",
|
|
3
|
-
"version": "2.28.7
|
|
3
|
+
"version": "2.28.7",
|
|
4
4
|
"description": "offer state component",
|
|
5
5
|
"author": "Eugenio Lattanzio <eugenio63@gmail.com>",
|
|
6
6
|
"homepage": "https://github.com/ordergroove/plush-toys#readme",
|
|
@@ -47,5 +47,5 @@
|
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@ordergroove/offers-templates": "^0.4.19"
|
|
49
49
|
},
|
|
50
|
-
"gitHead": "
|
|
50
|
+
"gitHead": "20dc90b04c6796d7374cde6375228faf3a8108e1"
|
|
51
51
|
}
|
|
@@ -53,12 +53,6 @@ export class OptinStatus extends withProduct(TemplateElement) {
|
|
|
53
53
|
.checkbox {
|
|
54
54
|
border-radius: 3px;
|
|
55
55
|
}
|
|
56
|
-
|
|
57
|
-
.radio,
|
|
58
|
-
.checkbox {
|
|
59
|
-
border-color: var(--og-checkbox-border-color, black);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
56
|
.checkbox.active::after,
|
|
63
57
|
.radio.active::after {
|
|
64
58
|
position: absolute;
|
|
@@ -67,12 +61,12 @@ export class OptinStatus extends withProduct(TemplateElement) {
|
|
|
67
61
|
width: 100%;
|
|
68
62
|
height: 100%;
|
|
69
63
|
box-sizing: border-box;
|
|
70
|
-
background: var(--og-checkbox-border-color, black);
|
|
71
64
|
}
|
|
72
65
|
|
|
73
66
|
.radio.active::after {
|
|
74
67
|
content: ' ';
|
|
75
68
|
border-radius: 100%;
|
|
69
|
+
background: var(--og-primary-color, var(--og-radio-background-color, black));
|
|
76
70
|
border: 2px solid #fff;
|
|
77
71
|
}
|
|
78
72
|
|
|
@@ -81,6 +75,7 @@ export class OptinStatus extends withProduct(TemplateElement) {
|
|
|
81
75
|
border-radius: 0;
|
|
82
76
|
background: #fff;
|
|
83
77
|
content: '\\2714';
|
|
78
|
+
color: var(--og-primary-color, var(--og-checkbox-color, black));
|
|
84
79
|
line-height: 1;
|
|
85
80
|
text-align: center;
|
|
86
81
|
overflow: visible;
|
|
@@ -25,7 +25,7 @@ export class OptinToggle extends OptinStatus {
|
|
|
25
25
|
height: var(--og-radio-height, 1.4em);
|
|
26
26
|
margin: var(--og-radio-margin, 0);
|
|
27
27
|
padding: 0;
|
|
28
|
-
border: 1px solid var(--og-
|
|
28
|
+
border: 1px solid var(--og-primary-color, black);
|
|
29
29
|
background: #fff;
|
|
30
30
|
vertical-align: middle;
|
|
31
31
|
color: var(--og-primary-color, black);
|
|
@@ -36,7 +36,7 @@ export class OptinToggle extends OptinStatus {
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
.btn.active {
|
|
39
|
-
background:
|
|
39
|
+
background: #090909;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
.btn.active:after {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fetchMock from 'fetch-mock';
|
|
2
2
|
import { CART_UPDATED_EVENT, OPTIN_PRODUCT, SETUP_CART } from '../../core/constants';
|
|
3
|
-
import {
|
|
3
|
+
import { synchronizeCartOptin, getTrackingEvent } from '../shopifyMiddleware';
|
|
4
|
+
import { getOrCreateHidden } from '../../core/utils';
|
|
4
5
|
|
|
5
6
|
function makeForm(addInput = true) {
|
|
6
7
|
const element = document.createElement('form');
|
|
@@ -43,6 +44,46 @@ describe('getOrCreateHidden', () => {
|
|
|
43
44
|
});
|
|
44
45
|
});
|
|
45
46
|
|
|
47
|
+
describe('getTrackingEvent', () => {
|
|
48
|
+
const keyRegex = new RegExp('og__[0-9]+');
|
|
49
|
+
it('make tracking event for optout', () => {
|
|
50
|
+
const actionIn = {
|
|
51
|
+
type: 'OPTOUT_PRODUCT',
|
|
52
|
+
payload: {
|
|
53
|
+
product: {
|
|
54
|
+
id: 'testProduct'
|
|
55
|
+
},
|
|
56
|
+
offer: {
|
|
57
|
+
location: 'testLocation',
|
|
58
|
+
variationId: 'testVariation'
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
const trackingEvent = getTrackingEvent(actionIn);
|
|
63
|
+
expect(keyRegex.test(trackingEvent[0])).toEqual(true);
|
|
64
|
+
expect(trackingEvent[1]).toEqual('testProduct,optout_product,testLocation,,testVariation');
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('make tracking event for optin', () => {
|
|
68
|
+
const actionIn = {
|
|
69
|
+
type: 'OPTIN_PRODUCT',
|
|
70
|
+
payload: {
|
|
71
|
+
product: {
|
|
72
|
+
id: 'testProduct'
|
|
73
|
+
},
|
|
74
|
+
offer: {
|
|
75
|
+
location: 'testLocation',
|
|
76
|
+
variationId: 'testVariation'
|
|
77
|
+
},
|
|
78
|
+
frequency: 'testFrequency'
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
const trackingEvent = getTrackingEvent(actionIn);
|
|
82
|
+
expect(keyRegex.test(trackingEvent[0])).toEqual(true);
|
|
83
|
+
expect(trackingEvent[1]).toEqual('testProduct,optin_product,testLocation,testFrequency,testVariation');
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
46
87
|
describe('synchronizeCartOptin', () => {
|
|
47
88
|
let store, offer, frequency, product;
|
|
48
89
|
beforeEach(() => {
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { updateTrackingInputs, addDefaultToSubTracking } from '../shopifyTrackingMiddleware';
|
|
2
|
+
|
|
3
|
+
function makeForm() {
|
|
4
|
+
const element = document.createElement('form');
|
|
5
|
+
element.action = '/cart/add';
|
|
6
|
+
element.innerHTML = `
|
|
7
|
+
<input value="123456" name="id" type="hidden">
|
|
8
|
+
`;
|
|
9
|
+
document.body.appendChild(element);
|
|
10
|
+
return element;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const testProduct = '123456';
|
|
14
|
+
|
|
15
|
+
describe('updateTrackingInputs', () => {
|
|
16
|
+
let formElement;
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
formElement = makeForm();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
afterEach(() => {
|
|
22
|
+
formElement.remove();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const testName = 'og__123456789';
|
|
26
|
+
const testValue = '123456,optin_product,pdp,yay_selling_plan,yay_variant';
|
|
27
|
+
|
|
28
|
+
it('should add tracking input with all values passed in', () => {
|
|
29
|
+
expect(formElement.querySelector(`[name="attributes[${testName}]"]`)).toBeFalsy();
|
|
30
|
+
updateTrackingInputs(testProduct, testName, testValue);
|
|
31
|
+
const newInput = formElement.querySelector(`[name="attributes[${testName}]"]`);
|
|
32
|
+
expect(newInput).toBeTruthy();
|
|
33
|
+
expect(newInput.getAttribute('value')).toEqual(testValue);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('should not add input if one already exists with the same name', () => {
|
|
37
|
+
updateTrackingInputs(testProduct, testName, testValue);
|
|
38
|
+
const newInput = formElement.querySelectorAll(`[name="attributes[${testName}]"]`);
|
|
39
|
+
expect(newInput.length).toBe(1);
|
|
40
|
+
updateTrackingInputs(testProduct, testName, testValue);
|
|
41
|
+
expect(newInput.length).toBe(1);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('should add inputs to multiple forms if existing', () => {
|
|
45
|
+
const formElement1 = makeForm();
|
|
46
|
+
const formElement2 = makeForm();
|
|
47
|
+
updateTrackingInputs(testProduct, testName, testValue);
|
|
48
|
+
const newInput1 = formElement1.querySelector(`[name="attributes[${testName}]"]`);
|
|
49
|
+
const newInput2 = formElement2.querySelector(`[name="attributes[${testName}]"]`);
|
|
50
|
+
expect(newInput1).toBeTruthy();
|
|
51
|
+
expect(newInput2).toBeTruthy();
|
|
52
|
+
expect(newInput1).toEqual(newInput2);
|
|
53
|
+
formElement1.remove();
|
|
54
|
+
formElement2.remove();
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe('addDefaultToSubTracking', () => {
|
|
59
|
+
const keyRegex = new RegExp('attributes[og__[0-9]+]');
|
|
60
|
+
|
|
61
|
+
const defaultValue = `${testProduct},optin_product,testLocation,4_2,testVariation`;
|
|
62
|
+
const inputSelector = `[value="${defaultValue}"]`;
|
|
63
|
+
|
|
64
|
+
const storeOg = {
|
|
65
|
+
getState: () => {
|
|
66
|
+
return { optedin: [{ id: testProduct, frequency: '4_2' }] };
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
let formElement;
|
|
71
|
+
beforeEach(() => {
|
|
72
|
+
formElement = makeForm();
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
afterEach(() => {
|
|
76
|
+
formElement.remove();
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('make tracking event for default to sub optin for RECEIVE_OFFER', () => {
|
|
80
|
+
const actionReceiveOffer = {
|
|
81
|
+
type: 'RECEIVE_OFFER',
|
|
82
|
+
payload: {
|
|
83
|
+
product: {
|
|
84
|
+
id: testProduct
|
|
85
|
+
},
|
|
86
|
+
offer: {
|
|
87
|
+
location: 'testLocation',
|
|
88
|
+
variationId: 'testVariation',
|
|
89
|
+
autoshipByDefault: true,
|
|
90
|
+
product: {
|
|
91
|
+
id: testProduct
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
expect(document.querySelector(inputSelector)).toBeFalsy();
|
|
97
|
+
addDefaultToSubTracking(actionReceiveOffer, storeOg);
|
|
98
|
+
const defaultInput = document.querySelector(inputSelector);
|
|
99
|
+
expect(defaultInput).toBeTruthy();
|
|
100
|
+
expect(keyRegex.test(defaultInput.name)).toEqual(true);
|
|
101
|
+
expect(defaultInput.value).toEqual(defaultValue);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('should not make input if product is not default-to-sub', () => {
|
|
105
|
+
const actionReceiveOffer = {
|
|
106
|
+
type: 'RECEIVE_OFFER',
|
|
107
|
+
payload: {
|
|
108
|
+
product: {
|
|
109
|
+
id: testProduct
|
|
110
|
+
},
|
|
111
|
+
offer: {
|
|
112
|
+
location: 'testLocation',
|
|
113
|
+
variationId: 'testVariation',
|
|
114
|
+
product: {
|
|
115
|
+
id: testProduct
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
expect(document.querySelector(inputSelector)).toBeFalsy();
|
|
121
|
+
addDefaultToSubTracking(actionReceiveOffer, storeOg);
|
|
122
|
+
const defaultInput = document.querySelector(inputSelector);
|
|
123
|
+
expect(defaultInput).toBeFalsy();
|
|
124
|
+
});
|
|
125
|
+
});
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
|
|
15
15
|
import { makeSubscribedSelector } from '../core/selectors';
|
|
16
16
|
import { getOrCreateHidden, safeProductId } from '../core/utils';
|
|
17
|
+
import { getTrackingKey } from './shopifyTrackingMiddleware';
|
|
17
18
|
|
|
18
19
|
const SHOPIFY_ROOT = window.Shopify?.routes?.root || '/';
|
|
19
20
|
const CART_PAGE_URL = '/cart';
|
|
@@ -262,7 +263,7 @@ export async function synchronizeCartOptin(action: any, store: any) {
|
|
|
262
263
|
export function getTrackingEvent(action): Array<string> {
|
|
263
264
|
const product_id = action.payload.product.id;
|
|
264
265
|
if (!product_id) return [];
|
|
265
|
-
const key =
|
|
266
|
+
const key = getTrackingKey();
|
|
266
267
|
const location = action.payload.offer?.location || '';
|
|
267
268
|
const variation = action.payload.offer?.variationId || '';
|
|
268
269
|
const value = [product_id, action.type.toLowerCase(), location];
|
|
@@ -285,6 +286,12 @@ export function getTrackingEvent(action): Array<string> {
|
|
|
285
286
|
return [key, value.join(',')];
|
|
286
287
|
}
|
|
287
288
|
|
|
289
|
+
export function getSubscribedFrequency(productId, store) {
|
|
290
|
+
const subscribedSelector = makeSubscribedSelector({ id: productId });
|
|
291
|
+
const sellingPlanId = subscribedSelector(store.getState())?.frequency;
|
|
292
|
+
return sellingPlanId;
|
|
293
|
+
}
|
|
294
|
+
|
|
288
295
|
/**
|
|
289
296
|
* // update <input type="hidden" name="selling_plan"/> if available
|
|
290
297
|
*
|
|
@@ -296,8 +303,7 @@ function synchronizeSellingPlan(store: any, offerElement?: HTMLElement) {
|
|
|
296
303
|
[...document.querySelectorAll('form[action$="/cart/add"] [name=id]')].forEach((productIdInput: HTMLInputElement) => {
|
|
297
304
|
const productId = productIdInput.value;
|
|
298
305
|
|
|
299
|
-
const
|
|
300
|
-
const sellingPlanId = subscribedSelector(store.getState())?.frequency;
|
|
306
|
+
const sellingPlanId = getSubscribedFrequency(productId, store);
|
|
301
307
|
|
|
302
308
|
getOrCreateHidden(productIdInput.form, 'selling_plan', sellingPlanId);
|
|
303
309
|
if (offerElement) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { OPTIN_PRODUCT, OPTOUT_PRODUCT, PRODUCT_CHANGE_FREQUENCY } from '../core/constants';
|
|
2
|
-
import { getTrackingEvent } from './shopifyMiddleware';
|
|
1
|
+
import { OPTIN_PRODUCT, OPTOUT_PRODUCT, PRODUCT_CHANGE_FREQUENCY, RECEIVE_OFFER, SETUP_PRODUCT } from '../core/constants';
|
|
2
|
+
import { getTrackingEvent, getSubscribedFrequency } from './shopifyMiddleware';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Creates or updates a hidden input used for tracking on non-cart pages
|
|
@@ -31,6 +31,27 @@ export function updateTrackingInputs(product_id: string, name: string, value: st
|
|
|
31
31
|
});
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
export function getTrackingKey() {
|
|
35
|
+
return `og__${Math.ceil(new Date().getTime() / 1000)}`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function addDefaultToSubTracking(action, store) {
|
|
39
|
+
// check if default to sub
|
|
40
|
+
const isDefaultToSub = action.payload.offer?.autoshipByDefault;
|
|
41
|
+
if (!isDefaultToSub) return;
|
|
42
|
+
|
|
43
|
+
// if default to sub, get tracking info
|
|
44
|
+
const productId = action.payload.offer?.product.id;
|
|
45
|
+
const key = getTrackingKey();
|
|
46
|
+
const location = action.payload.offer?.location || '';
|
|
47
|
+
const variation = action.payload.offer?.variationId || '';
|
|
48
|
+
const frequency = getSubscribedFrequency(productId, store);
|
|
49
|
+
const value = [productId, OPTIN_PRODUCT.toLowerCase(), location, frequency, variation];
|
|
50
|
+
const inputValue = value.join(',');
|
|
51
|
+
updateTrackingInputs(productId, key, inputValue);
|
|
52
|
+
|
|
53
|
+
}
|
|
54
|
+
|
|
34
55
|
export default function shopifyTrackingMiddleware(store) {
|
|
35
56
|
return next => action => {
|
|
36
57
|
next(action);
|
|
@@ -46,6 +67,9 @@ export default function shopifyTrackingMiddleware(store) {
|
|
|
46
67
|
updateTrackingInputs(offerElement.product.id, trackingEvent[0], trackingEvent[1]);
|
|
47
68
|
}
|
|
48
69
|
break;
|
|
70
|
+
case RECEIVE_OFFER:
|
|
71
|
+
addDefaultToSubTracking(action, store);
|
|
72
|
+
break;
|
|
49
73
|
default:
|
|
50
74
|
}
|
|
51
75
|
};
|