fix(reset-password): implement password visibility toggle for input fields
This commit is contained in:
@@ -13,11 +13,13 @@
|
|||||||
<label class="visually-hidden" for="reset-password-new">Nueva Contraseña</label>
|
<label class="visually-hidden" for="reset-password-new">Nueva Contraseña</label>
|
||||||
<app-input
|
<app-input
|
||||||
id="reset-password-new"
|
id="reset-password-new"
|
||||||
type="password"
|
type="password-toggle"
|
||||||
placeholder="Nueva Contraseña"
|
placeholder="Nueva Contraseña"
|
||||||
[value]="form.controls.password.value"
|
[value]="form.controls.password.value"
|
||||||
[invalid]="showControlError('password')"
|
[invalid]="showControlError('password')"
|
||||||
|
[visible]="passwordVisible()"
|
||||||
(valueChange)="updatePassword('password', $event)"
|
(valueChange)="updatePassword('password', $event)"
|
||||||
|
(visibleChange)="setPasswordVisibility($event)"
|
||||||
/>
|
/>
|
||||||
@if (getControlError('password'); as errorMessage) {
|
@if (getControlError('password'); as errorMessage) {
|
||||||
<small class="text-danger">{{ errorMessage }}</small>
|
<small class="text-danger">{{ errorMessage }}</small>
|
||||||
@@ -30,11 +32,13 @@
|
|||||||
</label>
|
</label>
|
||||||
<app-input
|
<app-input
|
||||||
id="reset-password-confirmation"
|
id="reset-password-confirmation"
|
||||||
type="password"
|
type="password-toggle"
|
||||||
placeholder="Repetir Nueva Contraseña"
|
placeholder="Repetir Nueva Contraseña"
|
||||||
[value]="form.controls.password_confirmation.value"
|
[value]="form.controls.password_confirmation.value"
|
||||||
[invalid]="showControlError('password_confirmation')"
|
[invalid]="showControlError('password_confirmation')"
|
||||||
|
[visible]="passwordVisible()"
|
||||||
(valueChange)="updatePassword('password_confirmation', $event)"
|
(valueChange)="updatePassword('password_confirmation', $event)"
|
||||||
|
(visibleChange)="setPasswordVisibility($event)"
|
||||||
/>
|
/>
|
||||||
@if (getControlError('password_confirmation'); as errorMessage) {
|
@if (getControlError('password_confirmation'); as errorMessage) {
|
||||||
<small class="text-danger">{{ errorMessage }}</small>
|
<small class="text-danger">{{ errorMessage }}</small>
|
||||||
|
|||||||
@@ -50,6 +50,43 @@ describe('ResetPasswordPageComponent', () => {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
it('shows and hides both password fields with either visibility control', async () => {
|
||||||
|
const modalService = {
|
||||||
|
openSimple: vi.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [ResetPasswordPageComponent],
|
||||||
|
providers: resetProviders(modalService),
|
||||||
|
}).compileComponents();
|
||||||
|
|
||||||
|
const fixture = TestBed.createComponent(ResetPasswordPageComponent);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const getPasswordInputs = () =>
|
||||||
|
Array.from(fixture.nativeElement.querySelectorAll('input')) as HTMLInputElement[];
|
||||||
|
const getVisibilityButtons = () =>
|
||||||
|
Array.from(
|
||||||
|
fixture.nativeElement.querySelectorAll('button[aria-label]'),
|
||||||
|
) as HTMLButtonElement[];
|
||||||
|
|
||||||
|
expect(getPasswordInputs().map((input) => input.type)).toEqual(['password', 'password']);
|
||||||
|
|
||||||
|
getVisibilityButtons()[0].click();
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(getPasswordInputs().map((input) => input.type)).toEqual(['text', 'text']);
|
||||||
|
expect(getVisibilityButtons().map((button) => button.getAttribute('aria-label'))).toEqual([
|
||||||
|
'Ocultar contraseña',
|
||||||
|
'Ocultar contraseña',
|
||||||
|
]);
|
||||||
|
|
||||||
|
getVisibilityButtons()[1].click();
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(getPasswordInputs().map((input) => input.type)).toEqual(['password', 'password']);
|
||||||
|
});
|
||||||
|
|
||||||
it('rejects passwords that do not match', async () => {
|
it('rejects passwords that do not match', async () => {
|
||||||
const modalService = {
|
const modalService = {
|
||||||
openSimple: vi.fn(),
|
openSimple: vi.fn(),
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ export class ResetPasswordPageComponent {
|
|||||||
private readonly submittedState = signal(false);
|
private readonly submittedState = signal(false);
|
||||||
private readonly isSubmittingState = signal(false);
|
private readonly isSubmittingState = signal(false);
|
||||||
private readonly serverErrorState = signal<string | null>(null);
|
private readonly serverErrorState = signal<string | null>(null);
|
||||||
|
private readonly passwordVisibleState = signal(false);
|
||||||
|
|
||||||
private readonly email = this.route.snapshot.queryParamMap.get('email') ?? '';
|
private readonly email = this.route.snapshot.queryParamMap.get('email') ?? '';
|
||||||
private readonly code = this.route.snapshot.queryParamMap.get('code') ?? '';
|
private readonly code = this.route.snapshot.queryParamMap.get('code') ?? '';
|
||||||
@@ -72,6 +73,11 @@ export class ResetPasswordPageComponent {
|
|||||||
protected readonly submitted = this.submittedState.asReadonly();
|
protected readonly submitted = this.submittedState.asReadonly();
|
||||||
protected readonly isSubmitting = this.isSubmittingState.asReadonly();
|
protected readonly isSubmitting = this.isSubmittingState.asReadonly();
|
||||||
protected readonly serverError = this.serverErrorState.asReadonly();
|
protected readonly serverError = this.serverErrorState.asReadonly();
|
||||||
|
protected readonly passwordVisible = this.passwordVisibleState.asReadonly();
|
||||||
|
|
||||||
|
protected setPasswordVisibility(visible: boolean): void {
|
||||||
|
this.passwordVisibleState.set(visible);
|
||||||
|
}
|
||||||
|
|
||||||
protected updatePassword(controlName: PasswordControlName, value: string | number): void {
|
protected updatePassword(controlName: PasswordControlName, value: string | number): void {
|
||||||
this.form.controls[controlName].setValue(String(value));
|
this.form.controls[controlName].setValue(String(value));
|
||||||
|
|||||||
Reference in New Issue
Block a user