ACA-4600-adding-input-validations-for-edit-profile (#2601)

* ACA-4600 Added input field validations for edit profile page
This commit is contained in:
Shubham Bansal 2022-09-12 15:06:50 +00:00 committed by GitHub
parent 49e48abf3a
commit 87638c8811
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 237 additions and 97 deletions

View File

@ -1,26 +1,26 @@
<div class="app-profile-container">
<div class="app-profile-row">
<div class="app-profile-title">
<mat-icon class="app-profile-icon" (click)="navigateToPersonalFiles()">arrow_back</mat-icon>
<mat-icon class="app-profile-icon" (click)="navigateToPersonalFiles()" id="backButton">arrow_back</mat-icon>
<h3 class="app-profile">{{'APP.EDIT_PROFILE.MY_PROFILE' | translate}}</h3>
</div>
<mat-divider class="app-mat-divider"></mat-divider>
</div>
<div class="app-profile-general-row" [formGroup]="profileForm">
<div class="app-profile-general">
<div class="app-profile-general-div">
<mat-icon class="app-profile-general-icon" (click)="toggleGeneralDropdown()">
<div class="app-profile-general-section">
<mat-icon class="app-profile-general-icon" (click)="toggleGeneralDropdown()" id="toggle-general-dropdown">
{{ generalSectionDropdown ? 'expand_more' : 'chevron_right'}}</mat-icon>
<h4 class="app-general-title">{{'APP.EDIT_PROFILE.GENERAL' | translate}}</h4>
</div>
<div class="app-general-edit-btn" *ngIf="generalSectionButtonsToggle">
<button mat-raised-button (click)="toggleGeneralButtons()"
<button mat-raised-button (click)="toggleGeneralButtons()" id="general-edit-button"
class="app-general-edit">{{'APP.EDIT_PROFILE.EDIT' | translate}}</button>
</div>
<div class="app-general-edit-btn" *ngIf="!generalSectionButtonsToggle">
<button mat-raised-button class="app-general-cancel-btn"
<button mat-raised-button class="app-general-cancel-btn" id="general-cancel-button"
(click)="toggleGeneralButtons()">{{'APP.EDIT_PROFILE.CANCEL' | translate}}</button>
<button mat-raised-button class="app-general-save-btn"
<button mat-raised-button class="app-general-save-btn" id="general-save-button" [disabled]="isSaveButtonDisabled()"
(click)="onSaveGeneralData(profileForm)">{{'APP.EDIT_PROFILE.SAVE' | translate}}</button>
</div>
</div>
@ -56,6 +56,9 @@
<p class="app-profile-general-dropdown-details" *ngIf="generalSectionButtonsToggle">{{personDetails?.telephone}}</p>
<input type="tel" name="Telephone" value="" formControlName="telephone" *ngIf="!generalSectionButtonsToggle"
class="app-profile-general-dropdown-input-details app-selected">
<mat-error class="app-error-message" *ngIf="profileForm.get('telephone').invalid">
{{ 'APP.EDIT_PROFILE.INVALID_INPUT' | translate }}
</mat-error>
</div>
<mat-divider class="app-general-dropdown-divider"></mat-divider>
<div class="app-general-dropdown-details">
@ -63,62 +66,9 @@
<p class="app-profile-general-dropdown-details" *ngIf="generalSectionButtonsToggle">{{personDetails?.mobile}}</p>
<input type="tel" value="" formControlName="mobile" *ngIf="!generalSectionButtonsToggle"
class="app-profile-general-dropdown-input-details app-selected">
</div>
</div>
</div>
</div>
<div class="app-profile-login-row" [formGroup]="profileForm">
<div class="app-profile-login" >
<div class="app-profile-general-div">
<mat-icon class="app-profile-general-icon" (click)="toggleLoginDropdown()">
{{ loginSectionDropdown ? 'expand_more' : 'chevron_right'}}</mat-icon>
<h4 class="app-general-title">{{'APP.EDIT_PROFILE.LOGIN' | translate}}</h4>
</div>
<div class="app-general-edit-btn" *ngIf="loginSectionButtonsToggle">
<button mat-raised-button (click)="toggleLoginButtons()"
class="app-general-edit">{{'APP.EDIT_PROFILE.EDIT' | translate}}</button>
</div>
<div class="app-general-edit-btn" *ngIf="!loginSectionButtonsToggle">
<button mat-raised-button class="app-general-cancel-btn"
(click)="toggleLoginButtons()">{{'APP.EDIT_PROFILE.CANCEL' | translate}}</button>
<button mat-raised-button class="app-general-save-btn"
(click)="onSaveLoginData()">{{'APP.EDIT_PROFILE.SAVE' | translate}}</button>
</div>
</div>
<mat-divider class="app-mat-divider" *ngIf="loginSectionDropdown"></mat-divider>
<div class="app-profile-login-dropdown" *ngIf="loginSectionDropdown">
<div class="app-general-dropdown" >
<div class="app-general-dropdown-details">
<h4 class="app-profile-general-dropdown-heading">{{'APP.EDIT_PROFILE.USERNAME' | translate}}</h4>
<p class="app-profile-general-dropdown-details">{{personDetails?.id}}</p>
</div>
<mat-divider class="app-general-dropdown-divider"></mat-divider>
<div class="app-general-dropdown-details">
<h4 class="app-profile-general-dropdown-heading">{{'APP.EDIT_PROFILE.EMAIL' | translate}}</h4>
<p class="app-profile-general-dropdown-details">{{personDetails?.email}}</p>
</div>
<mat-divider class="app-general-dropdown-divider" *ngIf="!passwordSectionDropdown"></mat-divider>
<div class="app-general-dropdown-details" *ngIf="!passwordSectionDropdown">
<h4 class="app-profile-general-dropdown-heading">{{'APP.EDIT_PROFILE.PASSWORD' | translate}}</h4>
<p class="app-profile-general-dropdown-details"></p>
</div>
<mat-divider class="app-general-dropdown-divider" *ngIf="passwordSectionDropdown"></mat-divider>
<div class="app-general-dropdown-details" *ngIf="passwordSectionDropdown">
<h4 class="app-profile-general-dropdown-heading">{{'APP.EDIT_PROFILE.OLD_PASSWORD' | translate}}</h4>
<input class="app-profile-login-dropdown-details app-selected" formControlName="oldPassword" type="password">
<h4 class="app-profile-login-dropdown-heading-forgot">{{'APP.EDIT_PROFILE.FORGOT_PASSWORD' | translate}}</h4>
</div>
<mat-divider class="app-general-dropdown-divider" *ngIf="passwordSectionDropdown"></mat-divider>
<div class="app-general-dropdown-details" *ngIf="passwordSectionDropdown">
<h4 class="app-profile-general-dropdown-heading">{{'APP.EDIT_PROFILE.NEW_PASSWORD' | translate}}</h4>
<input class="app-profile-login-dropdown-details app-selected" type="password" formControlName="newPassword">
</div>
<mat-divider class="app-general-dropdown-divider" *ngIf="passwordSectionDropdown"></mat-divider>
<div class="app-general-dropdown-details" *ngIf="passwordSectionDropdown">
<h4 class="app-profile-general-dropdown-heading">{{'APP.EDIT_PROFILE.VERIFY_PASSWORD' | translate}}</h4>
<input class="app-profile-login-dropdown-details app-selected" type="password" formControlName="verifyPassword">
<mat-error class="app-error-message" *ngIf="profileForm.get('mobile').invalid">
{{ 'APP.EDIT_PROFILE.INVALID_INPUT' | translate }}
</mat-error>
</div>
</div>
</div>
@ -126,20 +76,20 @@
<div class="app-profile-contact-row" [formGroup]="profileForm">
<div class="app-profile-general profile-general-bottom-radius">
<div class="app-profile-general-div">
<mat-icon class="app-profile-general-icon" (click)="toggleContactDropdown()">
<div class="app-profile-general-section">
<mat-icon class="app-profile-general-icon" (click)="toggleContactDropdown()" id="toggle-contact-dropdown">
{{ contactSectionDropdown ? 'expand_more' : 'chevron_right'}}</mat-icon>
<h4 class="app-general-title">{{'APP.EDIT_PROFILE.COMPANY_DETAILS' | translate}}</h4>
</div>
<div class="app-general-edit-btn" *ngIf="contactSectionButtonsToggle">
<button mat-raised-button class="app-general-edit"
<button mat-raised-button class="app-general-edit" id="contact-edit-button"
(click)="toggleContactButtons()">{{'APP.EDIT_PROFILE.EDIT' | translate}}</button>
</div>
<div class="app-general-edit-btn" *ngIf="!contactSectionButtonsToggle">
<button mat-raised-button class="app-general-cancel-btn"
<button mat-raised-button class="app-general-cancel-btn" id="contact-cancel-button"
(click)="toggleContactButtons()">{{'APP.EDIT_PROFILE.CANCEL' | translate}}</button>
<button mat-raised-button class="app-general-save-btn"
<button mat-raised-button class="app-general-save-btn" id="contact-save-button" [disabled]="isSaveButtonDisabled()"
(click)="onSaveCompanyData(profileForm)">{{'APP.EDIT_PROFILE.SAVE' | translate}}</button>
</div>
</div>
@ -148,7 +98,9 @@
<div class="app-general-dropdown">
<div class="app-general-dropdown-details">
<h4 class="app-profile-general-dropdown-heading">{{'APP.EDIT_PROFILE.NAME' | translate}}</h4>
<p class="app-profile-general-dropdown-details">{{personDetails?.company?.organization}}</p>
<p class="app-profile-general-dropdown-details" *ngIf="contactSectionButtonsToggle">{{personDetails?.company?.organization}}</p>
<input type="text" value="" *ngIf="!contactSectionButtonsToggle" formControlName="companyName"
class="app-profile-general-dropdown-input-details app-selected">
</div>
<mat-divider class="app-general-dropdown-divider"></mat-divider>
<div class="app-general-dropdown-details">
@ -159,11 +111,10 @@
</div>
<mat-divider class="app-general-dropdown-divider"></mat-divider>
<div class="app-general-dropdown-details">
<h4 class="app-profile-general-dropdown-heading">{{'APP.EDIT_PROFILE.POST_CODE' | translate}}</h4>
<h4 class="app-profile-general-dropdown-heading">{{'APP.EDIT_PROFILE.POSTCODE' | translate}}</h4>
<p class="app-profile-general-dropdown-details" *ngIf="contactSectionButtonsToggle">{{personDetails?.company?.postcode}}</p>
<input type="text" value="" *ngIf="!contactSectionButtonsToggle" formControlName="companyPostCode"
class="app-profile-general-dropdown-input-details app-selected">
</div>
<mat-divider class="app-general-dropdown-divider"></mat-divider>
<div class="app-general-dropdown-details">
@ -171,7 +122,9 @@
<p class="app-profile-general-dropdown-details" *ngIf="contactSectionButtonsToggle">{{personDetails?.company?.telephone}}</p>
<input type="tel" value="" *ngIf="!contactSectionButtonsToggle" formControlName="companyTelephone"
class="app-profile-general-dropdown-input-details app-selected">
<mat-error class="app-error-message" *ngIf="profileForm.get('companyTelephone').invalid">
{{ 'APP.EDIT_PROFILE.INVALID_INPUT' | translate }}
</mat-error>
</div>
<mat-divider class="app-general-dropdown-divider"></mat-divider>
<div class="app-general-dropdown-details">
@ -179,6 +132,9 @@
<p class="app-profile-general-dropdown-details" *ngIf="contactSectionButtonsToggle">{{personDetails?.company?.email}}</p>
<input type="text" value="" *ngIf="!contactSectionButtonsToggle" formControlName="companyEmail"
class="app-profile-general-dropdown-input-details app-selected">
<mat-error class="app-error-message" *ngIf="profileForm.get('companyEmail').invalid">
{{ 'APP.EDIT_PROFILE.INVALID_INPUT' | translate }}
</mat-error>
</div>
</div>
</div>

View File

@ -52,7 +52,7 @@ app-view-profile {
border-bottom-right-radius: 0;
}
.app-profile-general-div {
.app-profile-general-section {
display: flex;
width: 60%;
padding-top: 1rem;
@ -92,6 +92,8 @@ app-view-profile {
.app-general-edit {
margin: 0.7rem;
background: var(--theme-grey-text-background-color);
height: 30px;
margin: 1rem;
}
.app-general-edit:hover {
@ -99,12 +101,18 @@ app-view-profile {
}
.app-general-cancel-btn {
margin: 0.7rem;
height: 30px;
margin: 1rem;
margin-left: .5rem;
background-color: var(--theme-grey-text-background-color);
padding-top: .25px;
}
.app-general-save-btn {
margin: 0.7rem;
width: 75px;
height: 30px;
margin: 1rem;
margin-left: .5rem;
color: white;
background-color: var(--theme-blue-button-color);
}
@ -177,4 +185,9 @@ app-view-profile {
border: 1px solid var(--theme-grey-background-color);
border-radius: 1rem;
}
.app-error-message {
padding-top: 2rem;
padding-left: .5rem;
}
}

View File

@ -0,0 +1,165 @@
/*
* Copyright © 2005 - 2021 Alfresco Software, Ltd. All rights reserved.
*
* License rights for this program may be obtained from Alfresco Software, Ltd.
* pursuant to a written agreement and any use of this program without such an
* agreement is prohibited.
*/
import { TestBed, ComponentFixture } from '@angular/core/testing';
import { AppConfigModule } from '@alfresco/adf-core';
import { ViewProfileComponent } from './view-profile.component';
import { AppTestingModule } from '../../testing/app-testing.module';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { By } from '@angular/platform-browser';
import { Router } from '@angular/router';
describe('ViewProfileComponent', () => {
let fixture: ComponentFixture<ViewProfileComponent>;
let component: ViewProfileComponent;
let router: Router;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [AppTestingModule, AppConfigModule, FormsModule, ReactiveFormsModule],
declarations: [ViewProfileComponent]
});
fixture = TestBed.createComponent(ViewProfileComponent);
component = fixture.componentInstance;
router = TestBed.inject(Router);
router.initialNavigation();
});
it('should company dropdown remains close', async () => {
expect(component.loginSectionDropdown).toBe(false);
expect(component.contactSectionDropdown).toBe(false);
});
it('should save button is disabled if form has invalid mobile number', () => {
component.ngOnInit();
const profileFormGroup = component.profileForm;
profileFormGroup.setValue({
jobTitle: 'Developer',
location: 'US',
telephone: '2744245',
mobile: 'AB8866322112',
oldPassword: 'admin@123',
newPassword: 'admin@1234',
verifyPassword: 'admin@1234',
companyName: 'test Name',
companyPostCode: '12345',
companyAddress: 'test address',
companyTelephone: '27442266',
companyEmail: 'email@test.com'
});
expect(profileFormGroup.valid).toEqual(false);
expect(component.isSaveButtonDisabled()).toBeTruthy();
});
it('should save button is disabled if form has invalid email', () => {
component.ngOnInit();
const profileFormGroup = component.profileForm;
profileFormGroup.setValue({
jobTitle: 'Developer',
location: 'US',
telephone: '27442445',
mobile: '457554',
oldPassword: 'admin@123',
newPassword: 'admin@1234',
verifyPassword: 'admin@1234',
companyName: 'test Name',
companyPostCode: '12345',
companyAddress: 'test address',
companyTelephone: '27442266',
companyEmail: 'email'
});
expect(profileFormGroup.valid).toEqual(false);
expect(component.isSaveButtonDisabled()).toBeTruthy();
});
it('should save button is enabled if form has valid inputs', () => {
component.ngOnInit();
const profileFormGroup = component.profileForm;
profileFormGroup.setValue({
jobTitle: 'Developer',
location: 'US',
telephone: '274-422-55',
mobile: '886-632-2112',
oldPassword: 'test@123',
newPassword: 'test@1234',
verifyPassword: 'test@1234',
companyName: 'testCompany',
companyPostCode: '12345',
companyAddress: 'test address',
companyTelephone: '274-22-66',
companyEmail: 'testEmail@test.com'
});
expect(profileFormGroup.valid).toEqual(true);
expect(component.isSaveButtonDisabled()).toBeFalsy();
});
it('should navigate to personal files when back button is clicked', () => {
const navigateSpy = spyOn(router, 'navigate');
component.navigateToPersonalFiles();
expect(navigateSpy).toHaveBeenCalledWith(['/personal-files'], { replaceUrl: true });
});
it('should expand or compress general dropdown when arrow button is clicked', () => {
spyOn(component, 'toggleGeneralDropdown').and.callThrough();
component.generalSectionDropdown = false;
fixture.detectChanges();
const generalToggleIcon = fixture.debugElement.query(By.css('#toggle-general-dropdown'));
generalToggleIcon.triggerEventHandler('click', null);
expect(component.toggleGeneralDropdown).toHaveBeenCalled();
expect(component.generalSectionButtonsToggle).toBe(true);
});
it('should expand or compress contact dropdown when arrow button is clicked', () => {
spyOn(component, 'toggleContactDropdown').and.callThrough();
component.contactSectionDropdown = false;
fixture.detectChanges();
const contactToggleIcon = fixture.debugElement.query(By.css('#toggle-contact-dropdown'));
contactToggleIcon.triggerEventHandler('click', null);
expect(component.toggleContactDropdown).toHaveBeenCalled();
expect(component.contactSectionButtonsToggle).toBe(true);
});
it('should toggle form view when edit or cancel buttons is clicked for general form', () => {
spyOn(component, 'toggleGeneralButtons').and.callThrough();
fixture.detectChanges();
const generalEditButton = fixture.debugElement.query(By.css('#general-edit-button'));
generalEditButton.triggerEventHandler('click', null);
fixture.detectChanges();
const generalCancelButton = fixture.debugElement.query(By.css('#general-cancel-button'));
generalCancelButton.triggerEventHandler('click', null);
expect(component.toggleGeneralButtons).toHaveBeenCalledTimes(2);
});
it('should toggle form view when edit or cancel buttons is clicked for contact form', () => {
spyOn(component, 'toggleContactButtons').and.callThrough();
fixture.detectChanges();
const contactEditButton = fixture.debugElement.query(By.css('#contact-edit-button'));
contactEditButton.triggerEventHandler('click', null);
fixture.detectChanges();
const contactCancelButton = fixture.debugElement.query(By.css('#contact-cancel-button'));
contactCancelButton.triggerEventHandler('click', null);
expect(component.toggleContactButtons).toHaveBeenCalledTimes(2);
});
});

