Skip to content

Commit 3ecf92d

Browse files
committed
feat(ui): Wire preferredIdentifier into SignUp's getInitialActiveIdentifier
When both email and phone are in the same tier (both enabled and both required, or both enabled and both optional), preferredIdentifier from appearance.options now breaks the tie instead of hardcoding email-first.
1 parent 38dc379 commit 3ecf92d

4 files changed

Lines changed: 102 additions & 17 deletions

File tree

packages/ui/src/components/SignUp/SignUpContinue.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { buildRequest, useFormControl } from '@/ui/utils/useFormControl';
1212
import { createUsernameError } from '@/ui/utils/usernameUtils';
1313

1414
import { SignInContext, useCoreSignUp, useEnvironment, useSignUpContext } from '../../contexts';
15-
import { descriptors, Flex, Flow, localizationKeys, useLocalizations } from '../../customizables';
15+
import { descriptors, Flex, Flow, localizationKeys, useAppearance, useLocalizations } from '../../customizables';
1616
import { useRouter } from '../../router';
1717
import { SignUpForm } from './SignUpForm';
1818
import type { ActiveIdentifier } from './signUpFormHelpers';
@@ -44,8 +44,9 @@ function SignUpContinueInternal() {
4444
const isWithinSignInContext = !!React.useContext(SignInContext);
4545
const isCombinedFlow = !!(_isCombinedFlow && !!isWithinSignInContext);
4646
const isProgressiveSignUp = userSettings.signUp.progressive;
47+
const { preferredIdentifier } = useAppearance().parsedOptions;
4748
const [activeCommIdentifierType, setActiveCommIdentifierType] = React.useState<ActiveIdentifier>(
48-
getInitialActiveIdentifier(attributes, userSettings.signUp.progressive),
49+
getInitialActiveIdentifier(attributes, userSettings.signUp.progressive, undefined, preferredIdentifier),
4950
);
5051
const ctx = useSignUpContext();
5152

packages/ui/src/components/SignUp/SignUpStart.tsx

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ function SignUpStartInternal(): JSX.Element {
3737
const clerk = useClerk();
3838
const status = useLoadingStatus();
3939
const signUp = useCoreSignUp();
40-
const { showOptionalFields } = useAppearance().parsedOptions;
40+
const { showOptionalFields, preferredIdentifier } = useAppearance().parsedOptions;
4141
const { userSettings, authConfig } = useEnvironment();
4242
const { navigate } = useRouter();
4343
const { attributes } = userSettings;
@@ -47,16 +47,21 @@ function SignUpStartInternal(): JSX.Element {
4747
const { afterSignUpUrl, signInUrl, unsafeMetadata, navigateOnSetActive } = ctx;
4848
const isCombinedFlow = !!(ctx.isCombinedFlow && !!isWithinSignInContext);
4949
const [activeCommIdentifierType, setActiveCommIdentifierType] = React.useState<ActiveIdentifier>(() =>
50-
getInitialActiveIdentifier(attributes, userSettings.signUp.progressive, {
51-
phoneNumber: ctx.initialValues?.phoneNumber === null ? undefined : ctx.initialValues?.phoneNumber,
52-
emailAddress: ctx.initialValues?.emailAddress === null ? undefined : ctx.initialValues?.emailAddress,
53-
...(isCombinedFlow
54-
? {
55-
emailAddress: signUp.emailAddress,
56-
phoneNumber: signUp.phoneNumber,
57-
}
58-
: {}),
59-
}),
50+
getInitialActiveIdentifier(
51+
attributes,
52+
userSettings.signUp.progressive,
53+
{
54+
phoneNumber: ctx.initialValues?.phoneNumber === null ? undefined : ctx.initialValues?.phoneNumber,
55+
emailAddress: ctx.initialValues?.emailAddress === null ? undefined : ctx.initialValues?.emailAddress,
56+
...(isCombinedFlow
57+
? {
58+
emailAddress: signUp.emailAddress,
59+
phoneNumber: signUp.phoneNumber,
60+
}
61+
: {}),
62+
},
63+
preferredIdentifier,
64+
),
6065
);
6166
const { t, locale } = useLocalizations();
6267
const initialValues = ctx.initialValues || {};

packages/ui/src/components/SignUp/__tests__/signUpFormHelpers.test.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1047,4 +1047,71 @@ describe('getInitialActiveIdentifier()', () => {
10471047
expect(getInitialActiveIdentifier(attributes, false)).toBe(null);
10481048
});
10491049
});
1050+
1051+
describe('respects preferredIdentifier on ties', () => {
1052+
it('returns phoneNumber in email-or-phone when preferredIdentifier is phoneNumber', () => {
1053+
const attributes = {
1054+
email_address: createAttributeData('email_address', true, false, true),
1055+
phone_number: createAttributeData('phone_number', true, false, true),
1056+
};
1057+
1058+
expect(getInitialActiveIdentifier(attributes, false, undefined, 'phoneNumber')).toBe('phoneNumber');
1059+
});
1060+
1061+
it('returns emailAddress in email-or-phone when preferredIdentifier is emailAddress', () => {
1062+
const attributes = {
1063+
email_address: createAttributeData('email_address', true, false, true),
1064+
phone_number: createAttributeData('phone_number', true, false, true),
1065+
};
1066+
1067+
expect(getInitialActiveIdentifier(attributes, false, undefined, 'emailAddress')).toBe('emailAddress');
1068+
});
1069+
1070+
it('returns phoneNumber when both are required and preferredIdentifier is phoneNumber', () => {
1071+
const attributes = {
1072+
email_address: createAttributeData('email_address', true, true, true),
1073+
phone_number: createAttributeData('phone_number', true, true, true),
1074+
};
1075+
1076+
expect(getInitialActiveIdentifier(attributes, false, undefined, 'phoneNumber')).toBe('phoneNumber');
1077+
});
1078+
1079+
it('defaults to emailAddress in email-or-phone when no preferredIdentifier', () => {
1080+
const attributes = {
1081+
email_address: createAttributeData('email_address', true, false, true),
1082+
phone_number: createAttributeData('phone_number', true, false, true),
1083+
};
1084+
1085+
expect(getInitialActiveIdentifier(attributes, false)).toBe('emailAddress');
1086+
});
1087+
1088+
it('ignores preferredIdentifier username since SignUp only has email/phone', () => {
1089+
const attributes = {
1090+
email_address: createAttributeData('email_address', true, false, true),
1091+
phone_number: createAttributeData('phone_number', true, false, true),
1092+
};
1093+
1094+
expect(getInitialActiveIdentifier(attributes, false, undefined, 'username')).toBe('emailAddress');
1095+
});
1096+
1097+
it('initialValues take precedence over preferredIdentifier', () => {
1098+
const attributes = {
1099+
email_address: createAttributeData('email_address', true, false, true),
1100+
phone_number: createAttributeData('phone_number', true, false, true),
1101+
};
1102+
1103+
expect(getInitialActiveIdentifier(attributes, false, { emailAddress: 'test@example.com' }, 'phoneNumber')).toBe(
1104+
'emailAddress',
1105+
);
1106+
});
1107+
1108+
it('returns phoneNumber in progressive signup email-or-phone with preferredIdentifier', () => {
1109+
const attributes = {
1110+
email_address: createAttributeData('email_address', true, false, true),
1111+
phone_number: createAttributeData('phone_number', true, false, true),
1112+
};
1113+
1114+
expect(getInitialActiveIdentifier(attributes, true, undefined, 'phoneNumber')).toBe('phoneNumber');
1115+
});
1116+
});
10501117
});

