@salesforce/retail-react-app 8.1.0 → 8.2.0-nightly-20250929080228

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 CHANGED
@@ -1,3 +1,5 @@
1
+ ## v8.2.0-dev (Sep 26, 2025)
2
+
1
3
  ## v8.1.0 (Sep 25, 2025)
2
4
  - Updated search UX - prices, images, suggestions new layout [#3271](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/3271)
3
5
  - Updated the UI for StoreDisplay component which displays pickup in-store information on different pages. [#3248](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/3248)
@@ -1146,7 +1146,6 @@ const Cart = () => {
1146
1146
  />
1147
1147
  )
1148
1148
  )}
1149
-
1150
1149
  {/* Render grouped bonus products from this shipment */}
1151
1150
  {shipmentInfo.categorizedProducts.bonusProducts
1152
1151
  ?.filter(
@@ -1191,7 +1190,6 @@ const Cart = () => {
1191
1190
  }}
1192
1191
  />
1193
1192
  ))}
1194
-
1195
1193
  {/* Render SelectBonusProductsCard for each bonusDiscountLineItem */}
1196
1194
  {basket.bonusDiscountLineItems?.map(
1197
1195
  (bonusDiscountLineItem) => {
@@ -1241,7 +1239,6 @@ const Cart = () => {
1241
1239
  )
1242
1240
  }
1243
1241
  )}
1244
-
1245
1242
  {/* Render orphaned bonus products (bonus products without bonusDiscountLineItemId) */}
1246
1243
  {(() => {
1247
1244
  const orphanedBonusProducts =
@@ -552,7 +552,8 @@ test('Can edit address during checkout as a registered customer', async () => {
552
552
  expect(screen.getByTestId('sf-toggle-card-step-2-content')).not.toBeEmptyDOMElement()
553
553
  })
554
554
 
555
- expect(screen.getByText('369 Main Street')).toBeInTheDocument()
555
+ const shippingAddressCard = screen.getByTestId('sf-toggle-card-step-1-content')
556
+ expect(within(shippingAddressCard).getByText('369 Main Street')).toBeInTheDocument()
556
557
  })
557
558
 
558
559
  test('Can add address during checkout as a registered customer', async () => {
@@ -192,6 +192,34 @@ export default function ShippingMethods() {
192
192
  )
193
193
  if (hasNewFields) {
194
194
  form.reset(newDefaults)
195
+ deliveryShipments.forEach(async (shipment) => {
196
+ const methodId = newDefaults[`shippingMethodId_${shipment.shipmentId}`]
197
+ const hasMethodInBasket = shipment.shippingMethod && shipment.shippingMethod.id
198
+
199
+ // auto-submit if;
200
+ // - default method to submit present
201
+ // - the shipment doesn't already have a method in basket
202
+ // - user hasn't manually selected
203
+ if (
204
+ methodId &&
205
+ !hasMethodInBasket &&
206
+ methodId === shippingMethods?.defaultShippingMethodId
207
+ ) {
208
+ try {
209
+ await updateShippingMethod.mutateAsync({
210
+ parameters: {
211
+ basketId: basket.basketId,
212
+ shipmentId: shipment.shipmentId
213
+ },
214
+ body: {
215
+ id: methodId
216
+ }
217
+ })
218
+ } catch (error) {
219
+ console.warn(error)
220
+ }
221
+ }
222
+ })
195
223
  }
196
224
  }, [deliveryShipments.length, shippingMethods?.defaultShippingMethodId])
197
225
 
@@ -12,7 +12,11 @@ import ShippingMethods from '@salesforce/retail-react-app/app/pages/checkout/par
12
12
  import {useCheckout} from '@salesforce/retail-react-app/app/pages/checkout/util/checkout-context'
13
13
  import {useCurrentBasket} from '@salesforce/retail-react-app/app/hooks/use-current-basket'
14
14
  import {useCurrency} from '@salesforce/retail-react-app/app/hooks'
15
- import {useShippingMethodsForShipment, useProducts} from '@salesforce/commerce-sdk-react'
15
+ import {
16
+ useShippingMethodsForShipment,
17
+ useProducts,
18
+ useShopperBasketsMutation
19
+ } from '@salesforce/commerce-sdk-react'
16
20
 
17
21
  // Mock the hooks
18
22
  jest.mock('@salesforce/retail-react-app/app/pages/checkout/util/checkout-context')
@@ -25,6 +29,7 @@ const mockUseCurrentBasket = useCurrentBasket
25
29
  const mockUseCurrency = useCurrency
26
30
  const mockUseShippingMethodsForShipment = useShippingMethodsForShipment
27
31
  const mockUseProducts = useProducts
32
+ const mockUseShopperBasketsMutation = useShopperBasketsMutation
28
33
 
29
34
  // Mock data
30
35
  const mockBasket = {
@@ -169,6 +174,7 @@ describe('ShippingMethods', () => {
169
174
  data: mockProductsMap,
170
175
  isLoading: false
171
176
  })
177
+ mockUseShopperBasketsMutation.mockReturnValue(jest.fn().mockResolvedValue({}))
172
178
  })
