Skip to content

Commit a12d423

Browse files
authored
Merge pull request #3475 from SalesforceCommerceCloud/rvishwanathbhat/sfp-update-amount-prevent-reinit
@W-20117176 : Call updateAmount on Order Total change
2 parents 7290727 + d5e145b commit a12d423

File tree

2 files changed

+182
-6
lines changed

2 files changed

+182
-6
lines changed

packages/template-retail-react-app/app/pages/checkout/partials/sf-payments-sheet.jsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,12 @@ const SFPaymentsSheet = forwardRef((props, ref) => {
393393
}
394394
}, [sfp, metadata, containerElementRef.current, paymentConfig, cardCaptureAutomatic])
395395

396+
useEffect(() => {
397+
if (checkoutComponent.current && basket?.orderTotal) {
398+
checkoutComponent.current.updateAmount(basket.orderTotal)
399+
}
400+
}, [basket?.orderTotal])
401+
396402
return (
397403
<ToggleCard
398404
id="step-3"
@@ -406,14 +412,15 @@ const SFPaymentsSheet = forwardRef((props, ref) => {
406412
id: 'toggle_card.action.editPaymentInfo'
407413
})}
408414
>
415+
<Box display={step === STEPS.PAYMENT ? 'block' : 'none'}>
416+
<Box ref={containerElementRef} />
417+
</Box>
409418
<ToggleCardEdit>
410419
<Box mt={-2} mb={4}>
411420
<PromoCode {...promoCodeProps} itemProps={{border: 'none'}} />
412421
</Box>
413422

414423
<Stack spacing={6}>
415-
<Box ref={containerElementRef} />
416-
417424
<Divider borderColor="gray.100" />
418425

419426
<Stack spacing={2}>

packages/template-retail-react-app/app/pages/checkout/partials/sf-payments-sheet.test.js

Lines changed: 173 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ const mockStartConfirming = jest.fn()
152152
const mockEndConfirming = jest.fn()
153153
const mockCheckoutConfirm = jest.fn()
154154
const mockCheckoutDestroy = jest.fn()
155+
const mockUpdateAmount = jest.fn()
155156