packages/ui/src/components/SignUp/signUpFormHelpers.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ export const getInitialActiveIdentifier = (
107107
attributes: Partial<Attributes>,
108108
isProgressiveSignUp: boolean,
109109
initialValues?: { phoneNumber?: string | null; emailAddress?: string | null },
110+
preferredIdentifier?: 'emailAddress' | 'phoneNumber' | 'username',
110111
): ActiveIdentifier => {
111112
if (initialValues?.emailAddress) {
112113
return 'emailAddress';
@@ -115,18 +116,29 @@ export const getInitialActiveIdentifier = (
115116
return 'phoneNumber';
116117
}
117118

119+
const preferred =
120+
preferredIdentifier === 'emailAddress' || preferredIdentifier === 'phoneNumber' ? preferredIdentifier : undefined;
121+
118122
if (emailOrPhone(attributes, isProgressiveSignUp)) {
119-
// If we are in the case of Email OR Phone, email takes priority
120-
return 'emailAddress';
123+
return preferred ?? 'emailAddress';
121124
}
122125

123126
const { email_address, phone_number } = attributes;
124127

125-
if (email_address?.enabled && isProgressiveSignUp ? email_address.required : email_address?.used_for_first_factor) {
128+
const emailMatches =
129+
email_address?.enabled && (isProgressiveSignUp ? email_address.required : email_address?.used_for_first_factor);
130+
const phoneMatches =
131+
phone_number?.enabled && (isProgressiveSignUp ? phone_number.required : phone_number?.used_for_first_factor);
132+
133+
if (emailMatches && phoneMatches) {
134+
return preferred ?? 'emailAddress';
135+
}
136+
137+
if (emailMatches) {
126138
return 'emailAddress';
127139
}
128140

129-
if (phone_number?.enabled && isProgressiveSignUp ? phone_number.required : phone_number?.used_for_first_factor) {
141+
if (phoneMatches) {
130142
return 'phoneNumber';
131143
}
132144

0 commit comments

Comments
 (0)