diff --git a/src/app/features/auth/pages/reset-password/reset-password.component.html b/src/app/features/auth/pages/reset-password/reset-password.component.html
index 088211af8..0115e3a39 100644
--- a/src/app/features/auth/pages/reset-password/reset-password.component.html
+++ b/src/app/features/auth/pages/reset-password/reset-password.component.html
@@ -14,7 +14,7 @@
{{ 'auth.resetPassword.title' | translate }}
autocomplete="new-password"
>
-
+
diff --git a/src/app/features/auth/pages/reset-password/reset-password.component.ts b/src/app/features/auth/pages/reset-password/reset-password.component.ts
index 8e371b70d..0969df45c 100644
--- a/src/app/features/auth/pages/reset-password/reset-password.component.ts
+++ b/src/app/features/auth/pages/reset-password/reset-password.component.ts
@@ -30,7 +30,10 @@ export class ResetPasswordComponent {
resetPasswordForm: ResetPasswordFormGroupType = this.fb.group(
{
- newPassword: ['', [CustomValidators.requiredTrimmed(), Validators.pattern(this.passwordRegex)]],
+ newPassword: [
+ '',
+ [CustomValidators.requiredTrimmed(), Validators.minLength(8), Validators.pattern(this.passwordRegex)],
+ ],
confirmNewPassword: ['', CustomValidators.requiredTrimmed()],
},
{
@@ -38,10 +41,6 @@ export class ResetPasswordComponent {
}
);
- get isNewPasswordError() {
- return this.resetPasswordForm.get('newPassword')?.errors && this.resetPasswordForm.get('newPassword')?.touched;
- }
-
get isMismatchError(): boolean {
return (
this.resetPasswordForm.get('confirmNewPassword')?.dirty &&
diff --git a/src/app/features/auth/pages/sign-up/sign-up.component.html b/src/app/features/auth/pages/sign-up/sign-up.component.html
index 0a303cb2f..5c87f594c 100644
--- a/src/app/features/auth/pages/sign-up/sign-up.component.html
+++ b/src/app/features/auth/pages/sign-up/sign-up.component.html
@@ -89,7 +89,7 @@
{{ 'auth.signUp.title' | translate }}
autocomplete="new-password"
styleClass="w-full"
>
-
+
diff --git a/src/app/features/auth/pages/sign-up/sign-up.component.ts b/src/app/features/auth/pages/sign-up/sign-up.component.ts
index 47c5c5b14..9763ec4cc 100644
--- a/src/app/features/auth/pages/sign-up/sign-up.component.ts
+++ b/src/app/features/auth/pages/sign-up/sign-up.component.ts
@@ -47,16 +47,11 @@ export class SignUpComponent implements OnInit {
private readonly environment = inject(ENVIRONMENT);
signUpForm = new FormGroup({} as SignUpForm);
- passwordRegex: RegExp = PASSWORD_REGEX;
inputLimits = InputLimits;
isFormSubmitted = signal(false);
readonly siteKey = this.environment.recaptchaSiteKey;
- get isPasswordError() {
- return this.signUpForm.controls['password'].errors && this.signUpForm.get('password')?.touched;
- }
-
ngOnInit(): void {
this.initializeForm();
}
@@ -76,7 +71,7 @@ export class SignUpComponent implements OnInit {
}),
password: new FormControl('', {
nonNullable: true,
- validators: [CustomValidators.requiredTrimmed(), Validators.pattern(this.passwordRegex)],
+ validators: [CustomValidators.requiredTrimmed(), Validators.minLength(8), Validators.pattern(PASSWORD_REGEX)],
}),
acceptedTermsOfService: new FormControl(false, {
nonNullable: true,
diff --git a/src/app/features/settings/account-settings/components/change-password/change-password.component.html b/src/app/features/settings/account-settings/components/change-password/change-password.component.html
index 76a42e982..0564816dc 100644
--- a/src/app/features/settings/account-settings/components/change-password/change-password.component.html
+++ b/src/app/features/settings/account-settings/components/change-password/change-password.component.html
@@ -47,30 +47,6 @@ {{ 'settings.accountSettings.changePassword.title' | translate }}
"
/>
- @if (
- FormValidationHelper.hasError(getFormControl(AccountSettingsPasswordFormControls.NewPassword), 'required')
- ) {
-
- {{ 'settings.accountSettings.changePassword.validation.newPasswordRequired' | translate }}
-
- }
-
- @if (
- FormValidationHelper.hasError(getFormControl(AccountSettingsPasswordFormControls.NewPassword), 'minlength')
- ) {
-
- {{ 'settings.accountSettings.changePassword.validation.newPasswordMinLength' | translate }}
-
- }
-
- @if (
- FormValidationHelper.hasError(getFormControl(AccountSettingsPasswordFormControls.NewPassword), 'pattern')
- ) {
-
- {{ 'settings.accountSettings.changePassword.validation.newPasswordPattern' | translate }}
-
- }
-
@if (
getFormErrors()['sameAsOldPassword'] &&
FormValidationHelper.isFieldTouched(getFormControl(AccountSettingsPasswordFormControls.NewPassword))
@@ -80,9 +56,7 @@ {{ 'settings.accountSettings.changePassword.title' | translate }}
}
-
- {{ 'settings.accountSettings.changePassword.form.passwordRequirements' | translate }}
-
+
diff --git a/src/app/features/settings/account-settings/components/change-password/change-password.component.scss b/src/app/features/settings/account-settings/components/change-password/change-password.component.scss
index 3a4cf6910..e69de29bb 100644
--- a/src/app/features/settings/account-settings/components/change-password/change-password.component.scss
+++ b/src/app/features/settings/account-settings/components/change-password/change-password.component.scss
@@ -1,5 +0,0 @@
-.password-help {
- color: var(--pr-blue-1);
- font-size: 0.75rem;
- font-weight: 600;
-}
diff --git a/src/app/features/settings/account-settings/components/change-password/change-password.component.ts b/src/app/features/settings/account-settings/components/change-password/change-password.component.ts
index db3343e85..16e9d4237 100644
--- a/src/app/features/settings/account-settings/components/change-password/change-password.component.ts
+++ b/src/app/features/settings/account-settings/components/change-password/change-password.component.ts
@@ -20,7 +20,8 @@ import {
} from '@angular/forms';
import { AuthService } from '@osf/core/services';
-import { CustomValidators, FormValidationHelper } from '@osf/shared/helpers';
+import { PasswordInputHintComponent } from '@osf/shared/components';
+import { CustomValidators, FormValidationHelper, PASSWORD_REGEX } from '@osf/shared/helpers';
import { LoaderService, ToastService } from '@osf/shared/services';
import { AccountSettingsPasswordForm, AccountSettingsPasswordFormControls } from '../../models';
@@ -28,7 +29,7 @@ import { UpdatePassword } from '../../store';
@Component({
selector: 'osf-change-password',
- imports: [Card, ReactiveFormsModule, Password, CommonModule, Button, TranslatePipe],
+ imports: [Card, ReactiveFormsModule, Password, CommonModule, Button, TranslatePipe, PasswordInputHintComponent],
templateUrl: './change-password.component.html',
styleUrl: './change-password.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush,
@@ -47,11 +48,7 @@ export class ChangePasswordComponent implements OnInit {
}),
[AccountSettingsPasswordFormControls.NewPassword]: new FormControl('', {
nonNullable: true,
- validators: [
- CustomValidators.requiredTrimmed(),
- Validators.minLength(8),
- Validators.pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*[\d!@#$%^&*])[A-Za-z\d!@#$%^&*_]{8,}$/),
- ],
+ validators: [CustomValidators.requiredTrimmed(), Validators.minLength(8), Validators.pattern(PASSWORD_REGEX)],
}),
[AccountSettingsPasswordFormControls.ConfirmPassword]: new FormControl('', {
nonNullable: true,
diff --git a/src/app/shared/components/password-input-hint/password-input-hint.component.html b/src/app/shared/components/password-input-hint/password-input-hint.component.html
index 028698b23..87e286390 100644
--- a/src/app/shared/components/password-input-hint/password-input-hint.component.html
+++ b/src/app/shared/components/password-input-hint/password-input-hint.component.html
@@ -1,3 +1,19 @@
-
- {{ 'auth.common.password.requirements' | translate }}
-
+@if (validationError) {
+
+ @switch (validationError) {
+ @case ('required') {
+ {{ 'auth.common.password.validation.required' | translate }}
+ }
+ @case ('minlength') {
+ {{ 'auth.common.password.validation.minlength' | translate }}
+ }
+ @case ('pattern') {
+ {{ 'auth.common.password.validation.pattern' | translate }}
+ }
+ }
+
+} @else {
+
+ {{ 'auth.common.password.requirements' | translate }}
+
+}
diff --git a/src/app/shared/components/password-input-hint/password-input-hint.component.scss b/src/app/shared/components/password-input-hint/password-input-hint.component.scss
index c3fced420..00ea80865 100644
--- a/src/app/shared/components/password-input-hint/password-input-hint.component.scss
+++ b/src/app/shared/components/password-input-hint/password-input-hint.component.scss
@@ -1,9 +1,7 @@
-:host {
- color: var(--primary-blue-1);
- font-weight: 600;
- font-size: 0.85rem;
+.text-danger {
+ color: var(--red-1);
+}
- .text-danger {
- color: var(--red-1);
- }
+.hint-text {
+ color: var(--pr-blue-1);
}
diff --git a/src/app/shared/components/password-input-hint/password-input-hint.component.ts b/src/app/shared/components/password-input-hint/password-input-hint.component.ts
index 14a2f865d..dab3cfe2f 100644
--- a/src/app/shared/components/password-input-hint/password-input-hint.component.ts
+++ b/src/app/shared/components/password-input-hint/password-input-hint.component.ts
@@ -1,16 +1,26 @@
import { TranslatePipe } from '@ngx-translate/core';
-import { ChangeDetectionStrategy, Component, input } from '@angular/core';
-
-import { BooleanOrNullOrUndefined } from '@osf/shared/helpers';
+import { CommonModule } from '@angular/common';
+import { Component, input } from '@angular/core';
+import { AbstractControl } from '@angular/forms';
@Component({
selector: 'osf-password-input-hint',
- imports: [TranslatePipe],
+ imports: [TranslatePipe, CommonModule],
templateUrl: './password-input-hint.component.html',
styleUrl: './password-input-hint.component.scss',
- changeDetection: ChangeDetectionStrategy.OnPush,
})
export class PasswordInputHintComponent {
- isError = input
(false);
+ control = input(null);
+
+ get validationError(): string | null {
+ const ctrl = this.control();
+ if (!ctrl || !ctrl.errors || !ctrl.touched) return null;
+
+ if (ctrl.errors['required']) return 'required';
+ if (ctrl.errors['minlength']) return 'minlength';
+ if (ctrl.errors['pattern']) return 'pattern';
+
+ return null;
+ }
}
diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json
index 4205675c1..89943c1b4 100644
--- a/src/assets/i18n/en.json
+++ b/src/assets/i18n/en.json
@@ -243,7 +243,12 @@
"new": "New Password",
"confirm": "Confirm New Password",
"mismatch": "Passwords must match.",
- "requirements": "Your password needs to be at least 8 characters long, include both lower- and upper-case characters, and have at least one number and special character"
+ "requirements": "Your password needs to be at least 8 characters long, include both lower- and upper-case characters, and have at least one number and special character",
+ "validation": {
+ "required": "Password is required.",
+ "minlength": "Password must be at least 8 characters long.",
+ "pattern": "Password must include lowercase, uppercase, and at least one number and special character."
+ }
}
},
"forgotPassword": {
@@ -1172,7 +1177,7 @@
"seeAllFiles": "See All Files in a Provider",
"seeAllFilesDescription": "All connected storage providers are displayed in the dropdown above the files list. Choose one provider to view contents.",
"selectFilesFolders": "Select Files/Folders",
- "selectFilesFoldersDescription": "Click on a row (outside of the file or folder name). Click on more rows to select multiple files. To select a range of files, click a file to begin selection and then shift+click another file to select the range. Select the ellipses on the right side of the row to see more file options.",
+ "selectFilesFoldersDescription": "Click on a row (outside of the file or folder name). Click on more rows to select multiple files. Select the ellipses on the right side of the row to see more file options.",
"openViewFiles": "Open/View Files and Folders",
"openViewFilesDescription": "Click a file name to go to view the file in the OSF. Opens file in a new tab. Click on the folder to open the contents in the files list.",
"upload": "Upload",
@@ -1902,14 +1907,10 @@
"newPassword": "New password",
"newPasswordPlaceholder": "Enter your new password",
"confirmPassword": "Confirm password",
- "confirmPasswordPlaceholder": "Confirm your new password",
- "passwordRequirements": "Your password needs to be at least 8 characters long, include both lower- and upper-case characters, and have at least one number or special character"
+ "confirmPasswordPlaceholder": "Confirm your new password"
},
"validation": {
"oldPasswordRequired": "Old password is required",
- "newPasswordRequired": "New password is required",
- "newPasswordMinLength": "Password must be at least 8 characters long.",
- "newPasswordPattern": "Password must include lowercase, uppercase, and at least one number or special character.",
"confirmPasswordRequired": "Please confirm your password",
"passwordsDoNotMatch": "Passwords do not match",
"sameAsOldPassword": "New password must be different from old password"