@ordergroove/offers 2.27.15 → 2.27.17
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 +23 -0
- package/dist/bundle-report.html +20 -20
- package/dist/examples.js +9 -9
- package/dist/examples.js.map +2 -2
- package/dist/offers.js +24 -24
- package/dist/offers.js.map +2 -2
- package/examples/index.js +20 -1
- package/package.json +2 -2
- package/src/components/IncentiveText.js +0 -3
- package/src/components/Offer.js +5 -3
- package/src/components/SelectFrequency.js +11 -4
- package/src/components/When.js +2 -3
- package/src/components/__tests__/OG.fspec.js +178 -0
- package/src/core/actions.js +1 -1
- package/src/core/descriptors.js +1 -1
- package/src/core/reducer.js +4 -0
- package/src/core/utils.ts +1 -0
- package/src/init-func-tests.js +1 -0
- package/src/shopify/shopifyMiddleware.ts +23 -6
- package/src/shopify/shopifyReducer.js +6 -1
- package/src/components/__tests__/Price.fspec.js +0 -43
- package/src/components/__tests__/SelectFrequency.fspec.js +0 -44
package/examples/index.js
CHANGED
|
@@ -68,11 +68,13 @@ const exampleTemplates = [
|
|
|
68
68
|
'Offers example',
|
|
69
69
|
{
|
|
70
70
|
name: 'empty',
|
|
71
|
-
selector: 'og-offer:not([location])'
|
|
71
|
+
selector: 'og-offer:not([location])',
|
|
72
|
+
id: '1'
|
|
72
73
|
},
|
|
73
74
|
{
|
|
74
75
|
name: 'built-in',
|
|
75
76
|
selector: 'og-offer[location="initial"]',
|
|
77
|
+
id: '2',
|
|
76
78
|
config: {
|
|
77
79
|
settings: {
|
|
78
80
|
...settings,
|
|
@@ -84,6 +86,7 @@ const exampleTemplates = [
|
|
|
84
86
|
{
|
|
85
87
|
name: 'checkbox',
|
|
86
88
|
selector: 'og-offer[location="initial-checkbox"]',
|
|
89
|
+
id: '3',
|
|
87
90
|
config: {
|
|
88
91
|
settings: {
|
|
89
92
|
...settings,
|
|
@@ -97,6 +100,7 @@ const exampleTemplates = [
|
|
|
97
100
|
{
|
|
98
101
|
name: 'select',
|
|
99
102
|
selector: 'og-offer[location="initial-select"]',
|
|
103
|
+
id: '4',
|
|
100
104
|
config: {
|
|
101
105
|
settings: {
|
|
102
106
|
...settings,
|
|
@@ -108,6 +112,7 @@ const exampleTemplates = [
|
|
|
108
112
|
{
|
|
109
113
|
name: 'cart',
|
|
110
114
|
selector: 'og-offer[location="cart"]',
|
|
115
|
+
id: '5',
|
|
111
116
|
defaultPreviewMode: 'subscribed',
|
|
112
117
|
config: {
|
|
113
118
|
settings: {
|
|
@@ -120,6 +125,7 @@ const exampleTemplates = [
|
|
|
120
125
|
{
|
|
121
126
|
name: 'side-by-side',
|
|
122
127
|
selector: 'og-offer[location="side-by-side"]',
|
|
128
|
+
id: '6',
|
|
123
129
|
config: {
|
|
124
130
|
settings: {
|
|
125
131
|
...settings,
|
|
@@ -135,6 +141,7 @@ const exampleTemplates = [
|
|
|
135
141
|
{
|
|
136
142
|
name: 'read-only',
|
|
137
143
|
selector: 'og-offer[location="read-only"]',
|
|
144
|
+
id: '7',
|
|
138
145
|
defaultPreviewMode: 'subscribed',
|
|
139
146
|
config: {
|
|
140
147
|
settings: {
|
|
@@ -151,6 +158,7 @@ const exampleTemplates = [
|
|
|
151
158
|
{
|
|
152
159
|
name: 'buttons',
|
|
153
160
|
selector: 'og-offer[location="buttons"]',
|
|
161
|
+
id: '8',
|
|
154
162
|
config: {
|
|
155
163
|
settings: {
|
|
156
164
|
...settings,
|
|
@@ -167,6 +175,7 @@ const exampleTemplates = [
|
|
|
167
175
|
{
|
|
168
176
|
name: 'first-test',
|
|
169
177
|
selector: 'og-offer[location="first-test"]',
|
|
178
|
+
id: '9',
|
|
170
179
|
config: {
|
|
171
180
|
settings: {
|
|
172
181
|
...settings,
|
|
@@ -181,6 +190,7 @@ const exampleTemplates = [
|
|
|
181
190
|
{
|
|
182
191
|
name: 'second-test no tooltip',
|
|
183
192
|
selector: 'og-offer[location="second-test"]',
|
|
193
|
+
id: '10',
|
|
184
194
|
config: {
|
|
185
195
|
settings: {
|
|
186
196
|
...settings,
|
|
@@ -196,6 +206,7 @@ const exampleTemplates = [
|
|
|
196
206
|
{
|
|
197
207
|
name: 'Select mode',
|
|
198
208
|
selector: 'og-offer[location="select1"]',
|
|
209
|
+
id: '11',
|
|
199
210
|
markup: `
|
|
200
211
|
<og-optin-select>
|
|
201
212
|
<option value="optedOut">Buy one time</option>
|
|
@@ -206,6 +217,7 @@ const exampleTemplates = [
|
|
|
206
217
|
{
|
|
207
218
|
name: 'Custom markup',
|
|
208
219
|
selector: 'og-offer[location="custom1"]',
|
|
220
|
+
id: '12',
|
|
209
221
|
markup: `
|
|
210
222
|
<og-when test="regularEligible">
|
|
211
223
|
<p>
|
|
@@ -259,6 +271,7 @@ Add item to order on
|
|
|
259
271
|
name: 'Upsell modal - radio',
|
|
260
272
|
defaultPreviewMode: 'upsell',
|
|
261
273
|
selector: 'og-offer[location="upsell-radio"]',
|
|
274
|
+
id: '13',
|
|
262
275
|
markup: `
|
|
263
276
|
<og-when test="upsellEligible">
|
|
264
277
|
<og-upsell-button target="#upsell-radio">
|
|
@@ -283,6 +296,7 @@ Add item to order on
|
|
|
283
296
|
name: 'Upsell modal - select',
|
|
284
297
|
defaultPreviewMode: 'upsell',
|
|
285
298
|
selector: 'og-offer[location="upsell-select"]',
|
|
299
|
+
id: '14',
|
|
286
300
|
markup: `
|
|
287
301
|
<og-when test="upsellEligible">
|
|
288
302
|
<og-upsell-button target="#upsell-select">
|
|
@@ -297,6 +311,7 @@ Add item to order on
|
|
|
297
311
|
name: 'Upsell modal - toggle',
|
|
298
312
|
defaultPreviewMode: 'upsell',
|
|
299
313
|
selector: 'og-offer[location="upsell-toggle"]',
|
|
314
|
+
id: '15',
|
|
300
315
|
markup: `
|
|
301
316
|
<og-when test="upsellEligible">
|
|
302
317
|
<og-upsell-button target="#upsell-toggle">
|
|
@@ -315,6 +330,7 @@ Add item to order on
|
|
|
315
330
|
{
|
|
316
331
|
name: 'Product-specific default frequencies',
|
|
317
332
|
selector: 'og-offer[location="psdf"]',
|
|
333
|
+
id: '16',
|
|
318
334
|
markup: `
|
|
319
335
|
<og-when test="regularEligible">
|
|
320
336
|
<og-select-frequency default-text=" Most common!">
|
|
@@ -331,6 +347,7 @@ Add item to order on
|
|
|
331
347
|
{
|
|
332
348
|
name: 'og-optin-button',
|
|
333
349
|
selector: 'og-offer[location=og-optin-button]',
|
|
350
|
+
id: '17',
|
|
334
351
|
markup: `
|
|
335
352
|
<og-optin-button>
|
|
336
353
|
I want to subscribe to this product
|
|
@@ -340,6 +357,7 @@ Add item to order on
|
|
|
340
357
|
{
|
|
341
358
|
name: 'Frequency Status',
|
|
342
359
|
selector: 'og-offer[location="frequency"]',
|
|
360
|
+
id: '18',
|
|
343
361
|
markup: `
|
|
344
362
|
<og-frequency-status>
|
|
345
363
|
<span slot="not-subscribed">you are not subscribed </span>
|
|
@@ -348,6 +366,7 @@ Add item to order on
|
|
|
348
366
|
{
|
|
349
367
|
name: 'Tooltip',
|
|
350
368
|
selector: 'og-offer[location="tooltip-placement"]',
|
|
369
|
+
id: '19',
|
|
351
370
|
markup: [
|
|
352
371
|
['top-left', 'top', 'top-right'],
|
|
353
372
|
['left', '', 'right'],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ordergroove/offers",
|
|
3
|
-
"version": "2.27.
|
|
3
|
+
"version": "2.27.17",
|
|
4
4
|
"description": "offer state component",
|
|
5
5
|
"author": "Eugenio Lattanzio <eugenio63@gmail.com>",
|
|
6
6
|
"homepage": "https://github.com/ordergroove/plush-toys#readme",
|
|
@@ -45,5 +45,5 @@
|
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@ordergroove/offers-templates": "^0.4.15"
|
|
47
47
|
},
|
|
48
|
-
"gitHead": "
|
|
48
|
+
"gitHead": "982c709e906d0fe6f151bdd71d194363af85b60d"
|
|
49
49
|
}
|
|
@@ -107,9 +107,6 @@ export class IncentiveText extends withProduct(LitElement) {
|
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
renderFallback() {
|
|
110
|
-
const incentiveClass = this.from;
|
|
111
|
-
const incentiveValue = this.value;
|
|
112
|
-
const incentiveType = this.initial ? 'initial' : 'ongoing';
|
|
113
110
|
return html`
|
|
114
111
|
${discountBuilder({
|
|
115
112
|
field: 'sub_total',
|
package/src/components/Offer.js
CHANGED
|
@@ -54,7 +54,8 @@ export class Offer extends TemplateElement {
|
|
|
54
54
|
frequency: { type: String, reflect: true },
|
|
55
55
|
productFrequency: { type: String },
|
|
56
56
|
isCart: { type: Boolean, attribute: 'cart' },
|
|
57
|
-
optedin: { type: Object }
|
|
57
|
+
optedin: { type: Object },
|
|
58
|
+
variationId: { type: String }
|
|
58
59
|
};
|
|
59
60
|
}
|
|
60
61
|
|
|
@@ -236,7 +237,8 @@ export class Offer extends TemplateElement {
|
|
|
236
237
|
|
|
237
238
|
applyTemplate(template) {
|
|
238
239
|
super.applyTemplate(template);
|
|
239
|
-
const { locale } = template;
|
|
240
|
+
const { id: variationId, locale } = template;
|
|
241
|
+
this.variationId = variationId;
|
|
240
242
|
this.locale = locale;
|
|
241
243
|
const event = new CustomEvent('template-changed');
|
|
242
244
|
this.dispatchEvent(event);
|
|
@@ -248,7 +250,7 @@ export class Offer extends TemplateElement {
|
|
|
248
250
|
}
|
|
249
251
|
this.frequency = this.defaultFrequency;
|
|
250
252
|
|
|
251
|
-
if (changed.has('product') &&
|
|
253
|
+
if (changed.has('product') && !this.isPreview) {
|
|
252
254
|
onReady(() => this.fetchOffer(this.product.id, DEFAULT_OFFER_MODULE, this));
|
|
253
255
|
}
|
|
254
256
|
|
|
@@ -89,10 +89,17 @@ export class SelectFrequency extends withChildOptions(FrequencyStatus) {
|
|
|
89
89
|
const defaultFrequency = this.defaultFrequency;
|
|
90
90
|
|
|
91
91
|
if (this.frequencies?.length) {
|
|
92
|
-
options = this.frequencies.map((value, ix) =>
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
92
|
+
options = this.frequencies.map((value, ix) => {
|
|
93
|
+
let text;
|
|
94
|
+
if (this.config.frequenciesEveryPeriod && ix in this.config.frequenciesEveryPeriod) {
|
|
95
|
+
text = frequencyText(this.config.frequenciesEveryPeriod[ix], defaultFrequency);
|
|
96
|
+
} else if (this.frequenciesText && ix in this.frequenciesText) {
|
|
97
|
+
text = this.frequenciesText[ix];
|
|
98
|
+
} else {
|
|
99
|
+
text = frequencyText(value, this.defaultFrequency);
|
|
100
|
+
}
|
|
101
|
+
return { value, text };
|
|
102
|
+
});
|
|
96
103
|
} else {
|
|
97
104
|
({ options } = this.childOptions);
|
|
98
105
|
}
|
package/src/components/When.js
CHANGED
|
@@ -31,9 +31,8 @@ export class When extends withProduct(LitElement) {
|
|
|
31
31
|
shouldUpdate(changedProperties) {
|
|
32
32
|
return (
|
|
33
33
|
changedProperties.size &&
|
|
34
|
-
this.product &&
|
|
35
|
-
|
|
36
|
-
this.product.id in this.state.inStock
|
|
34
|
+
((this.product && this.product.id in this.state.autoshipEligible && this.product.id in this.state.inStock) ||
|
|
35
|
+
!this.product.id)
|
|
37
36
|
);
|
|
38
37
|
}
|
|
39
38
|
}
|
|
@@ -1,4 +1,44 @@
|
|
|
1
1
|
const og = window.og;
|
|
2
|
+
|
|
3
|
+
async function simulateChange(element, value) {
|
|
4
|
+
const evt = new Event('change', { bubbles: true });
|
|
5
|
+
element.value = value;
|
|
6
|
+
element.dispatchEvent(evt, { target: { value } });
|
|
7
|
+
await new Promise(r => setTimeout(r, 1));
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const expectToBeVisible = offer => {
|
|
11
|
+
const { x, y, width, height } = offer.getClientRects()[0];
|
|
12
|
+
expect(height).toBeGreaterThan(30);
|
|
13
|
+
};
|
|
14
|
+
const expectNotToBeVisible = offer => {
|
|
15
|
+
const { x, y, width, height } = offer.getClientRects()[0];
|
|
16
|
+
expect(height).toBeLessThan(30);
|
|
17
|
+
};
|
|
18
|
+
const waitUpdate = async offer => {
|
|
19
|
+
await offer.updateComplete;
|
|
20
|
+
await new Promise(r => setTimeout(r, 10));
|
|
21
|
+
};
|
|
22
|
+
const mockOfferResponse = (productId, inStock = true, autoship = true) => {
|
|
23
|
+
return Promise.resolve({
|
|
24
|
+
json() {
|
|
25
|
+
return Promise.resolve({
|
|
26
|
+
in_stock: { [productId]: inStock },
|
|
27
|
+
eligibility_groups: { [productId]: ['subscription', 'upsell'] },
|
|
28
|
+
result: 'success',
|
|
29
|
+
autoship: { [productId]: autoship },
|
|
30
|
+
autoship_by_default: { [productId]: false },
|
|
31
|
+
modifiers: {},
|
|
32
|
+
module_view: { regular: '096135e6650111e9a444bc764e106cf4' },
|
|
33
|
+
incentives_display: {},
|
|
34
|
+
incentives: {
|
|
35
|
+
[productId]: { initial: [], ongoing: [] }
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
|
|
2
42
|
describe('og.offers', function() {
|
|
3
43
|
it('should define og namespace', () => {
|
|
4
44
|
expect(og).toEqual(jasmine.any(Object));
|
|
@@ -33,3 +73,141 @@ describe('og.offers', function() {
|
|
|
33
73
|
expect(og.offers).toEqual(api);
|
|
34
74
|
});
|
|
35
75
|
});
|
|
76
|
+
|
|
77
|
+
describe('Offer', function() {
|
|
78
|
+
let productIdNotInStock;
|
|
79
|
+
let productIdInStock;
|
|
80
|
+
let fetch;
|
|
81
|
+
|
|
82
|
+
beforeEach(async () => {
|
|
83
|
+
fetch = spyOn(window, 'fetch');
|
|
84
|
+
productIdNotInStock = `oos${Math.random()
|
|
85
|
+
.toString(36)
|
|
86
|
+
.substring(2, 10)}`;
|
|
87
|
+
productIdInStock = `s-${Math.random()
|
|
88
|
+
.toString(36)
|
|
89
|
+
.substring(2, 10)}`;
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('should show offer when instock and eligible', async () => {
|
|
93
|
+
fetch.and.returnValue(mockOfferResponse(productIdInStock, true, true));
|
|
94
|
+
document.body.innerHTML = `
|
|
95
|
+
<og-offer product="${productIdInStock}">
|
|
96
|
+
<og-select-frequency default-text=" (recomended)">
|
|
97
|
+
<option value="optedOut">Buy one time</option>
|
|
98
|
+
<option value="2w" selected="selected">2 weeks</option>
|
|
99
|
+
<option value="1m">1 month </option>
|
|
100
|
+
</og-select-frequency>
|
|
101
|
+
</og-offer>
|
|
102
|
+
`;
|
|
103
|
+
const offer = document.querySelector('og-offer');
|
|
104
|
+
await waitUpdate(offer);
|
|
105
|
+
expect(fetch).toHaveBeenCalledTimes(1);
|
|
106
|
+
expectToBeVisible(document.querySelector('og-offer'));
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('should not show offer when not in stock', async () => {
|
|
110
|
+
fetch.and.returnValue(mockOfferResponse(productIdNotInStock, false, false));
|
|
111
|
+
document.body.innerHTML = `
|
|
112
|
+
<og-offer product="${productIdNotInStock}">
|
|
113
|
+
<og-when test="regularEligible">
|
|
114
|
+
<og-select-frequency default-text=" (recomended)">
|
|
115
|
+
<option value="optedOut">Buy one time</option>
|
|
116
|
+
<option value="2w" selected="selected">2 weeks</option>
|
|
117
|
+
<option value="1m">1 month </option>
|
|
118
|
+
</og-select-frequency>
|
|
119
|
+
</og-when>
|
|
120
|
+
</og-offer>
|
|
121
|
+
`;
|
|
122
|
+
const offer = document.querySelector('og-offer');
|
|
123
|
+
await waitUpdate(offer);
|
|
124
|
+
expect(fetch).toHaveBeenCalledTimes(1);
|
|
125
|
+
expectNotToBeVisible(document.querySelector('og-offer'));
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('should show hide offer based on variants in stock or oos', async () => {
|
|
129
|
+
fetch.and.returnValue(mockOfferResponse(productIdNotInStock, false, true));
|
|
130
|
+
document.body.innerHTML = `
|
|
131
|
+
<og-offer product="${productIdNotInStock}">
|
|
132
|
+
<og-when test="regularEligible">
|
|
133
|
+
<og-select-frequency default-text=" (recomended)">
|
|
134
|
+
<option value="optedOut">Buy one time</option>
|
|
135
|
+
<option value="2w" selected="selected">2 weeks</option>
|
|
136
|
+
<option value="1m">1 month </option>
|
|
137
|
+
</og-select-frequency>
|
|
138
|
+
</og-when>
|
|
139
|
+
</og-offer>
|
|
140
|
+
`;
|
|
141
|
+
const offer = document.querySelector('og-offer');
|
|
142
|
+
|
|
143
|
+
await waitUpdate(offer);
|
|
144
|
+
expect(fetch).toHaveBeenCalledTimes(1);
|
|
145
|
+
expectNotToBeVisible(offer);
|
|
146
|
+
|
|
147
|
+
fetch.and.returnValue(mockOfferResponse(productIdInStock, true, true));
|
|
148
|
+
offer.setAttribute('product', productIdInStock);
|
|
149
|
+
await waitUpdate(offer);
|
|
150
|
+
expect(fetch).toHaveBeenCalledTimes(2);
|
|
151
|
+
expectToBeVisible(offer);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('should hide when no product id', async () => {
|
|
155
|
+
fetch.and.returnValue(mockOfferResponse(productIdInStock, true, true));
|
|
156
|
+
document.body.innerHTML = `
|
|
157
|
+
<og-offer product="${productIdInStock}">
|
|
158
|
+
<og-when test="regularEligible">
|
|
159
|
+
<og-select-frequency default-text=" (recomended)">
|
|
160
|
+
<option value="optedOut">Buy one time</option>
|
|
161
|
+
<option value="2w" selected="selected">2 weeks</option>
|
|
162
|
+
<option value="1m">1 month </option>
|
|
163
|
+
</og-select-frequency>
|
|
164
|
+
</og-when>
|
|
165
|
+
</og-offer>
|
|
166
|
+
`;
|
|
167
|
+
const offer = document.querySelector('og-offer');
|
|
168
|
+
await waitUpdate(offer);
|
|
169
|
+
expect(fetch).toHaveBeenCalledTimes(1);
|
|
170
|
+
expectToBeVisible(offer);
|
|
171
|
+
|
|
172
|
+
offer.removeAttribute('product');
|
|
173
|
+
await waitUpdate(offer);
|
|
174
|
+
expectNotToBeVisible(offer);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
describe('Select Frequency', function() {
|
|
178
|
+
let element;
|
|
179
|
+
beforeEach(async () => {
|
|
180
|
+
fetch.and.returnValue(mockOfferResponse(productIdInStock));
|
|
181
|
+
document.body.innerHTML = `
|
|
182
|
+
<og-offer product="${productIdInStock}">
|
|
183
|
+
<og-select-frequency default-text=" (recomended)">
|
|
184
|
+
<option value="optedOut">Buy one time</option>
|
|
185
|
+
<option value="2_2" selected="selected">2 weeks</option>
|
|
186
|
+
<option value="1_3">1 month </option>
|
|
187
|
+
</og-select-frequency>
|
|
188
|
+
</og-offer>
|
|
189
|
+
`;
|
|
190
|
+
element = document.querySelector('og-select-frequency');
|
|
191
|
+
await element.updateComplete;
|
|
192
|
+
await new Promise(r => setTimeout(r, 10));
|
|
193
|
+
expect(fetch).toHaveBeenCalledTimes(1);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it('it should have default frequency as value', async () => {
|
|
197
|
+
const htmlSelectElement = element.shadowRoot.querySelector('og-select').shadowRoot.querySelector('select');
|
|
198
|
+
expect(htmlSelectElement.value).toEqual('2_2');
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it('should append recomended text to default frequency', async () => {
|
|
202
|
+
const htmlSelectElement = element.shadowRoot.querySelector('og-select').shadowRoot.querySelector('select');
|
|
203
|
+
expect(htmlSelectElement.innerText).toEqual('Buy one time\n2 weeks (recomended)\n1 month');
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it('should not append recomended text to clicked frequency', async () => {
|
|
207
|
+
const htmlSelectElement = element.shadowRoot.querySelector('og-select').shadowRoot.querySelector('select');
|
|
208
|
+
await simulateChange(htmlSelectElement, '1_3');
|
|
209
|
+
expect(htmlSelectElement.value).toEqual('1_3');
|
|
210
|
+
expect(htmlSelectElement.innerText).toEqual('Buy one time\n2 weeks (recomended)\n1 month');
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
});
|
package/src/core/actions.js
CHANGED
|
@@ -197,7 +197,7 @@ export const fetchOffer = (product, module = constants.DEFAULT_OFFER_MODULE, off
|
|
|
197
197
|
dispatch(requestAction);
|
|
198
198
|
|
|
199
199
|
const productId = safeProductId(product);
|
|
200
|
-
|
|
200
|
+
if (!productId) return null;
|
|
201
201
|
return api
|
|
202
202
|
.fetchOffer(apiUrl, merchantId, sessionId, productId, module)
|
|
203
203
|
.then(
|
package/src/core/descriptors.js
CHANGED
|
@@ -31,7 +31,7 @@ export const upcomingOrderContainsProduct = (state, ownProps) =>
|
|
|
31
31
|
*/
|
|
32
32
|
export const upsellEligible = (state, ownProps) =>
|
|
33
33
|
// don't show IU in cart offers
|
|
34
|
-
!ownProps.offer
|
|
34
|
+
!ownProps.offer?.isCart &&
|
|
35
35
|
state.offerId &&
|
|
36
36
|
state.offerId !== '0' &&
|
|
37
37
|
state.auth &&
|
package/src/core/reducer.js
CHANGED
|
@@ -111,6 +111,9 @@ export const autoshipEligible = (state = {}, action) => {
|
|
|
111
111
|
|
|
112
112
|
export const inStock = (state = {}, action) => {
|
|
113
113
|
switch (action.type) {
|
|
114
|
+
// force offer to refresh when requesting a new one
|
|
115
|
+
case constants.REQUEST_OFFER:
|
|
116
|
+
return { ...state };
|
|
114
117
|
case constants.RECEIVE_OFFER:
|
|
115
118
|
return {
|
|
116
119
|
...state,
|
|
@@ -383,6 +386,7 @@ export const config = (
|
|
|
383
386
|
defaultFrequency: action.payload.defaultFrequency
|
|
384
387
|
? stringifyFrequency(action.payload.defaultFrequency)
|
|
385
388
|
: state.defaultFrequency,
|
|
389
|
+
frequenciesEveryPeriod: [],
|
|
386
390
|
frequencies: action.payload.frequencies ? action.payload.frequencies.map(stringifyFrequency) : state.frequencies
|
|
387
391
|
};
|
|
388
392
|
default:
|
package/src/core/utils.ts
CHANGED
|
@@ -36,6 +36,7 @@ export function resolveEnvAndMerchant() {
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
export const safeProductId = product => {
|
|
39
|
+
if (!product ) return '';
|
|
39
40
|
let productId = `${product.id || product}`;
|
|
40
41
|
if (platform?.shopify_selling_plans) {
|
|
41
42
|
// we can't avoid make offer request since we need to know the upsell group and autoship by default
|
package/src/init-func-tests.js
CHANGED
|
@@ -227,13 +227,27 @@ export async function synchronizeCartOptin(action: any, store: any) {
|
|
|
227
227
|
/**
|
|
228
228
|
* Returns a tracking event adhering to the below format:
|
|
229
229
|
*
|
|
230
|
-
* og__<ts in seconds>: "<product_id>,<action>,<location>,<selling_plan optional>"
|
|
230
|
+
* og__<ts in seconds>: "<product_id>,<action>,<location>,<selling_plan optional>,<variation_id optional>"
|
|
231
231
|
*
|
|
232
232
|
* Examples:
|
|
233
|
-
*
|
|
234
|
-
*
|
|
235
|
-
*
|
|
236
|
-
*
|
|
233
|
+
*
|
|
234
|
+
* - optin_product with selling plan ID 123 and variation ID 456:
|
|
235
|
+
* og__165653130: "optin_product,pdp,123,456"
|
|
236
|
+
*
|
|
237
|
+
* - optin_product with no selling plan and variation ID 456:
|
|
238
|
+
* og__165653137: "optin_product,pdp,,456"
|
|
239
|
+
*
|
|
240
|
+
* - optin_product with selling plan ID 123 and no variation ID:
|
|
241
|
+
* og__165653139: "optin_product,pdp,123,"
|
|
242
|
+
*
|
|
243
|
+
* - optin_product with no selling plan and no variation ID:
|
|
244
|
+
* og__165653141: "optin_product,pdp,,"
|
|
245
|
+
*
|
|
246
|
+
* - optout_product with variation id 456:
|
|
247
|
+
* og__165653135: "optout_product,pdp,,456"
|
|
248
|
+
*
|
|
249
|
+
* - product_change_frequency with selling plan ID 123 and variation ID 456:
|
|
250
|
+
* og__165653131: "product_change_frequency,pdp,123,456"
|
|
237
251
|
*
|
|
238
252
|
* @param action a Redux action
|
|
239
253
|
* @return {Array} an array with positional values key, value
|
|
@@ -243,16 +257,19 @@ export function getTrackingEvent(action): Array<string> {
|
|
|
243
257
|
if (!product_id) return [];
|
|
244
258
|
const key = `og__${Math.ceil(new Date().getTime() / 1000)}`;
|
|
245
259
|
const location = action.payload.offer?.location || '';
|
|
260
|
+
const variation = action.payload.offer?.variationId || '';
|
|
246
261
|
const value = [product_id, action.type.toLowerCase(), location];
|
|
247
262
|
|
|
248
263
|
switch (action.type) {
|
|
249
264
|
case REQUEST_OFFER:
|
|
250
265
|
case OPTOUT_PRODUCT:
|
|
251
|
-
value.push('');
|
|
266
|
+
value.push(''); // No selling plan should be associated with these actions
|
|
267
|
+
value.push(variation);
|
|
252
268
|
break;
|
|
253
269
|
case OPTIN_PRODUCT:
|
|
254
270
|
case PRODUCT_CHANGE_FREQUENCY:
|
|
255
271
|
value.push(action.payload.frequency);
|
|
272
|
+
value.push(variation);
|
|
256
273
|
break;
|
|
257
274
|
default:
|
|
258
275
|
return []; // we dont track anything else
|
|
@@ -111,7 +111,8 @@ export function textToFreq(text) {
|
|
|
111
111
|
export const config = (
|
|
112
112
|
state = {
|
|
113
113
|
frequencies: [],
|
|
114
|
-
offerType: 'radio'
|
|
114
|
+
offerType: 'radio',
|
|
115
|
+
frequenciesEveryPeriod: []
|
|
115
116
|
},
|
|
116
117
|
action
|
|
117
118
|
) => {
|
|
@@ -168,6 +169,10 @@ export const inStock = (state = {}, action) => {
|
|
|
168
169
|
|
|
169
170
|
return [product, ...product?.variants]?.reduce(productOrVariantInStockReducer, state) || state;
|
|
170
171
|
}
|
|
172
|
+
// force offer to refresh when requesting a new one
|
|
173
|
+
if (constants.REQUEST_OFFER === action.type && action.payload.product === null) {
|
|
174
|
+
return { ...state };
|
|
175
|
+
}
|
|
171
176
|
|
|
172
177
|
return state;
|
|
173
178
|
};
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
const og = window.og;
|
|
2
|
-
|
|
3
|
-
async function simulateChange(element, value) {
|
|
4
|
-
const evt = new Event('change', { bubbles: true });
|
|
5
|
-
element.value = value;
|
|
6
|
-
element.dispatchEvent(evt, { target: { value } });
|
|
7
|
-
await new Promise(r => setTimeout(r, 1));
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
describe('Select Frequency', function() {
|
|
11
|
-
let element;
|
|
12
|
-
beforeEach(async () => {
|
|
13
|
-
og.offers.clear();
|
|
14
|
-
document.body.innerHTML = `
|
|
15
|
-
<og-offer product="123" preview-standard-offer>
|
|
16
|
-
<og-select-frequency default-text=" (recomended)">
|
|
17
|
-
<option value="optedOut">Buy one time</option>
|
|
18
|
-
<option value="2w" selected="selected">2 weeks</option>
|
|
19
|
-
<option value="1m">1 month </option>
|
|
20
|
-
</og-select-frequency>
|
|
21
|
-
</og-offer>
|
|
22
|
-
`;
|
|
23
|
-
element = document.querySelector('og-select-frequency');
|
|
24
|
-
await element.updateComplete;
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it('it should have default frequency as value', async () => {
|
|
28
|
-
const htmlSelectElement = element.shadowRoot.querySelector('og-select').shadowRoot.querySelector('select');
|
|
29
|
-
expect(htmlSelectElement.value).toEqual('2_2');
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it('should append recomended text to default frequency', async () => {
|
|
33
|
-
const htmlSelectElement = element.shadowRoot.querySelector('og-select').shadowRoot.querySelector('select');
|
|
34
|
-
expect(htmlSelectElement.innerText).toEqual('Buy one time\n2 weeks (recomended)\n1 month');
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it('should not append recomended text to clicked frequency', async () => {
|
|
38
|
-
const htmlSelectElement = element.shadowRoot.querySelector('og-select').shadowRoot.querySelector('select');
|
|
39
|
-
await simulateChange(htmlSelectElement, '1_3');
|
|
40
|
-
expect(htmlSelectElement.value).toEqual('1_3');
|
|
41
|
-
expect(htmlSelectElement.innerText).toEqual('Buy one time\n2 weeks (recomended)\n1 month');
|
|
42
|
-
});
|
|
43
|
-
});
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
const og = window.og;
|
|
2
|
-
|
|
3
|
-
async function simulateChange(element, value) {
|
|
4
|
-
const evt = new Event('change', { bubbles: true });
|
|
5
|
-
element.value = value;
|
|
6
|
-
element.dispatchEvent(evt, { target: { value } });
|
|
7
|
-
await new Promise(r => setTimeout(r, 1));
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
describe('Select Frequency', function() {
|
|
11
|
-
let element;
|
|
12
|
-
beforeEach(async () => {
|
|
13
|
-
og.offers.clear();
|
|
14
|
-
document.body.innerHTML = `
|
|
15
|
-
<og-offer product="123" preview-standard-offer>
|
|
16
|
-
<og-select-frequency default-text=" (recomended)">
|
|
17
|
-
<option value="optedOut">Buy one time</option>
|
|
18
|
-
<option value="2w" selected="selected">2 weeks</option>
|
|
19
|
-
<option value="1m">1 month </option>
|
|
20
|
-
</og-select-frequency>
|
|
21
|
-
</og-offer>
|
|
22
|
-
`;
|
|
23
|
-
element = document.querySelector('og-select-frequency');
|
|
24
|
-
await element.updateComplete;
|
|
25
|
-
await new Promise(r => setTimeout(r, 1000));
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
it('it should have default frequency as value', async () => {
|
|
29
|
-
const htmlSelectElement = element.shadowRoot.querySelector('og-select').shadowRoot.querySelector('select');
|
|
30
|
-
expect(htmlSelectElement.value).toEqual('2_2');
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it('should append recomended text to default frequency', async () => {
|
|
34
|
-
const htmlSelectElement = element.shadowRoot.querySelector('og-select').shadowRoot.querySelector('select');
|
|
35
|
-
expect(htmlSelectElement.innerText).toEqual('Buy one time\n2 weeks (recomended)\n1 month');
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it('should not append recomended text to clicked frequency', async () => {
|
|
39
|
-
const htmlSelectElement = element.shadowRoot.querySelector('og-select').shadowRoot.querySelector('select');
|
|
40
|
-
await simulateChange(htmlSelectElement, '1_3');
|
|
41
|
-
expect(htmlSelectElement.value).toEqual('1_3');
|
|
42
|
-
expect(htmlSelectElement.innerText).toEqual('Buy one time\n2 weeks (recomended)\n1 month');
|
|
43
|
-
});
|
|
44
|
-
});
|