Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/template-retail-react-app/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## v9.1.0-dev
- Add Node 24 support. Drop Node 16 support [#3652](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/3652)
- [Bugfix] Fix error toast for no applicable shipping methods in one-click checkout [#3673](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/3673)

## v9.0.0 (Feb 12, 2026)
- [Feature] One Click Checkout (in Developer Preview) [#3552](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/3552)
Expand Down
9 changes: 9 additions & 0 deletions packages/template-retail-react-app/app/pages/cart/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,15 @@ const Cart = () => {
return
}

// Don't assign methods until at least one delivery shipment has an address
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The change to Cart page I mentioned @patricksullivansf

const deliveryShipments = basket.shipments?.filter((s) => !isPickupShipment(s)) || []
const hasDeliveryWithAddress = deliveryShipments.some(
(s) => s.shippingAddress?.address1
)
if (deliveryShipments.length > 0 && !hasDeliveryWithAddress) {
return
}

setIsProcessingShippingMethods(true)
try {
await updateShipmentsWithoutMethods()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,14 @@ export default function ShippingAddress(props) {
const {enableUserRegistration = false, isShipmentCleanupComplete = true} = props
const {formatMessage} = useIntl()
const [isManualSubmitLoading, setIsManualSubmitLoading] = useState(false)
const [isMultiShipping, setIsMultiShipping] = useState(false)
const [openedByUser, setOpenedByUser] = useState(false)
const {data: customer} = useCurrentCustomer()
const currentBasketQuery = useCurrentBasket()
const {data: basket} = currentBasketQuery
const deliveryShipments =
basket?.shipments?.filter((shipment) => !isPickupShipment(shipment)) || []
const hasMultipleDeliveryShipments = deliveryShipments.length > 1
const [isMultiShipping, setIsMultiShipping] = useState(hasMultipleDeliveryShipments)
const [openedByUser, setOpenedByUser] = useState(false)
Comment on lines +56 to +58
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice

const selectedShippingAddress = deliveryShipments[0]?.shippingAddress
const targetDeliveryShipmentId = deliveryShipments[0]?.shipmentId || 'me'
const isAddressFilled = selectedShippingAddress?.address1 && selectedShippingAddress?.city
Expand All @@ -65,7 +66,7 @@ export default function ShippingAddress(props) {
'updateShippingAddressForShipment'
)
const multishipEnabled = getConfig()?.app?.multishipEnabled ?? true
const hasMultipleDeliveryShipments = deliveryShipments.length > 1

const {removeEmptyShipments} = useMultiship(basket)
const {updateItemsToDeliveryShipment} = useItemShipmentManagement(basket?.basketId)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -618,6 +618,14 @@ describe('ShippingAddress Component', () => {
expect(screen.getByRole('button', {name: 'Ship items to one address'})).toBeInTheDocument()
})
test('should consolidate multiple shipments when shipping to single address', async () => {
mockUpdateShippingAddress.mutateAsync.mockResolvedValue({})
mockUpdateCustomerAddress.mutateAsync.mockResolvedValue({})
mockUpdateItemsToDeliveryShipment.mockResolvedValue({
basketId: 'test-basket-id',
shipments: []
})
mockRemoveEmptyShipments.mockResolvedValue({})

jest.resetModules()
jest.doMock('@salesforce/retail-react-app/app/hooks/use-current-basket', () => ({
useCurrentBasket: () => ({
Expand Down Expand Up @@ -658,18 +666,30 @@ describe('ShippingAddress Component', () => {

const {user} = localRenderWithProviders(<Component />)

//only get first
const continueButton = screen.getAllByRole('button', {
name: 'Continue to Shipping Method'
})[0]
// With multiple delivery shipments the component shows multi-address view first.
// Click "Ship items to one address" to switch to single-address form.
const shipToOneAddressButtons = screen.getAllByRole('button', {
name: 'Ship items to one address'
})
await act(async () => {
await user.click(shipToOneAddressButtons[0])
})

const continueButtons = screen.getAllByRole('button', {
name: 'Continue to Shipping Method'
})
const continueButton = continueButtons[0]
await act(async () => {
await user.click(continueButton)
})
// Expect removeEmptyShipments to be called
expect(mockRemoveEmptyShipments).toHaveBeenCalled()
// Expect updateItemsToDeliveryShipment to be called
expect(mockUpdateItemsToDeliveryShipment).toHaveBeenCalled()

// Wait for async submit flow (address update -> optional customer address -> remove empty)
await waitFor(() => {
expect(mockUpdateShippingAddress.mutateAsync).toHaveBeenCalled()
})
await waitFor(() => {
expect(mockRemoveEmptyShipments).toHaveBeenCalled()
})
})

test('does not show multiship option when only one delivery item exists with pickup items', async () => {
Expand Down Expand Up @@ -907,8 +927,10 @@ describe('ShippingAddress Component', () => {
mockUpdateShippingAddress.mutateAsync.mockResolvedValue({})
const {user} = renderWithProviders(<ShippingAddress enableUserRegistration={true} />)

const stepContainers = screen.getAllByTestId('sf-toggle-card-step-1')
const selection = within(stepContainers[0]).getByTestId('shipping-address-selection')
// Wait for the address selection form to be visible (may race with auto-select)
const selection = await waitFor(() => screen.getByTestId('shipping-address-selection'), {
timeout: 2000
})
const submitButton = within(selection).getByRole('button', {
name: /Continue to Shipping Method/i
})
Expand Down
Loading
Loading