View File

@ -8,7 +8,7 @@
import { AlfrescoApiService } from '@alfresco/adf-core';
import { PeopleApi, Person } from '@alfresco/js-api';
import { Component, OnInit, ViewEncapsulation } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { Router } from '@angular/router';
import { throwError } from 'rxjs';
@ -34,13 +34,12 @@ export class ViewProfileComponent implements OnInit {
contactSectionDropdown = false;
contactSectionButtonsToggle = true;
constructor(private formBuilder: FormBuilder, private router: Router, apiService: AlfrescoApiService) {
constructor(private router: Router, apiService: AlfrescoApiService) {
this.peopleApi = new PeopleApi(apiService.getInstance());
}
ngOnInit() {
this.populateForm(this.personDetails);
this.peopleApi
.getPerson('-me-')
.then((userInfo) => {
@ -53,18 +52,19 @@ export class ViewProfileComponent implements OnInit {
}
populateForm(userInfo: Person) {
this.profileForm = this.formBuilder.group({
jobTitle: [userInfo?.jobTitle || ''],
location: [userInfo?.location || ''],
telephone: [userInfo?.telephone || '', Validators.pattern('^[0-9]*$')],
mobile: [userInfo?.mobile || '', Validators.pattern('^[0-9]*$')],
oldPassword: [''],
newPassword: [''],
verifyPassword: [''],
companyPostCode: [userInfo?.company?.postcode || ''],
companyAddress: [userInfo?.company?.address1 || ''],
companyTelephone: [userInfo?.company?.telephone || '', Validators.pattern('^[0-9]*$')],
companyEmail: [userInfo?.company?.email || '', Validators.email]
this.profileForm = new FormGroup({
jobTitle: new FormControl(userInfo?.jobTitle || ''),
location: new FormControl(userInfo?.location || ''),
telephone: new FormControl(userInfo?.telephone || '', [Validators.pattern('^([0-9]+-)*[0-9]+$')]),
mobile: new FormControl(userInfo?.mobile || '', [Validators.pattern('^([0-9]+-)*[0-9]+$')]),
oldPassword: new FormControl(''),
newPassword: new FormControl(''),
verifyPassword: new FormControl(''),
companyName: new FormControl(userInfo?.company?.organization || ''),
companyPostCode: new FormControl(userInfo?.company?.postcode || ''),
companyAddress: new FormControl(userInfo?.company?.address1 || ''),
companyTelephone: new FormControl(userInfo?.company?.telephone || '', [Validators.pattern('^([0-9]+-)*[0-9]+$')]),
companyEmail: new FormControl(userInfo?.company?.email || '', [Validators.email])
});
}
@ -142,12 +142,13 @@ export class ViewProfileComponent implements OnInit {
updatePersonDetails(event) {
if (this.profileForm.valid) {
this.peopleApi
.updatePerson(this.personDetails.id, {
.updatePerson('-me-', {
jobTitle: event.value.jobTitle,
location: event.value.location,
telephone: event.value.telephone,
mobile: event.value.mobile,
company: {
organization: event.value.companyName,
postcode: event.value.companyPostCode,
address1: event.value.companyAddress,
telephone: event.value.companyTelephone,
@ -166,4 +167,8 @@ export class ViewProfileComponent implements OnInit {
this.populateForm(this.personDetails);
}
}
isSaveButtonDisabled(): boolean {
return this.profileForm.invalid;
}
}

View File

@ -70,7 +70,8 @@
"COMPANY_DETAILS": "Company details",
"NAME": "Name",
"ADDRESS": "Address",
"POST_CODE": "Post code"
"POSTCODE": "Postcode",
"INVALID_INPUT": "Invalid Input"
},
"LOCKED_BY": "Locked by: ",
"PREVIEW": {