Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -153,18 +153,31 @@ export const SavedAddressOptions = ({
}
}, [isStepActive])

const loneInvalidAddress =
savedAddresses.length === 1 && initialSelectedAddress?.isValid === false
? initialSelectedAddress
: null

// Auto-open edit form for the single saved address if it has missing fields
// biome-ignore lint/correctness/useExhaustiveDependencies: intentionally runs once on mount
useEffect(() => {
if (savedAddresses.length !== 1 || !initialSelectedAddress) return
if (!initialSelectedAddress.isValid) {
setUserAddressMode({ mode: "edit", address: initialSelectedAddress })
if (loneInvalidAddress) {
setUserAddressMode({ mode: "edit", address: loneInvalidAddress })
}
}, [])

// Reactively set/clear error banner based on selected address validity and shippability
useEffect(() => {
if (!selectedAddress || userAddressMode || !isStepActive) return
if (!selectedAddress || !isStepActive) return

// Skip while the user is adding or editing an address — except for the lone
// invalid address above, whose form is open precisely because of this error.
// In the offer flow the step isn't active on mount, so this effect first
// runs once the form is already open; skipping would drop the error (and
// its `errorMessageViewed` event) entirely.
const isEditingLoneInvalidAddress =
userAddressMode?.mode === "edit" && !!loneInvalidAddress
if (userAddressMode && !isEditingLoneInvalidAddress) return

if (!selectedAddress.isShippable && !isOffer) {
setSectionErrorMessage({
Expand All @@ -189,6 +202,7 @@ export const SavedAddressOptions = ({
}, [
selectedAddress,
userAddressMode,
loneInvalidAddress,
isOffer,
setSectionErrorMessage,
shippingOriginRegion,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -709,6 +709,84 @@ describe("SavedAddressOptions", () => {
})
})

it("surfaces the error banner for the auto-opened edit form once the step becomes active", async () => {
const mockSingleInvalidAddress: ProcessedUserAddress = {
...mockUSAddress1,
isValid: false,
address: { ...mockUSAddress1.address, city: "" },
}

const contextWith = ({
state,
userAddressMode,
}: {
state: CheckoutStepState
userAddressMode: { mode: "edit"; address: ProcessedUserAddress } | null
}) => {
return {
...mockCheckoutContext,
isOffer: true,
userAddressMode,
steps: [{ name: CheckoutStepName.FULFILLMENT_DETAILS, state }],
} as any
}

const props = {
savedAddresses: [mockSingleInvalidAddress],
initialSelectedAddress: mockSingleInvalidAddress,
}

// Offer flow: fulfillment details is not active yet, and the single
// invalid address auto-opens its edit form on mount.
mockUseCheckoutContext.mockReturnValue(
contextWith({
state: CheckoutStepState.UPCOMING,
userAddressMode: null,
}),
)

const { rerender } = renderSavedAddressOptions(props)

await waitFor(() => {
expect(mockCheckoutContext.setUserAddressMode).toHaveBeenCalledWith({
mode: "edit",
address: mockSingleInvalidAddress,
})
})
expect(mockCheckoutContext.setSectionErrorMessage).not.toHaveBeenCalled()

const userAddressMode = {
mode: "edit",
address: mockSingleInvalidAddress,
} as const

mockUseCheckoutContext.mockReturnValue(
contextWith({ state: CheckoutStepState.UPCOMING, userAddressMode }),
)
rerender(buildSavedAddressOptions(props))

expect(mockCheckoutContext.setSectionErrorMessage).not.toHaveBeenCalled()

// Completing the offer step activates fulfillment details, and the error
// explaining why the form is open is surfaced (and tracked as viewed).
mockUseCheckoutContext.mockReturnValue(
contextWith({ state: CheckoutStepState.ACTIVE, userAddressMode }),
)
rerender(buildSavedAddressOptions(props))

await waitFor(() => {
expect(mockCheckoutContext.setSectionErrorMessage).toHaveBeenCalledWith(
{
section: CheckoutStepName.FULFILLMENT_DETAILS,
error: {
title: "Missing required information",
message: "Edit your address and/or phone number to continue.",
},
},
)
})
})

it("does not open edit form on mount for single valid address", async () => {
renderSavedAddressOptions({ savedAddresses: [mockUSAddress1] })

Expand Down
Loading