@ordergroove/offers 2.24.3 → 2.25.0
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 +19 -0
- package/dist/bundle-report.html +99 -98
- package/dist/examples.js +9 -9
- package/dist/examples.js.map +2 -2
- package/dist/offers.js +159 -144
- package/dist/offers.js.map +3 -3
- package/examples/index.js +10 -1
- package/package.json +4 -4
- package/src/__tests__/offers.spec.js +100 -5
- package/src/components/Offer.js +10 -1
- package/src/components/Price.js +54 -0
- package/src/components/__tests__/OG.fspec.js +2 -2
- package/src/components/__tests__/Price.fspec.js +43 -0
- package/src/core/actions.js +7 -4
- package/src/core/constants.js +1 -0
- package/src/core/reducer.js +10 -0
- package/src/index.js +187 -150
- package/src/init-func-tests.js +1 -1
- package/src/init-test.js +2 -2
package/examples/index.js
CHANGED
|
@@ -5,6 +5,16 @@ import { getMarkup, getStyles } from '@ordergroove/offers-templates';
|
|
|
5
5
|
|
|
6
6
|
// runTests();
|
|
7
7
|
|
|
8
|
+
window.og_settings = {
|
|
9
|
+
// object were key is product id and value is an array of plan discount
|
|
10
|
+
product_discounts: {
|
|
11
|
+
UD729: {
|
|
12
|
+
'3_1': ['30.00', '10%', '$27.00'],
|
|
13
|
+
'1_2': ['30.00', '20%', '$24.00']
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
|
|
8
18
|
const frequencies = [
|
|
9
19
|
{
|
|
10
20
|
every: 1,
|
|
@@ -364,7 +374,6 @@ function updateTemplate(target) {
|
|
|
364
374
|
|
|
365
375
|
const style = document.getElementById('offersGlobalCss');
|
|
366
376
|
style.innerHTML = '';
|
|
367
|
-
console.log('it.config', JSON.parse(data).config, getStyles(JSON.parse(data).config));
|
|
368
377
|
style.appendChild(document.createTextNode(getStyles(JSON.parse(data).config)));
|
|
369
378
|
|
|
370
379
|
document.getElementById('the-html').innerText = (JSON.parse(data).markup || '').trim();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ordergroove/offers",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.25.0",
|
|
4
4
|
"description": "offer state component",
|
|
5
5
|
"author": "Eugenio Lattanzio <eugenio63@gmail.com>",
|
|
6
6
|
"homepage": "https://github.com/ordergroove/plush-toys#readme",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
],
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@ordergroove/auth": "^2.2.0",
|
|
37
|
-
"@ordergroove/offers-live-editor": "^0.4.
|
|
37
|
+
"@ordergroove/offers-live-editor": "^0.4.1",
|
|
38
38
|
"lit-element": "^2.1.0",
|
|
39
39
|
"lodash.memoize": "^4.1.2",
|
|
40
40
|
"logical-expression-parser": "1.0.0",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"throttle-debounce": "^2.1.0"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
|
-
"@ordergroove/offers-templates": "^0.4.
|
|
46
|
+
"@ordergroove/offers-templates": "^0.4.4"
|
|
47
47
|
},
|
|
48
|
-
"gitHead": "
|
|
48
|
+
"gitHead": "0b4d34f68c6a288cf8b1aa36af02f6d5838c81ab"
|
|
49
49
|
}
|
|
@@ -1,16 +1,111 @@
|
|
|
1
|
-
import offers from '../index';
|
|
1
|
+
import { offers } from '../index';
|
|
2
|
+
import { api } from '../core/api';
|
|
2
3
|
|
|
3
4
|
describe('Offers', () => {
|
|
5
|
+
let register, fetchOfferSpy, mockStore;
|
|
4
6
|
// TODO revisit chunks
|
|
5
7
|
it('should have setPublicPath method as compatibility', () => {
|
|
6
8
|
offers.setPublicPath('yum-path');
|
|
7
9
|
});
|
|
8
10
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
register = spyOn(offers, 'register');
|
|
13
|
+
fetchOfferSpy = spyOn(api, 'fetchOffer');
|
|
14
|
+
const dispatch = jasmine.createSpy();
|
|
15
|
+
mockStore = {
|
|
16
|
+
getState() {
|
|
17
|
+
return { sessionId: 'xyz' };
|
|
18
|
+
},
|
|
19
|
+
dispatch
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
offers.isReady = false;
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('should warn if attempting to initialize twice', () => {
|
|
26
|
+
const warn = spyOn(console, 'warn');
|
|
27
|
+
|
|
11
28
|
offers.initialize('0e5de2bedc5e11e3a2e4bc764e106cf4', 'staging');
|
|
12
|
-
expect(
|
|
29
|
+
expect(warn).not.toHaveBeenCalled();
|
|
30
|
+
|
|
13
31
|
offers.initialize('0e5de2bedc5e11e3a2e4bc764e106cf4', 'staging');
|
|
14
|
-
expect(
|
|
32
|
+
expect(warn).toHaveBeenCalledWith('og.offers has been initialized already. Skipping.');
|
|
33
|
+
expect(register).toHaveBeenCalledTimes(1);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('should warn if attempting to initialize twice', () => {
|
|
37
|
+
const resolveSettings = spyOn(offers, 'resolveSettings');
|
|
38
|
+
offers.initialize('0e5de2bedc5e11e3a2e4bc764e106cf4', 'staging');
|
|
39
|
+
expect(resolveSettings).toHaveBeenCalledWith(
|
|
40
|
+
'0e5de2bedc5e11e3a2e4bc764e106cf4',
|
|
41
|
+
'staging',
|
|
42
|
+
undefined,
|
|
43
|
+
offers.store
|
|
44
|
+
);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
describe('offers.resolveSettings', () => {
|
|
48
|
+
it('should fetch offer for single product in pdp', () => {
|
|
49
|
+
offers.resolveSettings('0e5de2bedc5e11e3a2e4bc764e106cf4', 'staging', { product: '123' }, mockStore);
|
|
50
|
+
expect(fetchOfferSpy).toHaveBeenCalledWith(
|
|
51
|
+
'https://staging.om.ordergroove.com',
|
|
52
|
+
'0e5de2bedc5e11e3a2e4bc764e106cf4',
|
|
53
|
+
'xyz',
|
|
54
|
+
'123',
|
|
55
|
+
'pdp'
|
|
56
|
+
);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('should fetch offer for each product in cart', () => {
|
|
60
|
+
offers.resolveSettings(
|
|
61
|
+
'0e5de2bedc5e11e3a2e4bc764e106cf4',
|
|
62
|
+
'staging',
|
|
63
|
+
{ cart: { products: ['123', '456'] } },
|
|
64
|
+
mockStore
|
|
65
|
+
);
|
|
66
|
+
expect(fetchOfferSpy).toHaveBeenCalledTimes(2);
|
|
67
|
+
|
|
68
|
+
expect(fetchOfferSpy.calls.argsFor(0)).toEqual([
|
|
69
|
+
'https://staging.om.ordergroove.com',
|
|
70
|
+
'0e5de2bedc5e11e3a2e4bc764e106cf4',
|
|
71
|
+
'xyz',
|
|
72
|
+
'123',
|
|
73
|
+
'pdp'
|
|
74
|
+
]);
|
|
75
|
+
expect(fetchOfferSpy.calls.argsFor(1)).toEqual([
|
|
76
|
+
'https://staging.om.ordergroove.com',
|
|
77
|
+
'0e5de2bedc5e11e3a2e4bc764e106cf4',
|
|
78
|
+
'xyz',
|
|
79
|
+
'456',
|
|
80
|
+
'pdp'
|
|
81
|
+
]);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('should set product price and discounts', () => {
|
|
85
|
+
offers.resolveSettings(
|
|
86
|
+
'0e5de2bedc5e11e3a2e4bc764e106cf4',
|
|
87
|
+
'staging',
|
|
88
|
+
{
|
|
89
|
+
// object were key is product id and value is an array of plan discount
|
|
90
|
+
product_discounts: {
|
|
91
|
+
123: {
|
|
92
|
+
'2_2': ['30.00', '20%', '$24.00'],
|
|
93
|
+
'1_3': ['30.00', '10%', '$27.00']
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
mockStore
|
|
98
|
+
);
|
|
99
|
+
expect(mockStore.dispatch).toHaveBeenCalledTimes(1);
|
|
100
|
+
expect(mockStore.dispatch).toHaveBeenCalledWith({
|
|
101
|
+
type: 'RECEIVE_PRODUCT_PLANS',
|
|
102
|
+
payload: {
|
|
103
|
+
123: {
|
|
104
|
+
'2_2': ['30.00', '20%', '$24.00'],
|
|
105
|
+
'1_3': ['30.00', '10%', '$27.00']
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
});
|
|
15
110
|
});
|
|
16
111
|
});
|
package/src/components/Offer.js
CHANGED
|
@@ -134,13 +134,22 @@ export class Offer extends TemplateElement {
|
|
|
134
134
|
return `
|
|
135
135
|
<og-when test="regularEligible">
|
|
136
136
|
<div>
|
|
137
|
+
|
|
137
138
|
<og-optout-button>
|
|
138
139
|
<og-text key="offerOptOutLabel"></og-text>
|
|
139
140
|
</og-optout-button>
|
|
140
141
|
</div>
|
|
141
142
|
<div>
|
|
142
143
|
<og-optin-button>
|
|
143
|
-
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
<og-price discount>
|
|
147
|
+
<span slot="prepend">Subscribe and get</span>
|
|
148
|
+
<span slot="append">off</span>
|
|
149
|
+
</og-price>
|
|
150
|
+
<og-price regular></og-price>
|
|
151
|
+
<og-price subscription></og-price>
|
|
152
|
+
|
|
144
153
|
</og-optin-button>
|
|
145
154
|
<og-tooltip placement="bottom">
|
|
146
155
|
<div slot="trigger">
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { html } from 'lit-element';
|
|
2
|
+
import { connect } from '../core/connect';
|
|
3
|
+
|
|
4
|
+
import { withProduct } from '../core/resolveProperties';
|
|
5
|
+
import { TemplateElement } from '../core/base';
|
|
6
|
+
import { makeProductFrequencySelector } from '../core/selectors';
|
|
7
|
+
|
|
8
|
+
export class Price extends withProduct(TemplateElement) {
|
|
9
|
+
static get properties() {
|
|
10
|
+
return {
|
|
11
|
+
...super.properties,
|
|
12
|
+
regular: { type: Boolean, reflect: true },
|
|
13
|
+
subscription: { type: Boolean, reflect: true },
|
|
14
|
+
discount: { type: Boolean, reflect: true },
|
|
15
|
+
frequency: { type: Object },
|
|
16
|
+
productPlans: { type: Object }
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
get value() {
|
|
21
|
+
const frequency = this.frequency || (this.offer && this.offer.defaultFrequency);
|
|
22
|
+
const plans = this.productPlans[this.product.id] || {};
|
|
23
|
+
const currentPlan = plans[frequency] || [];
|
|
24
|
+
if (!currentPlan) return '';
|
|
25
|
+
const [regularPrice, discountRate, subscriptionPrice] = currentPlan;
|
|
26
|
+
if (subscriptionPrice === regularPrice) return '';
|
|
27
|
+
|
|
28
|
+
if (this.regular) {
|
|
29
|
+
return regularPrice;
|
|
30
|
+
}
|
|
31
|
+
if (this.discount) return discountRate;
|
|
32
|
+
return subscriptionPrice;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
render() {
|
|
36
|
+
const value = this.value;
|
|
37
|
+
if (value)
|
|
38
|
+
return html`
|
|
39
|
+
<slot name="prepend"></slot>
|
|
40
|
+
${value}
|
|
41
|
+
<slot name="append"></slot>
|
|
42
|
+
`;
|
|
43
|
+
|
|
44
|
+
return html`
|
|
45
|
+
<slot></slot>
|
|
46
|
+
`;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
const mapStateToProps = (state, ownProps) => ({
|
|
50
|
+
productPlans: state.productPlans,
|
|
51
|
+
frequency: makeProductFrequencySelector(ownProps.product)(state)
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
export default connect(mapStateToProps)(Price);
|
|
@@ -2,10 +2,10 @@ const og = window.og;
|
|
|
2
2
|
describe('og.offers', function() {
|
|
3
3
|
it('should define og namespace', () => {
|
|
4
4
|
expect(og).toEqual(jasmine.any(Object));
|
|
5
|
-
expect(og.offers).toEqual(jasmine.any(
|
|
5
|
+
expect(og.offers).toEqual(jasmine.any(Object));
|
|
6
6
|
});
|
|
7
7
|
|
|
8
|
-
it('
|
|
8
|
+
it('should define register() method', () => {
|
|
9
9
|
expect(og.offers.register).toEqual(jasmine.any(Function));
|
|
10
10
|
});
|
|
11
11
|
});
|
|
@@ -0,0 +1,43 @@
|
|
|
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
|
+
});
|
package/src/core/actions.js
CHANGED
|
@@ -121,11 +121,14 @@ export const fetchOrders = (status = 1, ordering = 'place') =>
|
|
|
121
121
|
.then(
|
|
122
122
|
// eslint-disable-next-line camelcase
|
|
123
123
|
response => {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
124
|
+
if (response.results) {
|
|
125
|
+
dispatch(receiveOrders(response));
|
|
126
|
+
const nextOrderId = (response.results[0] || {}).public_id;
|
|
127
|
+
if (nextOrderId) {
|
|
128
|
+
return api.fetchItems(legoUrl, auth, nextOrderId).then(res => dispatch(receiveItems(res)));
|
|
129
|
+
}
|
|
128
130
|
}
|
|
131
|
+
dispatch(unauthorized(response.detail));
|
|
129
132
|
return null;
|
|
130
133
|
},
|
|
131
134
|
err => dispatch(unauthorized(err))
|
package/src/core/constants.js
CHANGED
|
@@ -36,3 +36,4 @@ export const LOCAL_STORAGE_CHANGE = 'LOCAL_STORAGE_CHANGE';
|
|
|
36
36
|
export const LOCAL_STORAGE_CLEAR = 'LOCAL_STORAGE_CLEAR';
|
|
37
37
|
export const SET_FIRST_ORDER_PLACE_DATE = 'SET_FIRST_ORDER_PLACE_DATE';
|
|
38
38
|
export const SET_PRODUCT_TO_SUBSCRIBE = 'SET_PRODUCT_TO_SUBSCRIBE';
|
|
39
|
+
export const RECEIVE_PRODUCT_PLANS = 'RECEIVE_PRODUCT_PLANS';
|
package/src/core/reducer.js
CHANGED
|
@@ -444,7 +444,17 @@ export const templates = (state = [], action) => {
|
|
|
444
444
|
}
|
|
445
445
|
};
|
|
446
446
|
|
|
447
|
+
export const productPlans = (state = {}, action) => {
|
|
448
|
+
switch (action.type) {
|
|
449
|
+
case constants.RECEIVE_PRODUCT_PLANS:
|
|
450
|
+
return { ...action.payload };
|
|
451
|
+
default:
|
|
452
|
+
return state;
|
|
453
|
+
}
|
|
454
|
+
};
|
|
455
|
+
|
|
447
456
|
export default combineReducers({
|
|
457
|
+
productPlans,
|
|
448
458
|
environment,
|
|
449
459
|
optedin,
|
|
450
460
|
optedout,
|