Skip to content

Commit

Permalink
Improve password input fields
Browse files Browse the repository at this point in the history
- add `show` style button to all input fields
- fixes #4219
  • Loading branch information
richard-cox committed Oct 22, 2020
1 parent ce4c108 commit 1d85f61
Show file tree
Hide file tree
Showing 15 changed files with 131 additions and 44 deletions.
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { CanActivate } from '@angular/router';
import { Store } from '@ngrx/store';
import { Observable, of as observableOf } from 'rxjs';
import { map, catchError, tap } from 'rxjs/operators';
import { catchError, map, tap } from 'rxjs/operators';

import { RouterNav } from '../../../store/src/actions/router.actions';
import { AppState } from '../../../store/src/app-state';
import { HttpClient } from '@angular/common/http';
import { environment } from '../environments/environment';

const { proxyAPIVersion } = environment;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
mat-checkbox {
margin-left: 15px;
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,32 @@ <h1>Backup Endpoints</h1>
<form [formGroup]="passwordForm" class="stepper-form">
<mat-form-field>
<mat-label>Password</mat-label>
<input matInput formControlName="password" name="password" required [type]="!show ? 'password' : 'text'">
<button mat-icon-button matSuffix (click)="show = !show" [attr.aria-label]="'Hide password'"
[attr.aria-pressed]="!show">
<mat-icon>{{!show ? 'visibility_off' : 'visibility'}}</mat-icon>
<input matInput formControlName="password" name="password" required
[type]="!showPassword[1] ? 'password' : 'text'">
<button mat-icon-button matSuffix (click)="showPassword[1] = !showPassword[1]"
[attr.aria-label]="'Hide password'" [attr.aria-pressed]="!showPassword[1]">
<mat-icon>{{!showPassword[1] ? 'visibility_off' : 'visibility'}}</mat-icon>
</button>
<mat-error *ngIf="passwordForm.controls.password.errors && passwordForm.controls.password.errors.required">
Password is required</mat-error>
<mat-error *ngIf="passwordForm.controls.password.errors && passwordForm.controls.password.errors.minlength">
Password must be at least {{passwordForm.controls.password.errors.minlength.requiredLength}} characters
</mat-error>
</mat-form-field>
<mat-form-field>
<mat-label>Confirm Password</mat-label>
<input matInput formControlName="password2" name="password2" required
[type]="!showPassword[2] ? 'password' : 'text'" [pattern]="passwordForm.controls.password2.value">
<button mat-icon-button matSuffix (click)="showPassword[2] = !showPassword[2]"
[attr.aria-label]="'Hide password'" [attr.aria-pressed]="!showPassword[2]">
<mat-icon>{{!showPassword[2] ? 'visibility_off' : 'visibility'}}</mat-icon>
</button>
<mat-error *ngIf="passwordForm.controls.password2.errors && passwordForm.controls.password2.errors.required">
Password is required</mat-error>
<mat-error *ngIf="passwordForm.controls.password2.errors && passwordForm.controls.password2.errors.pattern">
Passwords must match
</mat-error>
</mat-form-field>
</form>
</div>
</app-step>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { Component } from '@angular/core';
import { Component, OnDestroy } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import moment from 'moment';
import { Observable, of, Subject } from 'rxjs';
import { Observable, of, Subject, Subscription } from 'rxjs';
import { filter, first, map } from 'rxjs/operators';

import { entityCatalog } from '../../../../../../store/src/entity-catalog/entity-catalog';
import { httpErrorResponseToSafeString } from '../../../../../../store/src/jetstream';
import { stratosEntityCatalog } from '../../../../../../store/src/stratos-entity-catalog';
import { EndpointModel } from '../../../../../../store/src/types/endpoint.types';
import { safeUnsubscribe } from '../../../../core/utils.service';
import { ConfirmationDialogConfig } from '../../../../shared/components/confirmation-dialog.config';
import { ConfirmationDialogService } from '../../../../shared/components/confirmation-dialog.service';
import { ITableListDataSource } from '../../../../shared/components/list/data-sources-controllers/list-data-source-types';
Expand All @@ -26,7 +27,9 @@ import { BackupEndpointTypes } from '../backup-restore.types';
BackupEndpointsService
]
})
export class BackupEndpointsComponent {
export class BackupEndpointsComponent implements OnDestroy {

sub: Subscription;

// Step 1
columns: ITableColumn<EndpointModel>[] = [
Expand Down Expand Up @@ -66,7 +69,7 @@ export class BackupEndpointsComponent {
// Step 2
passwordValid$: Observable<boolean>;
passwordForm: FormGroup;
show = false;
showPassword: boolean[] = [];

constructor(
public service: BackupEndpointsService,
Expand All @@ -76,6 +79,9 @@ export class BackupEndpointsComponent {
this.setupPasswordStep();
}

ngOnDestroy(): void {
safeUnsubscribe(this.sub);
}

setupSelectStep() {
const endpointObs = stratosEntityCatalog.endpoint.store.getAll.getPaginationService();
Expand Down Expand Up @@ -105,7 +111,11 @@ export class BackupEndpointsComponent {
setupPasswordStep() {
this.passwordForm = new FormGroup({
password: new FormControl('', [Validators.required, Validators.minLength(6)]),
password2: new FormControl(''),
});
this.sub = this.passwordForm.controls.password.valueChanges.subscribe(value => this.passwordForm.controls.password2.setValidators(
[Validators.required, Validators.pattern(value)]
));
this.passwordValid$ = this.passwordForm.statusChanges.pipe(
map(() => {
this.service.password = this.passwordForm.controls.password.value;
Expand Down Expand Up @@ -162,7 +172,7 @@ export class BackupEndpointsComponent {
}

return result.asObservable();
}
};


private getEndpointTypeString(endpoint: EndpointModel): string {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@
<input matInput placeholder="Username" formControlName="username">
</mat-form-field>
<mat-form-field>
<input matInput placeholder="Password" type="password" formControlName="password">
<input matInput formControlName="password" name="password" required [type]="!showPassword ? 'password' : 'text'">
<button mat-icon-button matSuffix (click)="showPassword = !showPassword" [attr.aria-label]="'Hide password'"
[attr.aria-pressed]="!showPassword" type="button">
<mat-icon>{{!showPassword ? 'visibility_off' : 'visibility'}}</mat-icon>
</button>
</mat-form-field>
</div>
</div>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,8 @@ import { IAuthForm } from '../../../../../../store/src/extension-types';
styleUrls: ['./credentials-auth-form.component.scss']
})
export class CredentialsAuthFormComponent implements IAuthForm {

showPassword = false;

@Input() formGroup: FormGroup;
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,24 @@
<input matInput required [(ngModel)]="username" name="username" placeholder="Username">
</mat-form-field>
<mat-form-field *ngIf="!ssoLogin" [hideRequiredMarker]="true">
<input matInput required type="password" [(ngModel)]="password" name="password" placeholder="Password">
<input matInput required [type]="!showPassword ? 'password' : 'text'" [(ngModel)]="password" name="password"
placeholder="Password">
<button mat-icon-button matSuffix (click)="showPassword = !showPassword" [attr.aria-label]="'Hide password'"
[attr.aria-pressed]="!showPassword" type="button">
<mat-icon>{{!showpassword ? 'visibility_off' : 'visibility'}}</mat-icon>
</button>
</mat-form-field>
<button class="login__submit" color="primary" *ngIf="!loggedIn" type="submit" mat-button mat-raised-button [disabled]="!ssoLogin && !loginForm.valid">Login</button>
<button class="login__submit" color="primary" *ngIf="!loggedIn" type="submit" mat-button mat-raised-button
[disabled]="!ssoLogin && !loginForm.valid">Login</button>
</form>
</div>
<div id="login__loading" class="login__loading">
<mat-progress-bar mode="indeterminate"></mat-progress-bar>
</div>
</div>
</mat-card>
<div id="login-error-message" class="login-message" [ngClass]="{'login-message--show': !!message, 'login-message-error': this.error}">
<div id="login-error-message" class="login-message"
[ngClass]="{'login-message--show': !!message, 'login-message-error': this.error}">
{{ message }}
</div>
</app-intro-screen>
</app-intro-screen>
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ export class LoginPageComponent implements OnInit, OnDestroy {

subscription: Subscription;

showPassword = false;

ngOnInit() {
this.ssoLogin = false;
this.store.dispatch(new VerifySession());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,20 @@ <h1>{{title}} Setup with Local Admin Account</h1>
<form class="local-setup-wizard__form" [formGroup]="passwordForm" class="stepper-form">
<div class="local-setup-wizard__form-block">
<mat-form-field>
<input matInput formControlName="adminPassword" placeholder="Password" type="password">
<input matInput formControlName="adminPassword" placeholder="Password" name="password" required
[type]="!showPassword[1] ? 'password' : 'text'">
<button mat-icon-button matSuffix (click)="showPassword[1] = !showPassword[1]"
[attr.aria-label]="'Hide password'" [attr.aria-pressed]="!showPassword[1]">
<mat-icon>{{!showPassword[1] ? 'visibility_off' : 'visibility'}}</mat-icon>
</button>
</mat-form-field>
<mat-form-field>
<input matInput formControlName="adminPasswordConfirm" placeholder="Confirm Password" type="password">
<input matInput formControlName="adminPasswordConfirm" placeholder="Confirm Password"
name="adminPasswordConfirm" required [type]="!showPassword[2] ? 'password' : 'text'">
<button mat-icon-button matSuffix (click)="showPassword[2] = !showPassword[2]"
[attr.aria-label]="'Hide password'" [attr.aria-pressed]="!showPassword[2]">
<mat-icon>{{!showPassword[2] ? 'visibility_off' : 'visibility'}}</mat-icon>
</button>
</mat-form-field>
</div>
</form>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import { Component, OnInit, Inject } from '@angular/core';
import { FormGroup, FormControl, Validators, ValidatorFn, AbstractControl } from '@angular/forms';
import { Observable, BehaviorSubject, of as obsof } from 'rxjs';
import { StepOnNextFunction } from '../../../shared/components/stepper/step/step.component';
import { Component, Inject, OnInit } from '@angular/core';
import { AbstractControl, FormControl, FormGroup, ValidatorFn, Validators } from '@angular/forms';
import { Store } from '@ngrx/store';
import { InternalAppState } from '../../../../../store/src/app-state';
import { filter, delay, take, map, tap } from 'rxjs/operators';
import { UAASetupState, LocalAdminSetupData } from '../../../../../store/src/types/uaa-setup.types';
import { AuthState } from '../../../../../store/src/reducers/auth.reducer';
import { BehaviorSubject, Observable } from 'rxjs';
import { delay, filter, map, take, tap } from 'rxjs/operators';

import { VerifySession } from '../../../../../store/src/actions/auth.actions';
import { SetupSaveConfig } from '../../../../../store/src/actions/setup.actions';
import { InternalAppState } from '../../../../../store/src/app-state';
import { AuthState } from '../../../../../store/src/reducers/auth.reducer';
import { LocalAdminSetupData, UAASetupState } from '../../../../../store/src/types/uaa-setup.types';
import { APP_TITLE } from '../../../core/core.types';
import { StepOnNextFunction } from '../../../shared/components/stepper/step/step.component';

@Component({
selector: 'app-local-account-wizard',
Expand All @@ -22,6 +23,8 @@ export class LocalAccountWizardComponent implements OnInit {
validateLocalAuthForm: Observable<boolean>;
applyingSetup$ = new BehaviorSubject<boolean>(false);

showPassword: boolean[] = [];

constructor(private store: Store<Pick<InternalAppState, 'uaaSetup' | 'auth'>>, @Inject(APP_TITLE) public title: string) { }

ngOnInit() {
Expand Down Expand Up @@ -72,10 +75,10 @@ export class LocalAccountWizardComponent implements OnInit {
message: state[0].message
};
}));
}
};

confirmPasswordValidator(): ValidatorFn {
return (control: AbstractControl): { [key: string]: any } => {
return (control: AbstractControl): { [key: string]: any, } => {
const same = control.value === this.passwordForm.value.adminPassword;
return same ? null : { passwordMatch: { value: control.value } };
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
<h1>Edit User Profile</h1>
<div class="page-header-right">
<button mat-icon-button mat-button routerLink="/user-profile">
<mat-icon>clear</mat-icon>
</button>
<mat-icon>clear</mat-icon>
</button>
</div>
</app-page-header>
<app-steppers cancel="/user-profile">
<app-step [title]="'Edit User Profile'" [valid]="editProfileForm.valid && editProfileForm.dirty" [onNext]="updateProfile" finishButtonText="Save">
<app-step [title]="'Edit User Profile'" [valid]="editProfileForm.valid && editProfileForm.dirty"
[onNext]="updateProfile" finishButtonText="Save">
<div>
<form [formGroup]="editProfileForm" validate class="edit-profile edit-profile__group">
<mat-form-field>
Expand All @@ -19,24 +20,46 @@ <h1>Edit User Profile</h1>
<mat-form-field>
<input matInput placeholder="Primary Email Address" formControlName="emailAddress">
</mat-form-field>
<p *ngIf="!(canChangePassword | async) && needsPasswordForEmailChange">Current password is required when changing email address</p>
<p *ngIf="(canChangePassword | async) && needsPasswordForEmailChange">Current password is required when changing email address or password</p>
<p *ngIf="!(canChangePassword | async) && needsPasswordForEmailChange">Current password is required when
changing email address</p>
<p *ngIf="(canChangePassword | async) && needsPasswordForEmailChange">Current password is required when changing
email address or password</p>
<mat-form-field *ngIf="needsPasswordForEmailChange">
<input matInput placeholder="Current Password" type="password" formControlName="currentPassword" [required]="passwordRequired">
<input matInput placeholder="Current Password" [type]="!showPassword[1] ? 'password' : 'text'"
formControlName="currentPassword" [required]="passwordRequired">
<button mat-icon-button matSuffix (click)="showPassword[1] = !showPassword[1]"
[attr.aria-label]="'Hide password'" [attr.aria-pressed]="!showPassword[1]">
<mat-icon>{{!showPassword[1] ? 'visibility_off' : 'visibility'}}</mat-icon>
</button>
</mat-form-field>
<div *ngIf="(canChangePassword | async)" class="edit-profile__group">
<p>Change Password (Leave blank to keep current password)</p>
<mat-form-field *ngIf="!needsPasswordForEmailChange">
<input matInput placeholder="Current Password" type="password" formControlName="currentPassword" [required]="passwordRequired">
<input matInput placeholder="Current Password" [type]="!showPassword[2] ? 'password' : 'text'"
formControlName="currentPassword" [required]="passwordRequired">
<button mat-icon-button matSuffix (click)="showPassword[2] = !showPassword[2]"
[attr.aria-label]="'Hide password'" [attr.aria-pressed]="!showPassword[2]">
<mat-icon>{{!showPassword[2] ? 'visibility_off' : 'visibility'}}</mat-icon>
</button>
</mat-form-field>
<mat-form-field>
<input matInput placeholder="New Password" type="password" formControlName="newPassword">
<input matInput placeholder="New Password" [type]="!showPassword[3] ? 'password' : 'text'"
formControlName="newPassword">
<button mat-icon-button matSuffix (click)="showPassword[3] = !showPassword[3]"
[attr.aria-label]="'Hide password'" [attr.aria-pressed]="!showPassword[3]">
<mat-icon>{{!showPassword[3] ? 'visibility_off' : 'visibility'}}</mat-icon>
</button>
</mat-form-field>
<mat-form-field>
<input matInput placeholder="Confirm New Password" type="password" formControlName="confirmPassword">
<input matInput placeholder="Confirm New Password" [type]="!showPassword[4] ? 'password' : 'text'"
formControlName="confirmPassword">
<button mat-icon-button matSuffix (click)="showPassword[4] = !showPassword[4]"
[attr.aria-label]="'Hide password'" [attr.aria-pressed]="!showPassword[4]">
<mat-icon>{{!showPassword[4] ? 'visibility_off' : 'visibility'}}</mat-icon>
</button>
</mat-form-field>
</div>
</form>
</div>
</app-step>
</app-steppers>
</app-steppers>
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { StepOnNextFunction } from '../../../shared/components/stepper/step/step
export class EditProfileInfoComponent implements OnInit, OnDestroy {

editProfileForm: FormGroup;
showPassword: boolean[] = [];

needsPasswordForEmailChange: boolean;

Expand Down Expand Up @@ -104,7 +105,7 @@ export class EditProfileInfoComponent implements OnInit, OnDestroy {
}

confirmPasswordValidator(): ValidatorFn {
return (control: AbstractControl): { [key: string]: any } => {
return (control: AbstractControl): { [key: string]: any, } => {
const same = control.value === this.editProfileForm.value.newPassword;
return same ? null : { passwordMatch: { value: control.value } };
};
Expand Down Expand Up @@ -133,5 +134,5 @@ export class EditProfileInfoComponent implements OnInit, OnDestroy {
delay(300), // Ensure that the profile is updated before fetching to refresh local copy
tap(() => this.userProfileService.fetchUserProfile())
);
}
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export class UniqueDirective implements Validator {

@Input() appUnique: any[];

validate(c: AbstractControl): { [key: string]: any; } {
validate(c: AbstractControl): { [key: string]: any, } {
const found = this.appUnique ? this.appUnique.indexOf(c.value) >= 0 : false;
return found ? {
appUnique: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@
<input matInput placeholder="Access Key ID" formControlName="access_key">
</mat-form-field>
<mat-form-field>
<input matInput placeholder="Secret Access Key" type="password" formControlName="secret_key">
<input matInput placeholder="Secret Access Key" [type]="!showPassword ? 'password' : 'text'"
formControlName="secret_key">
<button mat-icon-button matSuffix (click)="showPassword = !showPassword" [attr.aria-label]="'Hide password'"
[attr.aria-pressed]="!showPassword">
<mat-icon>{{!showPassword ? 'visibility_off' : 'visibility'}}</mat-icon>
</button>
</mat-form-field>
</div>
</div>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ import { IAuthForm } from '../../../../../store/src/extension-types';
styleUrls: ['./kubernetes-aws-auth-form.component.scss']
})
export class KubernetesAWSAuthFormComponent implements IAuthForm {
showPassword = false;
@Input() formGroup: FormGroup;
}

0 comments on commit 1d85f61

Please sign in to comment.