Skip to content

Commit

Permalink
fix(clerk-js,localizations,types): Add min and max lengths to usernam…
Browse files Browse the repository at this point in the history
…e field error (#4771)
  • Loading branch information
alexcarpenter authored Dec 12, 2024
1 parent 5c5f1db commit aeafa7c
Show file tree
Hide file tree
Showing 8 changed files with 64 additions and 5 deletions.
7 changes: 7 additions & 0 deletions .changeset/cold-tomatoes-float.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@clerk/localizations': patch
'@clerk/clerk-js': patch
'@clerk/types': patch
---

Added min and max length username settings to username field error.
10 changes: 10 additions & 0 deletions packages/clerk-js/src/core/resources/UserSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type {
SamlSettings,
SignInData,
SignUpData,
UsernameSettingsData,
UserSettingsJSON,
UserSettingsJSONSnapshot,
UserSettingsResource,
Expand All @@ -19,6 +20,9 @@ import { BaseResource } from './internal';
const defaultMaxPasswordLength = 72;
const defaultMinPasswordLength = 8;

const defaultMinUsernameLength = 4;
const defaultMaxUsernameLength = 64;

export type Actions = {
create_organization: boolean;
delete_self: boolean;
Expand All @@ -40,6 +44,7 @@ export class UserSettings extends BaseResource implements UserSettingsResource {
signUp!: SignUpData;
passwordSettings!: PasswordSettingsData;
passkeySettings!: PasskeySettingsData;
usernameSettings!: UsernameSettingsData;

socialProviderStrategies: OAuthStrategy[] = [];
authenticatableSocialStrategies: OAuthStrategy[] = [];
Expand Down Expand Up @@ -85,6 +90,11 @@ export class UserSettings extends BaseResource implements UserSettingsResource {
? defaultMaxPasswordLength
: Math.min(data?.password_settings?.max_length, defaultMaxPasswordLength),
};
this.usernameSettings = {
...data.username_settings,
min_length: Math.max(data?.username_settings?.min_length, defaultMinUsernameLength),
max_length: Math.min(data?.username_settings?.max_length, defaultMaxUsernameLength),
};
this.passkeySettings = data.passkey_settings;
this.socialProviderStrategies = this.getSocialProviderStrategies(data.social);
this.authenticatableSocialStrategies = this.getAuthenticatableSocialStrategies(data.social);
Expand Down
5 changes: 3 additions & 2 deletions packages/clerk-js/src/ui/components/SignUp/SignUpStart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { useCardState } from '../../elements/contexts';
import { useLoadingStatus } from '../../hooks';
import { useRouter } from '../../router';
import type { FormControlState } from '../../utils';
import { buildRequest, createPasswordError, handleError, useFormControl } from '../../utils';
import { buildRequest, createPasswordError, createUsernameError, handleError, useFormControl } from '../../utils';
import { SignUpForm } from './SignUpForm';
import type { ActiveIdentifier } from './signUpFormHelpers';
import { determineActiveFields, emailOrPhone, getInitialActiveIdentifier, showFormFields } from './signUpFormHelpers';
Expand Down Expand Up @@ -52,7 +52,7 @@ function _SignUpStart(): JSX.Element {
const [missingRequirementsWithTicket, setMissingRequirementsWithTicket] = React.useState(false);

const {
userSettings: { passwordSettings },
userSettings: { passwordSettings, usernameSettings },
} = useEnvironment();

const { mode } = userSettings.signUp;
Expand All @@ -78,6 +78,7 @@ function _SignUpStart(): JSX.Element {
label: localizationKeys('formFieldLabel__username'),
placeholder: localizationKeys('formFieldInputPlaceholder__username'),
transformer: value => value.trim(),
buildErrorMessage: errors => createUsernameError(errors, { t, locale, usernameSettings }),
}),
phoneNumber: useFormControl('phoneNumber', signUp.phoneNumber || initialValues.phoneNumber || '', {
type: 'tel',
Expand Down
1 change: 1 addition & 0 deletions packages/clerk-js/src/ui/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ export * from './createCustomPages';
export * from './ExternalElementMounter';
export * from './colorOptionToHslaScale';
export * from './createCustomMenuItems';
export * from './usernameUtils';
7 changes: 7 additions & 0 deletions packages/clerk-js/src/ui/utils/useFormControl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,13 @@ type Options = {
buildErrorMessage?: (err: ClerkAPIError[]) => ClerkAPIError | string | undefined;
radioOptions?: never;
}
| {
label: string | LocalizationKey;
type: Extract<HTMLInputTypeAttribute, 'text'>;
validatePassword?: never;
buildErrorMessage?: (err: ClerkAPIError[]) => ClerkAPIError | string | undefined;
radioOptions?: never;
}
| {
validatePassword?: never;
buildErrorMessage?: never;
Expand Down
26 changes: 26 additions & 0 deletions packages/clerk-js/src/ui/utils/usernameUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import type { ClerkAPIError, UsernameSettingsData } from '@clerk/types';

import { type LocalizationKey, localizationKeys } from '../localization';

type LocalizationConfigProps = {
t: (localizationKey: LocalizationKey | string | undefined) => string;
locale: string;
usernameSettings: Pick<UsernameSettingsData, 'max_length' | 'min_length'>;
};

export const createUsernameError = (errors: ClerkAPIError[], localizationConfig: LocalizationConfigProps) => {
const { t, usernameSettings } = localizationConfig;

if (!localizationConfig) {
return errors[0].longMessage;
}

const msg = t(
localizationKeys('unstable__errors.form_username_invalid_length', {
min_length: usernameSettings.min_length,
max_length: usernameSettings.max_length,
}),
);

return msg;
};
6 changes: 3 additions & 3 deletions packages/localizations/src/en-US.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ export const enUS: LocalizationResource = {
formFieldInputPlaceholder__organizationDomainEmailAddress: 'you@example.com',
formFieldInputPlaceholder__organizationName: 'Organization name',
formFieldInputPlaceholder__organizationSlug: 'my-org',
formFieldInputPlaceholder__username: undefined,
formFieldInputPlaceholder__password: 'Enter your password',
formFieldInputPlaceholder__phoneNumber: 'Enter your phone number',
formFieldInputPlaceholder__username: undefined,
formFieldLabel__automaticInvitations: 'Enable automatic invitations for this domain',
formFieldLabel__backupCode: 'Backup code',
formFieldLabel__confirmDeletion: 'Confirmation',
Expand Down Expand Up @@ -439,6 +439,7 @@ export const enUS: LocalizationResource = {
detailsLabel: 'We need to verify your identity before resetting your password.',
},
start: {
__experimental_titleCombined: 'Continue to {{applicationName}}',
actionLink: 'Sign up',
actionLink__join_waitlist: 'Join waitlist',
actionLink__use_email: 'Use email',
Expand All @@ -450,7 +451,6 @@ export const enUS: LocalizationResource = {
actionText__join_waitlist: 'Want early access?',
subtitle: 'Welcome back! Please sign in to continue',
title: 'Sign in to {{applicationName}}',
__experimental_titleCombined: 'Continue to {{applicationName}}',
},
totpMfa: {
formTitle: 'Verification code',
Expand Down Expand Up @@ -566,7 +566,7 @@ export const enUS: LocalizationResource = {
form_password_validation_failed: 'Incorrect Password',
form_username_invalid_character:
'Your username contains invalid characters. Please use only letters, numbers, and underscores.',
form_username_invalid_length: 'Your username must be between 3 and 20 characters long.',
form_username_invalid_length: 'Your username must be between {{min_length}} and {{max_length}} characters long.',
identification_deletion_failed: 'You cannot delete your last identification.',
not_allowed_access:
'You do not have permission to access this page. Please contact support if you believe this is an error.',
Expand Down
7 changes: 7 additions & 0 deletions packages/types/src/userSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@ export type PasswordSettingsData = {
min_zxcvbn_strength: number;
};

export type UsernameSettingsData = {
min_length: number;
max_length: number;
};

export type PasskeySettingsData = {
allow_autofill: boolean;
show_sign_in_button: boolean;
Expand Down Expand Up @@ -119,6 +124,7 @@ export interface UserSettingsJSON extends ClerkResourceJSON {
sign_up: SignUpData;
password_settings: PasswordSettingsData;
passkey_settings: PasskeySettingsData;
username_settings: UsernameSettingsData;
}

export interface UserSettingsResource extends ClerkResource {
Expand All @@ -136,6 +142,7 @@ export interface UserSettingsResource extends ClerkResource {
signIn: SignInData;
signUp: SignUpData;
passwordSettings: PasswordSettingsData;
usernameSettings: UsernameSettingsData;
passkeySettings: PasskeySettingsData;
socialProviderStrategies: OAuthStrategy[];
authenticatableSocialStrategies: OAuthStrategy[];
Expand Down

0 comments on commit aeafa7c

Please sign in to comment.