156157
jest.mock('@salesforce/retail-react-app/app/hooks/use-sf-payments', () => {
157158
const actual = jest.requireActual('@salesforce/retail-react-app/app/hooks/use-sf-payments')
@@ -161,7 +162,8 @@ jest.mock('@salesforce/retail-react-app/app/hooks/use-sf-payments', () => {
161162
sfp: {
162163
checkout: jest.fn(() => ({
163164
confirm: mockCheckoutConfirm,
164-
destroy: mockCheckoutDestroy
165+
destroy: mockCheckoutDestroy,
166+
updateAmount: mockUpdateAmount
165167
}))
166168
},
167169
metadata: {key: 'value'},
@@ -198,15 +200,16 @@ jest.mock('@salesforce/retail-react-app/app/components/address-display', () => {
198200
})
199201

200202
jest.mock('@salesforce/retail-react-app/app/components/toggle-card', () => {
201-
const ToggleCard = ({children, title}) => (
202-
<div data-testid="toggle-card">
203+
const ToggleCard = ({children, title, editing}) => (
204+
<div data-testid="toggle-card" data-editing={editing ? 'true' : 'false'}>
203205
<h2>{title}</h2>
204206
{children}
205207
</div>
206208
)
207209
ToggleCard.propTypes = {
208210
children: () => null,
209-
title: () => null
211+
title: () => null,
212+
editing: () => null
210213
}
211214

212215
const ToggleCardEdit = ({children}) => <div data-testid="toggle-card-edit">{children}</div>
@@ -746,4 +749,170 @@ describe('SFPaymentsSheet', () => {
746749
expect(mockOnRequiresPayButtonChange).not.toHaveBeenCalled()
747750
})
748751
})
752+
753+
describe('container element persistence', () => {
754+
test('payment container is rendered outside ToggleCardEdit to prevent unmounting', () => {
755+
renderWithCheckoutContext(
756+
<SFPaymentsSheet
757+
ref={mockRef}
758+
onCreateOrder={mockOnCreateOrder}
759+
onError={mockOnError}
760+
/>
761+
)
762+
763+
const toggleCard = screen.getByTestId('toggle-card')
764+
765+
expect(toggleCard).toBeInTheDocument()
766+
expect(toggleCard).toBeInTheDocument()
767+
})
768+
})
769+
770+
describe('updateAmount', () => {
771+
beforeEach(() => {
772+
mockUpdateAmount.mockClear()
773+
})
774+
775+
test('calls updateAmount when basket orderTotal changes', async () => {
776+
const initialBasket = {
777+
...mockBasket,
778+
orderTotal: 100.0
779+
}
780+
781+
mockUseCurrentBasket.mockImplementation(() => ({
782+
data: initialBasket,
783+
derivedData: {
784+
totalItems: 2,
785+
isMissingShippingAddress: false,
786+
isMissingShippingMethod: false,
787+
totalDeliveryShipments: 1,
788+
totalPickupShipments: 0
789+
},
790+
isLoading: false
791+
}))
792+
793+
const {rerender} = renderWithCheckoutContext(
794+
<SFPaymentsSheet
795+
ref={mockRef}
796+
onCreateOrder={mockOnCreateOrder}
797+
onError={mockOnError}
798+
/>
799+
)
800+
801+
await waitFor(() => {
802+
expect(screen.getByTestId('toggle-card')).toBeInTheDocument()
803+
})
804+
805+
await waitFor(
806+
() => {
807+
expect(mockUpdateAmount).toHaveBeenCalledWith(100.0)
808+
},
809+
{timeout: 2000}
810+
)
811+
812+
mockUpdateAmount.mockClear()
813+
814+
const updatedBasket = {
815+
...initialBasket,
816+
orderTotal: 150.0
817+
}
818+
819+
mockUseCurrentBasket.mockImplementation(() => ({
820+
data: updatedBasket,
821+
derivedData: {
822+
totalItems: 2,
823+
isMissingShippingAddress: false,
824+
isMissingShippingMethod: false,
825+
totalDeliveryShipments: 1,
826+
totalPickupShipments: 0
827+
},
828+
isLoading: false
829+
}))
830+
831+
rerender(
832+
<CheckoutProvider>
833+
<SFPaymentsSheet
834+
ref={mockRef}
835+
onCreateOrder={mockOnCreateOrder}
836+
onError={mockOnError}
837+
/>
838+
</CheckoutProvider>
839+
)
840+
841+
await waitFor(
842+
() => {
843+
expect(mockUpdateAmount).toHaveBeenCalledWith(150.0)
844+
},
845+
{timeout: 2000}
846+
)
847+
})
848+
849+
test('does not call updateAmount when orderTotal is undefined', async () => {
850+
const basketWithoutOrderTotal = {
851+
...mockBasket,
852+
orderTotal: undefined
853+
}
854+
855+
mockUseCurrentBasket.mockImplementation(() => ({
856+
data: basketWithoutOrderTotal,
857+
derivedData: {
858+
totalItems: 2,
859+
isMissingShippingAddress: false,
860+
isMissingShippingMethod: false,
861+
totalDeliveryShipments: 1,
862+
totalPickupShipments: 0
863+
},
864+
isLoading: false
865+
}))
866+
867+
renderWithCheckoutContext(
868+
<SFPaymentsSheet
869+
ref={mockRef}
870+
onCreateOrder={mockOnCreateOrder}
871+
onError={mockOnError}
872+
/>
873+
)
874+
875+
await waitFor(() => {
876+
expect(screen.getByTestId('toggle-card')).toBeInTheDocument()
877+
})
878+
879+
await new Promise((resolve) => setTimeout(resolve, 100))
880+
881+
expect(mockUpdateAmount).not.toHaveBeenCalled()
882+
})
883+
884+
test('calls updateAmount with correct orderTotal value on initial render', async () => {
885+
const basketWithOrderTotal = {
886+
...mockBasket,
887+
orderTotal: 250.75
888+
}
889+
890+
mockUseCurrentBasket.mockImplementation(() => ({
891+
data: basketWithOrderTotal,
892+
derivedData: {
893+
totalItems: 2,
894+
isMissingShippingAddress: false,
895+
isMissingShippingMethod: false,
896+
totalDeliveryShipments: 1,
897+
totalPickupShipments: 0
898+
},
899+
isLoading: false
900+
}))
901+
902+
renderWithCheckoutContext(
903+
<SFPaymentsSheet
904+
ref={mockRef}
905+
onCreateOrder={mockOnCreateOrder}
906+
onError={mockOnError}
907+
/>
908+
)
909+
910+
await waitFor(
911+
() => {
912+
expect(mockUpdateAmount).toHaveBeenCalledWith(250.75)
913+
},
914+
{timeout: 2000}
915+
)
916+
})
917+
})
749918
})

0 commit comments

Comments
 (0)