Skip to content

Commit f075c42

Browse files
authored
Merge pull request #2701 from bigcommerce/BACK-779
feat(storefront): BACK-779 Decouple Show backorder message from Qty on Backorder settings
2 parents 41828b9 + b89916c commit f075c42

6 files changed

Lines changed: 52 additions & 27 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

77
## Draft
8+
- Decouple "Show backorder message" from "Qty on Backorder" settings so each works independently on PDP [#2701](https://github.com/bigcommerce/cornerstone/pull/2701)
89

910
## 6.20.0 (07-02-2026)
1011
- Display the cart shipping expectation prompt on its own full-width row beneath the Shipping label, keeping the shipping cost on the same row as the Shipping label [#2692](https://github.com/bigcommerce/cornerstone/pull/2692)

assets/js/test-unit/theme/common/picklist-backorder.spec.js

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,7 @@ describe('PicklistBackorder', () => {
424424
expect($('[data-picklist-backorder-list] li', $scope).length).toBe(0);
425425
});
426426

427-
it('skips the entire line when context.showQuantityOnBackorder is false', () => {
427+
it('shows only the backorder message when showQuantityOnBackorder is false', () => {
428428
$scope = buildScope(oneAttr('Bundle 1', 98, 'opt'));
429429
const renderer = new PicklistBackorder(
430430
$scope,
@@ -440,6 +440,30 @@ describe('PicklistBackorder', () => {
440440
})],
441441
}, 5);
442442

443+
const $items = $('[data-picklist-backorder-list] li', $scope);
444+
expect($items.length).toBe(1);
445+
const text = $items.eq(0).text();
446+
expect(text).toContain('Backorder message 1');
447+
expect(text).not.toContain('will be backordered');
448+
expect(text).not.toContain('|');
449+
});
450+
451+
it('hides the list when showQuantityOnBackorder is false and no backorder message exists', () => {
452+
$scope = buildScope(oneAttr('Bundle 1', 98, 'opt'));
453+
const renderer = new PicklistBackorder(
454+
$scope,
455+
{ ...context, showQuantityOnBackorder: false, showBackorderMessage: false },
456+
);
457+
458+
renderer.render({
459+
selected_picklist_options: [selection()],
460+
picklist_products_details: [detail({
461+
available_on_hand: 0,
462+
available_for_backorder: 10,
463+
backorder_message_id: 2,
464+
})],
465+
}, 5);
466+
443467
const $list = $('[data-picklist-backorder-list]', $scope);
444468
expect($list.children('li').length).toBe(0);
445469
expect($list.css('display')).toBe('none');

assets/js/theme/common/picklist-backorder.js

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,6 @@ export default class PicklistBackorder {
3333
}
3434

3535
buildLines(selections, detailsByProductId, mainQty) {
36-
if (this.context.showQuantityOnBackorder === false) {
37-
return [];
38-
}
39-
4036
const lines = [];
4137
const requestedQty = parseInt(mainQty, 10) || 0;
4238

@@ -143,24 +139,32 @@ export default class PicklistBackorder {
143139
return;
144140
}
145141

142+
const showQty = this.context.showQuantityOnBackorder !== false;
146143
const qtyTemplate = this.context.quantityBackorderedMessage
147144
|| '__QTY__ will be backordered';
148145

149146
lines.forEach(({ name, qty, productId }) => {
150-
const qtyMsg = qtyTemplate.replace('__QTY__', qty);
147+
const qtyMsg = showQty ? qtyTemplate.replace('__QTY__', qty) : '';
151148
const messageText = this.lookupBackorderMessage(productId);
152-
const suffix = messageText ? ` | ${messageText}` : '';
149+
150+
if (!qtyMsg && !messageText) return;
151+
153152
const $li = $('<li>', {
154153
class: 'productView-picklist-backorder-item',
155154
});
156155
$('<span>', {
157156
class: 'productView-picklist-backorder-name',
158157
}).text(`${name}:`).appendTo($li);
159-
$li.append(document.createTextNode(` ${qtyMsg}${suffix}`));
158+
const parts = [qtyMsg, messageText].filter(Boolean);
159+
$li.append(document.createTextNode(` ${parts.join(' | ')}`));
160160
this.$list.append($li);
161161
});
162162

163-
this.$list.show();
163+
if (this.$list.children().length) {
164+
this.$list.show();
165+
} else {
166+
this.$list.hide();
167+
}
164168
}
165169

166170
lookupBackorderMessage(productId) {

assets/js/theme/common/product-details-base.js

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -433,12 +433,6 @@ export default class ProductDetailsBase {
433433

434434
if (!viewModel.$backordered.length) return;
435435

436-
if (this.context.showQuantityOnBackorder === false) {
437-
viewModel.$backorderedQtyMessage.text('');
438-
this.toggleBackorderedContainer(viewModel);
439-
return;
440-
}
441-
442436
const contextOnHand = parseInt(this.context.availableOnHand, 10);
443437
const stockFromDom = parseInt(viewModel.stock.$input.text(), 10);
444438
let onHand = 0;
@@ -453,28 +447,24 @@ export default class ProductDetailsBase {
453447
const availableForBackorder = unlimited
454448
? Infinity
455449
: parseInt(this.context.availableForBackorder, 10) || 0;
456-
const backordered = Math.max(0, Math.min(qty - onHand, availableForBackorder));
450+
this.backorderedQty = Math.max(0, Math.min(qty - onHand, availableForBackorder));
457451

458-
if (backordered > 0) {
452+
if (this.context.showQuantityOnBackorder !== false && this.backorderedQty > 0) {
459453
const message = this.context.quantityBackorderedMessage
460-
? this.context.quantityBackorderedMessage.replace('__QTY__', backordered)
461-
: `${backordered} will be backordered`;
454+
? this.context.quantityBackorderedMessage.replace('__QTY__', this.backorderedQty)
455+
: `${this.backorderedQty} will be backordered`;
462456
viewModel.$backorderedQtyMessage.text(message);
463457
} else {
464458
viewModel.$backorderedQtyMessage.text('');
465459
}
466-
467-
this.toggleBackorderedContainer(viewModel);
468460
}
469461

470462
updateBackorderMessage(passedViewModel) {
471463
const viewModel = passedViewModel || this.getViewModel(this.$scope);
472464

473465
if (!viewModel.$backordered.length) return;
474466

475-
const hasQtyMessage = viewModel.$backorderedQtyMessage.text().trim() !== '';
476-
477-
if (!hasQtyMessage) {
467+
if (!this.backorderedQty || this.backorderedQty <= 0) {
478468
viewModel.$backorderMessage.text('');
479469
return;
480470
}
@@ -493,9 +483,10 @@ export default class ProductDetailsBase {
493483
}
494484

495485
toggleBackorderedContainer(viewModel) {
496-
const hasQtyMessage = viewModel.$backorderedQtyMessage.text().trim() !== '';
486+
const hasContent = viewModel.$backorderedQtyMessage.text().trim() !== ''
487+
|| viewModel.$backorderMessage.text().trim() !== '';
497488

498-
if (hasQtyMessage) {
489+
if (hasContent) {
499490
viewModel.$backordered.show();
500491
} else {
501492
viewModel.$backordered.hide();
@@ -667,6 +658,7 @@ export default class ProductDetailsBase {
667658
const currentQty = parseInt(viewModel.quantity.$input.val(), 10) || 0;
668659
this.updateQtyBackorderedMessage(currentQty, viewModel);
669660
this.updateBackorderMessage(viewModel);
661+
this.toggleBackorderedContainer(viewModel);
670662
this.picklistBackorder.render(data, currentQty);
671663

672664
this.updateDefaultAttributesForOOS(data);

assets/js/theme/common/product-details.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,11 +93,13 @@ export default class ProductDetails extends ProductDetailsBase {
9393
// rule-hidden values or show an unavailable message for a no-longer-selected combination.
9494
// The corrective change runs its own optionChange for the corrected selection.
9595
if ($form.serialize() !== initialSelection) return;
96+
9697
this.updateBackorderContext(response.data);
9798
const vm = this.getViewModel(this.$scope);
9899
const qty = parseInt(vm.quantity.$input.val(), 10) || 0;
99100
this.updateQtyBackorderedMessage(qty, vm);
100101
this.updateBackorderMessage(vm);
102+
this.toggleBackorderedContainer(vm);
101103
this.picklistBackorder.render(response.data, qty);
102104
this.updateDefaultAttributesForOOS(response.data);
103105
// Apply out-of-stock hide/show from this same payload before the rule pass, so rule
@@ -417,6 +419,7 @@ export default class ProductDetails extends ProductDetailsBase {
417419
this.updateProductDetailsData();
418420
this.updateQtyBackorderedMessage(qty, viewModel);
419421
this.updateBackorderMessage(viewModel);
422+
this.toggleBackorderedContainer(viewModel);
420423
this.updateAddToCartForQty(qty, viewModel);
421424
this.picklistBackorder.rerender(qty);
422425
});
@@ -437,6 +440,7 @@ export default class ProductDetails extends ProductDetailsBase {
437440
this.updateProductDetailsData();
438441
this.updateQtyBackorderedMessage(qty, viewModel);
439442
this.updateBackorderMessage(viewModel);
443+
this.toggleBackorderedContainer(viewModel);
440444
this.updateAddToCartForQty(qty, viewModel);
441445
this.picklistBackorder.rerender(qty);
442446
});

assets/scss/components/stencil/productView/_productView.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@
231231
font-weight: fontWeight("bold");
232232
}
233233

234-
[data-backorder-message]:not(:empty)::before {
234+
[data-qty-backordered-message]:not(:empty) + [data-backorder-message]:not(:empty)::before {
235235
content: "| ";
236236
}
237237

0 commit comments

Comments
 (0)