173
179
 
174
180
  afterEach(() => {
@@ -538,4 +544,160 @@ describe('ShippingMethods', () => {
538
544
  expect(screen.getByText('Standard Shipping')).toBeInTheDocument()
539
545
  })
540
546
  })
547
+
548
+ describe('auto-submit functionality', () => {
549
+ test('should auto-submit default shipping method when available', async () => {
550
+ const basketWithoutMethods = {
551
+ ...mockBasket,
552
+ shipments: [
553
+ {
554
+ ...mockBasket.shipments[0],
555
+ shippingMethod: null
556
+ }
557
+ ]
558
+ }
559
+
560
+ const mockShippingMethods = {
561
+ defaultShippingMethodId: 'default-shipping-method',
562
+ applicableShippingMethods: [
563
+ {
564
+ id: 'default-shipping-method',
565
+ name: 'Default Shipping'
566
+ }
567
+ ]
568
+ }
569
+
570
+ const mockMutateAsync = jest.fn().mockResolvedValue({})
571
+ mockUseShopperBasketsMutation.mockReturnValue({
572
+ updateShippingMethod: {mutateAsync: mockMutateAsync}
573
+ })
574
+
575
+ // after auto-submit, step should advance to PAYMENT (summary mode)
576
+ mockUseCheckout.mockReturnValue({
577
+ step: 'PAYMENT',
578
+ STEPS: {SHIPPING_OPTIONS: 'SHIPPING_OPTIONS', PAYMENT: 'PAYMENT'},
579
+ goToStep: jest.fn(),
580
+ goToNextStep: jest.fn()
581
+ })
582
+
583
+ mockUseCurrentBasket.mockReturnValue({
584
+ data: basketWithoutMethods,
585
+ derivedData: {
586
+ totalShippingCost: 5.99,
587
+ isMissingShippingMethod: false
588
+ },
589
+ isLoading: false
590
+ })
591
+
592
+ mockUseShippingMethodsForShipment.mockReturnValue({
593
+ data: mockShippingMethods,
594
+ isLoading: false
595
+ })
596
+
597
+ renderWithIntl(<ShippingMethods />)
598
+
599
+ // component is in SUMMARY mode (collapsed) after auto-submit
600
+ expect(screen.getByRole('button', {name: 'Edit Shipping Options'})).toBeInTheDocument()
601
+ expect(
602
+ screen.queryByRole('radio', {name: 'Default Shipping $5.99'})
603
+ ).not.toBeInTheDocument()
604
+ expect(
605
+ screen.queryByRole('button', {name: 'Continue to Payment'})
606
+ ).not.toBeInTheDocument()
607
+ })
608
+
609
+ test('should not auto-submit if shipment already has a method', async () => {
610
+ // Mock basket that already has a shipping method
611
+ const basketWithMethod = {
612
+ ...mockBasket,
613
+ shipments: [
614
+ {
615
+ ...mockBasket.shipments[0],
616
+ shippingMethod: {
617
+ id: 'existing-method',
618
+ name: 'Existing Shipping'
619
+ }
620
+ }
621
+ ]
622
+ }
623
+
624
+ const mockUpdateShippingMethod = jest.fn().mockResolvedValue({})
625
+ mockUpdateShippingMethod.mutateAsync = jest.fn().mockResolvedValue({})
626
+
627
+ // Mock the mutation hook
628
+ mockUseShopperBasketsMutation.mockReturnValue(mockUpdateShippingMethod)
629
+
630
+ mockUseCurrentBasket.mockReturnValue({
631
+ data: basketWithMethod,
632
+ derivedData: {totalShippingCost: 0},
633
+ isLoading: false
634
+ })
635
+
636
+ mockUseShippingMethodsForShipment.mockReturnValue({
637
+ data: {
638
+ defaultShippingMethodId: 'default-method',
639
+ applicableShippingMethods: []
640
+ },
641
+ isLoading: false
642
+ })
643
+
644
+ renderWithIntl(<ShippingMethods />)
645
+
646
+ // no auto-submit happens
647
+ await waitFor(() => {
648
+ expect(mockUpdateShippingMethod).not.toHaveBeenCalled()
649
+ })
650
+ })
651
+
652
+ test('should not auto-submit if user has manually selected a different method', async () => {
653
+ const basketWithoutMethods = {
654
+ ...mockBasket,
655
+ shipments: [
656
+ {
657
+ ...mockBasket.shipments[0],
658
+ shippingMethod: null
659
+ }
660
+ ]
661
+ }
662
+
663
+ // Mock shipping methods with default
664
+ const mockShippingMethods = {
665
+ defaultShippingMethodId: 'default-method',
666
+ applicableShippingMethods: [
667
+ {
668
+ id: 'default-method',
669
+ name: 'Default Shipping'
670
+ },
671
+ {
672
+ id: 'user-selected-method',
673
+ name: 'User Selected Shipping'
674
+ }
675
+ ]
676
+ }
677
+
678
+ const mockUpdateShippingMethod = jest.fn().mockResolvedValue({})
679
+ mockUpdateShippingMethod.mutateAsync = jest.fn().mockResolvedValue({})
680
+
681
+ // Mock the mutation hook
682
+ mockUseShopperBasketsMutation.mockReturnValue(mockUpdateShippingMethod)
683
+
684
+ mockUseCurrentBasket.mockReturnValue({
685
+ data: basketWithoutMethods,
686
+ derivedData: {totalShippingCost: 0},
687
+ isLoading: false
688
+ })
689
+
690
+ mockUseShippingMethodsForShipment.mockReturnValue({
691
+ data: mockShippingMethods,
692
+ isLoading: false
693
+ })
694
+
695
+ renderWithIntl(<ShippingMethods />)
696
+
697
+ // no auto-submit happens because the form would have user-selected-method, not default-method)
698
+ await waitFor(() => {
699
+ expect(mockUpdateShippingMethod).not.toHaveBeenCalled()
700
+ })
701
+ })
702
+ })
541
703
  })
