Skip to content

Commit 09f5a85

Browse files
erikdstockclaude
andcommitted
refactor(checkout): use lenient yup schema for saved address validation
Create savedAddressValidationSchema that validates core address fields without strict phone validation, since saved addresses may have incomplete phone data (e.g., missing region code). This is more maintainable than manual boolean checks while being appropriately lenient for saved addresses. The strict deliveryAddressValidationSchema is still used for form submission to ensure complete and valid data before checkout. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent 0a95fcf commit 09f5a85

File tree

2 files changed

+40
-21
lines changed

2 files changed

+40
-21
lines changed

src/Apps/Order2/Routes/Checkout/Components/FulfillmentDetailsStep/__tests__/utils.jest.ts

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -129,9 +129,9 @@ describe("FulfillmentDetailsStep utils", () => {
129129
region: "NY",
130130
postalCode: "10001",
131131
country: "US",
132-
phoneNumber: "1234567890",
133-
phoneNumberCountryCode: "US",
134-
phoneNumberParsed: { display: "+1 1234567890" },
132+
phoneNumber: "5551234567",
133+
phoneNumberCountryCode: "+1",
134+
phoneNumberParsed: { display: "+1 5551234567" },
135135
isDefault: false,
136136
},
137137
},
@@ -145,9 +145,9 @@ describe("FulfillmentDetailsStep utils", () => {
145145
region: "ON",
146146
postalCode: "M5V 3A8",
147147
country: "CA",
148-
phoneNumber: "9876543210",
149-
phoneNumberCountryCode: "CA",
150-
phoneNumberParsed: { display: "+1 9876543210" },
148+
phoneNumber: "4165551234",
149+
phoneNumberCountryCode: "+1",
150+
phoneNumberParsed: { display: "+1 4165551234" },
151151
isDefault: true,
152152
},
153153
},
@@ -162,14 +162,14 @@ describe("FulfillmentDetailsStep utils", () => {
162162
)
163163

164164
expect(result).toHaveLength(2)
165-
// After sorting: usable addresses come first (non-shippable default comes after usable)
165+
// After sorting: shippable addresses come first, then non-shippable
166166
expect(result[0]).toMatchObject({
167167
internalID: "address1",
168168
isShippable: true, // US is in available countries
169-
isValid: true, // All required fields present
169+
isValid: true, // Lenient validation passes
170170
isDefault: false,
171-
phoneNumber: "1234567890",
172-
phoneNumberCountryCode: "US",
171+
phoneNumber: "5551234567",
172+
phoneNumberCountryCode: "+1",
173173
address: {
174174
name: "John Doe",
175175
country: "US",
@@ -179,10 +179,10 @@ describe("FulfillmentDetailsStep utils", () => {
179179
expect(result[1]).toMatchObject({
180180
internalID: "address2",
181181
isShippable: false, // CA is not in available countries
182-
isValid: true, // All required fields present
182+
isValid: true, // Lenient validation passes
183183
isDefault: true,
184-
phoneNumber: "9876543210",
185-
phoneNumberCountryCode: "CA",
184+
phoneNumber: "4165551234",
185+
phoneNumberCountryCode: "+1",
186186
address: {
187187
name: "Jane Smith",
188188
country: "CA",

src/Apps/Order2/Routes/Checkout/Components/FulfillmentDetailsStep/utils.ts

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ type MeAddresses = ExtractNodeType<
1414
type GravityAddress = ReturnType<typeof extractNodes<MeAddresses>>[number]
1515

1616
export type ProcessedUserAddress = FormikContextWithAddress & {
17+
/** valid shipping country for this order */
1718
isShippable: boolean
19+
/** pre-validated with our client schema (except phone number) */
1820
isValid: boolean
1921
internalID: string
2022
isDefault: boolean
@@ -43,17 +45,20 @@ export const countryNameFromAlpha2 = (country: string): string => {
4345
return COUNTRY_CODE_TO_COUNTRY_NAME[country.toUpperCase()] || country
4446
}
4547

48+
/**
49+
* Validates saved address fields with lenient phone validation.
50+
* Checks for required address fields and phone number presence,
51+
* but does not validate phone number format or region code.
52+
*/
4653
export const validateAddressFields = (
4754
values: FormikContextWithAddress,
4855
): boolean => {
49-
return (
50-
!!values.address.name &&
51-
!!values.address.country &&
52-
!!values.address.addressLine1 &&
53-
!!values.address.city &&
54-
!!values.phoneNumber &&
55-
!!values.phoneNumberCountryCode
56-
)
56+
try {
57+
savedAddressValidationSchema.validateSync(values)
58+
return true
59+
} catch {
60+
return false
61+
}
5762
}
5863

5964
export const processSavedAddresses = (
@@ -83,6 +88,11 @@ export const processSavedAddresses = (
8388

8489
export const sortAddressesByPriority = (addresses: ProcessedUserAddress[]) => {
8590
return [...addresses].sort((a, b) => {
91+
// Shippable addresses always come before non-shippable
92+
if (a.isShippable && !b.isShippable) return -1
93+
if (!a.isShippable && b.isShippable) return 1
94+
95+
// Within same shippability group, sort by validity and default status
8696
const aUsable = a.isShippable && a.isValid
8797
const bUsable = b.isShippable && b.isValid
8898

@@ -151,6 +161,15 @@ export const findInitialSelectedAddress = (
151161
return processedAddresses[0]
152162
}
153163

164+
/**
165+
* Schema for pre-validating saved addresses - lenient on phone validation
166+
* since saved addresses may have incomplete phone data (e.g., missing region code)
167+
*/
168+
export const savedAddressValidationSchema = yup.object().shape({
169+
...addressFormFieldsValidator(), // Just address fields, no phone
170+
phoneNumber: yup.string().required(), // Just check it exists, not format
171+
})
172+
154173
export const deliveryAddressValidationSchema = yup
155174
.object()
156175
.shape(addressFormFieldsValidator({ withPhoneNumber: true }))

0 commit comments

Comments
 (0)