@ordergroove/offers 2.48.0 → 2.48.1-alpha-PR-1371-2.308
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ordergroove/offers",
|
|
3
|
-
"version": "2.48.
|
|
3
|
+
"version": "2.48.1-alpha-PR-1371-2.308+bb24afe7a",
|
|
4
4
|
"description": "offer state component",
|
|
5
5
|
"author": "Eugenio Lattanzio <eugenio63@gmail.com>",
|
|
6
6
|
"homepage": "https://github.com/ordergroove/plush-toys#readme",
|
|
@@ -49,5 +49,5 @@
|
|
|
49
49
|
"@ordergroove/offers-templates": "^0.10.0",
|
|
50
50
|
"@types/lodash.memoize": "^4.1.9"
|
|
51
51
|
},
|
|
52
|
-
"gitHead": "
|
|
52
|
+
"gitHead": "bb24afe7a715c5f0ea03ab04992bd4bc25cd648b"
|
|
53
53
|
}
|
|
@@ -11,7 +11,8 @@ import {
|
|
|
11
11
|
synchronizeCartOptin,
|
|
12
12
|
getTrackingEvent,
|
|
13
13
|
guessProductHandle,
|
|
14
|
-
synchronizeSellingPlan
|
|
14
|
+
synchronizeSellingPlan,
|
|
15
|
+
setupPdp
|
|
15
16
|
} from '../shopifyMiddleware';
|
|
16
17
|
import { getOrCreateHidden } from '../../core/utils';
|
|
17
18
|
|
|
@@ -508,3 +509,112 @@ describe('synchronizeSellingPlan', () => {
|
|
|
508
509
|
expect(getFormData(formElement)).toEqual({});
|
|
509
510
|
});
|
|
510
511
|
});
|
|
512
|
+
|
|
513
|
+
describe('setupPdp', () => {
|
|
514
|
+
let store = { dispatch: () => {} };
|
|
515
|
+
let offer;
|
|
516
|
+
|
|
517
|
+
function makeCartAddForm() {
|
|
518
|
+
const element = document.createElement('form');
|
|
519
|
+
element.action = '/cart/add';
|
|
520
|
+
element.innerHTML = `
|
|
521
|
+
<input value="variant-1" name="id" type="hidden">
|
|
522
|
+
<input value="2" name="quantity" type="hidden">
|
|
523
|
+
`;
|
|
524
|
+
document.body.appendChild(element);
|
|
525
|
+
return element;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// wait for the debounced syncProductId function to run
|
|
529
|
+
async function waitForDebounce() {
|
|
530
|
+
// order matters here - this allows the MutationObserver callback to run, which queues a syncProductId call
|
|
531
|
+
await Promise.resolve();
|
|
532
|
+
// then wait for the debounced function to execute
|
|
533
|
+
jasmine.clock().tick(100);
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
beforeEach(() => {
|
|
537
|
+
offer = document.createElement('og-offer');
|
|
538
|
+
offer.dataset.shopifyProductHandle = 'test-product-handle';
|
|
539
|
+
spyOn(offer, 'setAttribute').and.callThrough();
|
|
540
|
+
|
|
541
|
+
let form = makeCartAddForm();
|
|
542
|
+
form.append(offer);
|
|
543
|
+
|
|
544
|
+
fetchMock.route('/products/test-product-handle.js', {}, { method: 'GET' });
|
|
545
|
+
fetchMock.route('/cart.js', {}, { method: 'GET' });
|
|
546
|
+
|
|
547
|
+
fetchMock.mockGlobal();
|
|
548
|
+
jasmine.clock().install();
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
afterEach(() => {
|
|
552
|
+
fetchMock.removeRoutes().unmockGlobal();
|
|
553
|
+
jasmine.clock().uninstall();
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
it('should guess handle and fetch data', async () => {
|
|
557
|
+
await setupPdp(store, offer);
|
|
558
|
+
|
|
559
|
+
expect(
|
|
560
|
+
fetchMock.callHistory.called({
|
|
561
|
+
url: 'path:/products/test-product-handle.js'
|
|
562
|
+
})
|
|
563
|
+
).toBe(true);
|
|
564
|
+
});
|
|
565
|
+
|
|
566
|
+
it('should update product attribute when hidden ID input is modified', async () => {
|
|
567
|
+
await setupPdp(store, offer);
|
|
568
|
+
|
|
569
|
+
const form = offer.closest('form');
|
|
570
|
+
// update variant ID
|
|
571
|
+
const idInput = form.querySelector('input[name="id"]');
|
|
572
|
+
idInput.value = 'variant-2';
|
|
573
|
+
|
|
574
|
+
await waitForDebounce();
|
|
575
|
+
|
|
576
|
+
expect(offer.setAttribute).toHaveBeenCalledWith('product', 'variant-2');
|
|
577
|
+
});
|
|
578
|
+
|
|
579
|
+
it('does not update product attribute if value attribute changes on a non-id input', async () => {
|
|
580
|
+
await setupPdp(store, offer);
|
|
581
|
+
|
|
582
|
+
const form = offer.closest('form');
|
|
583
|
+
const quantityInput = form.querySelector('input[name="quantity"]');
|
|
584
|
+
quantityInput.value = 5;
|
|
585
|
+
|
|
586
|
+
await waitForDebounce();
|
|
587
|
+
|
|
588
|
+
expect(offer.setAttribute).not.toHaveBeenCalled();
|
|
589
|
+
});
|
|
590
|
+
|
|
591
|
+
// this test is about covering existing behavior - in the future we may be able to strictly rely on the attribute change instead of subtree modifications
|
|
592
|
+
it('should update product attribute when node is added', async () => {
|
|
593
|
+
await setupPdp(store, offer);
|
|
594
|
+
|
|
595
|
+
const form = offer.closest('form');
|
|
596
|
+
const newInput = document.createElement('input');
|
|
597
|
+
newInput.name = 'other';
|
|
598
|
+
newInput.value = 'some-value';
|
|
599
|
+
|
|
600
|
+
form.appendChild(newInput);
|
|
601
|
+
|
|
602
|
+
await waitForDebounce();
|
|
603
|
+
|
|
604
|
+
// called with existing value in form
|
|
605
|
+
expect(offer.setAttribute).toHaveBeenCalledWith('product', 'variant-1');
|
|
606
|
+
});
|
|
607
|
+
|
|
608
|
+
// this test is about covering existing behavior - theoretically watching for changes to the "value" attribute should be sufficient
|
|
609
|
+
it('should update product attribute when change event fired', async () => {
|
|
610
|
+
await setupPdp(store, offer);
|
|
611
|
+
const form = offer.closest('form');
|
|
612
|
+
|
|
613
|
+
form.dispatchEvent(new Event('change'));
|
|
614
|
+
|
|
615
|
+
await waitForDebounce();
|
|
616
|
+
|
|
617
|
+
// called with existing value in form
|
|
618
|
+
expect(offer.setAttribute).toHaveBeenCalledWith('product', 'variant-1');
|
|
619
|
+
});
|
|
620
|
+
});
|
|
@@ -53,7 +53,7 @@ async function getCurrency() {
|
|
|
53
53
|
return cart.currency;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
async function setupPdp(store, offer) {
|
|
56
|
+
export async function setupPdp(store, offer) {
|
|
57
57
|
const handle = guessProductHandle(offer);
|
|
58
58
|
if (handle) {
|
|
59
59
|
try {
|
|
@@ -85,11 +85,26 @@ async function setupPdp(store, offer) {
|
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
if (form) {
|
|
88
|
-
//
|
|
88
|
+
// this code watches the cart form for changes, so that we can update the product ID on the offer when the variant changes
|
|
89
89
|
const syncProductId = makeSyncProductId(offer);
|
|
90
|
+
// watch the cart form for changes
|
|
91
|
+
// note: changes to hidden inputs do not fire change events
|
|
90
92
|
form.addEventListener('change', () => syncProductId(form));
|
|
91
|
-
|
|
92
|
-
|
|
93
|
+
// watch for added/removed nodes anywhere in the cart form (including updates to text content)
|
|
94
|
+
// also watch for changes to the "value" attribute, which will catch changes to hidden inputs
|
|
95
|
+
const mo = new MutationObserver(e => {
|
|
96
|
+
// if we're only looking at attribute changes
|
|
97
|
+
if (e.every(it => it.type === 'attributes')) {
|
|
98
|
+
// sync the product ID only if the "id" input changed - this happens when the product variant changes
|
|
99
|
+
// for performance reasons, avoid reacting to every form attribute change
|
|
100
|
+
if (e.some(it => (it.target as HTMLInputElement).name === 'id')) {
|
|
101
|
+
syncProductId(form);
|
|
102
|
+
}
|
|
103
|
+
} else {
|
|
104
|
+
syncProductId(form);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
mo.observe(form, { subtree: true, childList: true, attributes: true, attributeFilter: ['value'] });
|
|
93
108
|
} else {
|
|
94
109
|
console.info('no /cart/add form found for og-offer', offer);
|
|
95
110
|
}
|