Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
ea3b3df
fix pwless login in checkout contact info
alexvuong Jul 8, 2025
dc12c4a
fix changelog
alexvuong Jul 8, 2025
fc5e79e
minor fix
alexvuong Jul 8, 2025
d48e1af
lint
alexvuong Jul 8, 2025
c1c3804
lint
alexvuong Jul 8, 2025
0e89a9d
small fix
alexvuong Jul 8, 2025
80b0bfd
clean up
alexvuong Jul 8, 2025
86d6229
build translations
alexvuong Jul 8, 2025
eea3677
provide context
alexvuong Jul 8, 2025
5d705a3
apply the same for use auth modal
alexvuong Jul 9, 2025
6af0bca
fix the logic in use auth modal
alexvuong Jul 10, 2025
91c722e
fix the logic in login page
alexvuong Jul 10, 2025
9f6a29c
fix the logic in login page
alexvuong Jul 10, 2025
58bbcb3
translations
alexvuong Jul 10, 2025
8dec3de
Merge branch 'develop' into fix-pwless-login-contact-checkout
alexvuong Jul 10, 2025
b0a004f
lint
alexvuong Jul 10, 2025
774ab91
Merge branch 'develop' into fix-pwless-login-contact-checkout
alexvuong Jul 10, 2025
952ef3e
lint
alexvuong Jul 10, 2025
1233881
lint
alexvuong Jul 10, 2025
874797a
Merge branch 'develop' into fix-pwless-login-contact-checkout
alexvuong Jul 10, 2025
db741e2
allow enter to be aware of passwordless
alexvuong Jul 11, 2025
9180224
lint
alexvuong Jul 11, 2025
847f979
edit changlog
alexvuong Jul 11, 2025
bddbfe4
remove constant
alexvuong Jul 11, 2025
443a51a
simplify the logic
alexvuong Jul 14, 2025
07d4e75
simplify the logic
alexvuong Jul 14, 2025
eaa944a
simplify the logic
alexvuong Jul 14, 2025
c9626da
simplify the logic
alexvuong Jul 14, 2025
168c9f2
revert test
alexvuong Jul 14, 2025
c5c5756
lint
alexvuong Jul 14, 2025
f329e64
Merge branch 'develop' into fix-pwless-login-contact-checkout
alexvuong Jul 14, 2025
2ec021a
revert constants.js
alexvuong Jul 14, 2025
c60168e
Merge remote-tracking branch 'origin/fix-pwless-login-contact-checkou…
alexvuong Jul 14, 2025
1fd92cd
PR feedback
alexvuong Jul 14, 2025
251b983
Merge branch 'develop' into fix-pwless-login-contact-checkout
alexvuong Jul 14, 2025
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
2 changes: 1 addition & 1 deletion packages/template-retail-react-app/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
- Minor updates to support BOPIS E2E tests [#2716](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/2716)
- Provide support for partial hydration [#2696](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/2696)
- Show Automatic Bonus Products on Cart Page [#2704](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/2704)

- Fix passwordless race conditions in checkout contact info form [#2758](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/2758)

## v6.1.0 (May 22, 2025)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ const SocialLogin = ({form, idps = []}) => {
return (
config && (
<Button
key={name}
onClick={() => {
onSocialLoginClick(name)
}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,11 +107,6 @@ const ContactInfo = ({isSocialEnabled = false, isPasswordlessEnabled = false, id

const submitForm = async (data) => {
setError(null)
if (isPasswordlessLoginClicked) {
handlePasswordlessLogin(data.email)
setIsPasswordlessLoginClicked(false)
return
}
try {
if (!data.password) {
await updateCustomerForBasket.mutateAsync({
Expand Down Expand Up @@ -166,8 +161,38 @@ const ContactInfo = ({isSocialEnabled = false, isPasswordlessEnabled = false, id
}
}, [showPasswordField])

const onPasswordlessLoginClick = async () => {
setIsPasswordlessLoginClicked(true)
const onPasswordlessLoginClick = async (e) => {
if (e) {
e.preventDefault()
e.stopPropagation()
}
// since the pwless is no longer to through form submission flow,
// we need to manual validate the email value here
const email = form.getValues().email
if (!email) {
form.setError('email', {
type: 'manual',
message: formatMessage({
defaultMessage: 'Please enter your email address.',
id: 'contact_info.error.email_required'
})
})
return
}

const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
if (!emailRegex.test(email)) {
form.setError('email', {
type: 'manual',
message: formatMessage({
defaultMessage: 'Please enter a valid email address.',
id: 'contact_info.error.email_invalid'
})
})
return
}
Copy link
Contributor

Choose a reason for hiding this comment

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

This would be the first time we introduce a regex to validate email in the app. The regex doesn't cover all valid email scenarios (e.g., internationalized domains, plus signs, etc.).

Should we stick with react-hook-form's native validation like we do in other places?

Example:

const validateEmail = async () => {
    const isValid = await form.trigger('email')
    return isValid
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

good call, I will work on that

Copy link
Contributor Author

Choose a reason for hiding this comment

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

it is fixed


await handlePasswordlessLogin(email)
}

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ jest.mock('../util/checkout-context', () => {

afterEach(() => {
jest.resetModules()
jest.restoreAllMocks()
})

describe('passwordless and social disabled', () => {
Expand Down Expand Up @@ -149,17 +150,17 @@ describe('passwordless enabled', () => {

test('allows passwordless login', async () => {
jest.spyOn(window, 'location', 'get').mockReturnValue({
pathname: '/checkout'
pathname: '/checkout',
origin: 'https://example.com'
})

const {user} = renderWithProviders(<ContactInfo isPasswordlessEnabled={true} />)

// enter a valid email address
await user.type(screen.getByLabelText('Email'), validEmail)

// initiate passwordless login
const passwordlessLoginButton = screen.getByText('Secure Link')
// Click the button twice as the isPasswordlessLoginClicked state doesn't change after the first click
await user.click(passwordlessLoginButton)
await user.click(passwordlessLoginButton)
expect(
mockAuthHelperFunctions[AuthHelpers.AuthorizePasswordless].mutateAsync
Expand All @@ -177,7 +178,7 @@ describe('passwordless enabled', () => {
})

// resend the email
user.click(screen.getByText(/Resend Link/i))
await user.click(screen.getByText(/Resend Link/i))
expect(
mockAuthHelperFunctions[AuthHelpers.AuthorizePasswordless].mutateAsync
).toHaveBeenCalledWith({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const LoginState = ({
<Button
variant="outline"
borderColor="gray.500"
type="submit"
type="button"
Copy link
Contributor Author

Choose a reason for hiding this comment

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

We know that pwless does not require the same flow as other flows, we can safely use type=button to avoid it go through submission and handle the flow right in the onPasswordlessLoginClick handler

onClick={() => {
handlePasswordlessLoginClick()
}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1181,6 +1181,18 @@
"value": "Secure Link"
}
],
"contact_info.error.email_invalid": [
{
"type": 0,
"value": "Please enter a valid email address."
}
],
"contact_info.error.email_required": [
{
"type": 0,
"value": "Please enter your email address."
}
],
"contact_info.error.incorrect_username_or_password": [
{
"type": 0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1181,6 +1181,18 @@
"value": "Secure Link"
}
],
"contact_info.error.email_invalid": [
{
"type": 0,
"value": "Please enter a valid email address."
}
],
"contact_info.error.email_required": [
{
"type": 0,
"value": "Please enter your email address."
}
],
"contact_info.error.incorrect_username_or_password": [
{
"type": 0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2389,6 +2389,34 @@
"value": "]"
}
],
"contact_info.error.email_invalid": [
{
"type": 0,
"value": "["
},
{
"type": 0,
"value": "Ƥŀḗḗȧȧşḗḗ ḗḗƞŧḗḗř ȧȧ ṽȧȧŀīḓ ḗḗḿȧȧīŀ ȧȧḓḓřḗḗşş."
},
{
"type": 0,
"value": "]"
}
],
"contact_info.error.email_required": [
{
"type": 0,
"value": "["
},
{
"type": 0,
"value": "Ƥŀḗḗȧȧşḗḗ ḗḗƞŧḗḗř ẏǿǿŭŭř ḗḗḿȧȧīŀ ȧȧḓḓřḗḗşş."
},
{
"type": 0,
"value": "]"
}
],
"contact_info.error.incorrect_username_or_password": [
{
"type": 0,
Expand Down
6 changes: 6 additions & 0 deletions packages/template-retail-react-app/translations/en-GB.json
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,12 @@
"contact_info.button.secure_link": {
"defaultMessage": "Secure Link"
},
"contact_info.error.email_invalid": {
"defaultMessage": "Please enter a valid email address."
},
"contact_info.error.email_required": {
"defaultMessage": "Please enter your email address."
},
"contact_info.error.incorrect_username_or_password": {
"defaultMessage": "Incorrect username or password, please try again."
},
Expand Down
6 changes: 6 additions & 0 deletions packages/template-retail-react-app/translations/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,12 @@
"contact_info.button.secure_link": {
"defaultMessage": "Secure Link"
},
"contact_info.error.email_invalid": {
"defaultMessage": "Please enter a valid email address."
},
"contact_info.error.email_required": {
"defaultMessage": "Please enter your email address."
},
"contact_info.error.incorrect_username_or_password": {
"defaultMessage": "Incorrect username or password, please try again."
},
Expand Down
Loading