@@ -58,7 +58,6 @@ describe('Bonus Product Cart Utilities', () => {
58
58
  expect(result[0].quantity).toBe(2)
59
59
  })
60
60
  })
61
-
62
61
  describe('getBonusProductsForSpecificCartItem', () => {
63
62
  const extendedBasket = {
64
63
  bonusDiscountLineItems: [
@@ -1543,7 +1542,6 @@ describe('Bonus Product Cart Utilities', () => {
1543
1542
  })
1544
1543
  })
1545
1544
  })
1546
-
1547
1545
  describe('findAllBonusProductItemsToRemove', () => {
1548
1546
  test('finds all bonus products with same productId and promotionId', () => {
1549
1547
  const targetBonusProduct = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/retail-react-app",
3
- "version": "8.1.0",
3
+ "version": "8.2.0-nightly-20250929080228",
4
4
  "license": "See license in LICENSE",
5
5
  "author": "cc-pwa-kit@salesforce.com",
6
6
  "ccExtensibility": {
@@ -46,10 +46,10 @@
46
46
  "@loadable/component": "^5.15.3",
47
47
  "@peculiar/webcrypto": "^1.4.2",
48
48
  "@salesforce/cc-datacloud-typescript": "1.1.2",
49
- "@salesforce/commerce-sdk-react": "4.1.0",
50
- "@salesforce/pwa-kit-dev": "3.13.0",
51
- "@salesforce/pwa-kit-react-sdk": "3.13.0",
52
- "@salesforce/pwa-kit-runtime": "3.13.0",
49
+ "@salesforce/commerce-sdk-react": "4.2.0-nightly-20250929080228",
50
+ "@salesforce/pwa-kit-dev": "3.14.0-nightly-20250929080228",
51
+ "@salesforce/pwa-kit-react-sdk": "3.14.0-nightly-20250929080228",
52
+ "@salesforce/pwa-kit-runtime": "3.14.0-nightly-20250929080228",
53
53
  "@tanstack/react-query": "^4.28.0",
54
54
  "@tanstack/react-query-devtools": "^4.29.1",
55
55
  "@testing-library/dom": "^9.0.1",
@@ -107,5 +107,5 @@
107
107
  "maxSize": "335 kB"
108
108
  }
109
109
  ],
110
- "gitHead": "aec99418a128126c0cc4bbee70086d2c83112018"
110
+ "gitHead": "92d2291c0f1ffeccd9e43cc0e40eb9d8bca643e7"
111
111
  }