[AAE-1975] Add no results template when searching in People/Group clo… (#5479)

* [AAE-1975] Add no results template when searching in People/Group cloud component

* [AAE-1975] Add timeout for check no results e2e method

* [AAE-1975] Adapt unit tests, add no results checks

* [AAE-1975] Fix styling, remove error icon and red colour

* [AAE-1975] Remove timeout
This commit is contained in:
arditdomi
2020-02-24 15:37:55 +00:00
committed by GitHub
parent 536e2587ea
commit 56474bc96e
10 changed files with 119 additions and 65 deletions

View File

@@ -2,7 +2,7 @@
<mat-form-field class="adf-cloud-group"> <mat-form-field class="adf-cloud-group">
<mat-label *ngIf="!isReadonly()" <mat-label *ngIf="!isReadonly()"
id="adf-group-cloud-title-id">{{ (title || 'ADF_CLOUD_GROUPS.SEARCH-GROUP') | translate }}</mat-label> id="adf-group-cloud-title-id">{{ (title || 'ADF_CLOUD_GROUPS.SEARCH-GROUP') | translate }}</mat-label>
<mat-chip-list #groupChipList [disabled]="isReadonly() || isValidationLoading()" data-automation-id="adf-cloud-group-chip-list" class="apa-group-chip-list"> <mat-chip-list #groupChipList [disabled]="isReadonly() || isValidationLoading()" data-automation-id="adf-cloud-group-chip-list">
<mat-chip <mat-chip
*ngFor="let group of selectedGroups" *ngFor="let group of selectedGroups"
[removable]="!(group.readonly)" [removable]="!(group.readonly)"
@@ -34,18 +34,28 @@
(optionSelected)="onSelect($event.option.value)" (optionSelected)="onSelect($event.option.value)"
[displayWith]="getDisplayName" [displayWith]="getDisplayName"
data-automation-id="adf-cloud-group-autocomplete"> data-automation-id="adf-cloud-group-autocomplete">
<ng-container *ngIf="(searchGroups$ | async)?.length else noResults">
<mat-option *ngFor="let group of searchGroups$ | async; let i = index" [value]="group" <mat-option *ngFor="let group of searchGroups$ | async; let i = index" [value]="group"
[attr.data-automation-id]="'adf-cloud-group-chip-' + group.name"> [attr.data-automation-id]="'adf-cloud-group-chip-' + group.name">
<div class="adf-cloud-group-row" id="adf-group-{{i}}" fxLayout="row" fxLayoutAlign="start center" <div class="adf-cloud-group-row" id="adf-group-{{i}}" fxLayout="row" fxLayoutAlign="start center"
data-automation-id="adf-cloud-group-row"
fxLayoutGap="20px"> fxLayoutGap="20px">
<button class="adf-group-short-name" mat-fab>{{group | groupNameInitial }}</button> <button class="adf-group-short-name" mat-fab>{{group | groupNameInitial }}</button>
<span>{{group.name}}</span> <span>{{group.name}}</span>
</div> </div>
</mat-option> </mat-option>
</ng-container>
<ng-template #noResults>
<mat-option *ngIf="searchGroupsControl.hasError('searchTypingError') && !searchLoading" disabled
class="adf-cloud-group-option-not-active"
data-automation-id="adf-cloud-group-no-results">
<span> {{ 'ADF_CLOUD_GROUPS.ERROR.NOT_FOUND' | translate : { groupName: searchedValue } }}</span>
</mat-option>
</ng-template>
</mat-autocomplete> </mat-autocomplete>
</mat-form-field> </mat-form-field>
<mat-progress-bar <mat-progress-bar
*ngIf="isLoading" *ngIf="validationLoading"
mode="indeterminate"> mode="indeterminate">
</mat-progress-bar> </mat-progress-bar>

View File

@@ -14,6 +14,10 @@
&-cloud-group { &-cloud-group {
width: 100%; width: 100%;
&-option-not-active {
background: inherit !important;
}
.mat-form-field { .mat-form-field {
width: 100%; width: 100%;
} }

View File

@@ -97,7 +97,7 @@ describe('GroupCloudComponent', () => {
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();
expect(fixture.debugElement.queryAll(By.css('mat-option')).length).toEqual(5); expect(fixture.debugElement.queryAll(By.css('[data-automation-id="adf-cloud-group-row"]')).length).toEqual(5);
expect(findGroupsByNameSpy).toHaveBeenCalled(); expect(findGroupsByNameSpy).toHaveBeenCalled();
done(); done();
}); });
@@ -118,7 +118,7 @@ describe('GroupCloudComponent', () => {
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();
expect(fixture.debugElement.queryAll(By.css('mat-option')).length).toEqual(4); expect(fixture.debugElement.queryAll(By.css('[data-automation-id="adf-cloud-group-row"]')).length).toEqual(4);
done(); done();
}); });
}); });
@@ -134,7 +134,7 @@ describe('GroupCloudComponent', () => {
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
expect(element.querySelector('mat-option')).toBeNull(); expect(element.querySelector('[data-automation-id="adf-cloud-group-row"]')).toBeNull();
done(); done();
}); });
}); });
@@ -257,7 +257,7 @@ describe('GroupCloudComponent', () => {
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();
expect(fixture.debugElement.queryAll(By.css('mat-option')).length).toEqual(5); expect(fixture.debugElement.queryAll(By.css('[data-automation-id="adf-cloud-group-row"]')).length).toEqual(5);
done(); done();
}); });
}); });
@@ -275,7 +275,8 @@ describe('GroupCloudComponent', () => {
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();
expect(element.querySelectorAll('mat-option').length).toEqual(0); expect(fixture.debugElement.queryAll(By.css('[data-automation-id="adf-cloud-group-row"]')).length).toEqual(0);
expect(fixture.debugElement.queryAll(By.css('[data-automation-id="adf-cloud-group-no-results"]')).length).toEqual(1);
done(); done();
}); });
}); });
@@ -292,7 +293,7 @@ describe('GroupCloudComponent', () => {
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();
expect(fixture.debugElement.queryAll(By.css('mat-option')).length).toEqual(5); expect(fixture.debugElement.queryAll(By.css('[data-automation-id="adf-cloud-group-row"]')).length).toEqual(5);
expect(checkGroupHasAnyClientAppRoleSpy).toHaveBeenCalled(); expect(checkGroupHasAnyClientAppRoleSpy).toHaveBeenCalled();
done(); done();
}); });
@@ -311,7 +312,8 @@ describe('GroupCloudComponent', () => {
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();
expect(fixture.debugElement.queryAll(By.css('mat-option')).length).toEqual(0); expect(fixture.debugElement.queryAll(By.css('[data-automation-id="adf-cloud-group-row"]')).length).toEqual(0);
expect(fixture.debugElement.queryAll(By.css('[data-automation-id="adf-cloud-group-no-results"]')).length).toEqual(1);
expect(checkGroupHasAnyClientAppRoleSpy).toHaveBeenCalled(); expect(checkGroupHasAnyClientAppRoleSpy).toHaveBeenCalled();
done(); done();
}); });
@@ -433,7 +435,7 @@ describe('GroupCloudComponent', () => {
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();
expect(fixture.debugElement.queryAll(By.css('mat-option')).length).toEqual(5); expect(fixture.debugElement.queryAll(By.css('[data-automation-id="adf-cloud-group-row"]')).length).toEqual(5);
expect(checkGroupHasRoleSpy).toHaveBeenCalledTimes(5); expect(checkGroupHasRoleSpy).toHaveBeenCalledTimes(5);
done(); done();
}); });
@@ -451,7 +453,8 @@ describe('GroupCloudComponent', () => {
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();
expect(element.querySelectorAll('mat-option').length).toEqual(0); expect(fixture.debugElement.queryAll(By.css('[data-automation-id="adf-cloud-group-row"]')).length).toEqual(0);
expect(fixture.debugElement.queryAll(By.css('[data-automation-id="adf-cloud-group-no-results"]')).length).toEqual(1);
expect(checkGroupHasRoleSpy).toHaveBeenCalled(); expect(checkGroupHasRoleSpy).toHaveBeenCalled();
done(); done();
}); });

View File

@@ -129,7 +129,8 @@ export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy {
validateGroupsMessage: string; validateGroupsMessage: string;
searchedValue = ''; searchedValue = '';
isLoading = false; validationLoading = false;
searchLoading = false;
constructor( constructor(
private identityGroupService: IdentityGroupService, private identityGroupService: IdentityGroupService,
@@ -178,6 +179,7 @@ export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy {
initSearch(): void { initSearch(): void {
this.searchGroupsControl.valueChanges.pipe( this.searchGroupsControl.valueChanges.pipe(
filter((value) => { filter((value) => {
this.searchLoading = true;
return typeof value === 'string'; return typeof value === 'string';
}), }),
tap((value: string) => { tap((value: string) => {
@@ -201,6 +203,7 @@ export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy {
), ),
mergeMap((groups) => { mergeMap((groups) => {
this.resetSearchGroups(); this.resetSearchGroups();
this.searchLoading = false;
return groups; return groups;
}), }),
filter(group => !this.isGroupAlreadySelected(group)), filter(group => !this.isGroupAlreadySelected(group)),
@@ -308,9 +311,9 @@ export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy {
} }
this.groupChipsCtrl.setValue(this.selectedGroups[0].name); this.groupChipsCtrl.setValue(this.selectedGroups[0].name);
if (this.isValidationEnabled()) { if (this.isValidationEnabled()) {
this.isLoading = true; this.validationLoading = true;
await this.validatePreselectGroups(); await this.validatePreselectGroups();
this.isLoading = false; this.validationLoading = false;
} }
} }
@@ -322,6 +325,7 @@ export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy {
} }
onSelect(group: IdentityGroupModel): void { onSelect(group: IdentityGroupModel): void {
if (group) {
this.selectGroup.emit(group); this.selectGroup.emit(group);
if (this.isMultipleMode()) { if (this.isMultipleMode()) {
@@ -340,6 +344,7 @@ export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy {
this.changedGroups.emit(this.selectedGroups); this.changedGroups.emit(this.selectedGroups);
this.resetSearchGroups(); this.resetSearchGroups();
} }
}
onRemove(groupToRemove: IdentityGroupModel): void { onRemove(groupToRemove: IdentityGroupModel): void {
this.removeGroup.emit(groupToRemove); this.removeGroup.emit(groupToRemove);
@@ -474,7 +479,7 @@ export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy {
} }
isValidationLoading(): boolean { isValidationLoading(): boolean {
return this.isValidationEnabled() && this.isLoading; return this.isValidationEnabled() && this.validationLoading;
} }
setFocus(isFocused: boolean) { setFocus(isFocused: boolean) {

View File

@@ -30,16 +30,26 @@
#auto="matAutocomplete" #auto="matAutocomplete"
(optionSelected)="onSelect($event.option.value)" (optionSelected)="onSelect($event.option.value)"
[displayWith]="getDisplayName"> [displayWith]="getDisplayName">
<ng-container *ngIf="(searchUsers$ | async)?.length else noResults" >
<mat-option *ngFor="let user of searchUsers$ | async; let i = index" [value]="user"> <mat-option *ngFor="let user of searchUsers$ | async; let i = index" [value]="user">
<div class="adf-people-cloud-row" id="adf-people-cloud-user-{{i}}"> <div class="adf-people-cloud-row" id="adf-people-cloud-user-{{i}}"
data-automation-id="adf-people-cloud-row">
<div [outerHTML]="user | usernameInitials:'adf-people-widget-pic'"></div> <div [outerHTML]="user | usernameInitials:'adf-people-widget-pic'"></div>
<span class="adf-people-label-name"> {{user | fullName}}</span> <span class="adf-people-label-name"> {{user | fullName}}</span>
</div> </div>
</mat-option> </mat-option>
</ng-container>
<ng-template #noResults>
<mat-option *ngIf="searchUserCtrl.hasError('searchTypingError') && !searchLoading" disabled
class="adf-people-cloud-option-not-active"
data-automation-id="adf-people-cloud-no-results">
<span> {{ 'ADF_CLOUD_USERS.ERROR.NOT_FOUND' | translate : { userName: searchedValue } }}</span>
</mat-option>
</ng-template>
</mat-autocomplete> </mat-autocomplete>
</mat-form-field> </mat-form-field>
<mat-progress-bar <mat-progress-bar
*ngIf="isLoading" *ngIf="validationLoading"
mode="indeterminate"> mode="indeterminate">
</mat-progress-bar> </mat-progress-bar>

View File

@@ -8,6 +8,10 @@
&-people-cloud { &-people-cloud {
width: 100%; width: 100%;
&-option-not-active {
background: inherit !important;
}
} }
&-people-cloud-list { &-people-cloud-list {

View File

@@ -113,7 +113,7 @@ describe('PeopleCloudComponent', () => {
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();
expect(fixture.debugElement.queryAll(By.css('mat-option')).length).toEqual(3); expect(fixture.debugElement.queryAll(By.css('[data-automation-id="adf-people-cloud-row"]')).length).toEqual(3);
expect(findUsersByNameSpy).toHaveBeenCalled(); expect(findUsersByNameSpy).toHaveBeenCalled();
done(); done();
}); });
@@ -134,7 +134,7 @@ describe('PeopleCloudComponent', () => {
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();
expect(fixture.debugElement.queryAll(By.css('mat-option')).length).toEqual(2); expect(fixture.debugElement.queryAll(By.css('[data-automation-id="adf-people-cloud-row"]')).length).toEqual(2);
done(); done();
}); });
}); });
@@ -190,7 +190,7 @@ describe('PeopleCloudComponent', () => {
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
expect(element.querySelector('mat-option')).toBeNull(); expect(element.querySelector('[data-automation-id="adf-people-cloud-row"]')).toBeNull();
done(); done();
}); });
}); });
@@ -330,7 +330,8 @@ describe('PeopleCloudComponent', () => {
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();
expect(element.querySelectorAll('mat-option').length).toEqual(0); expect(fixture.debugElement.queryAll(By.css('[data-automation-id="adf-people-cloud-row"]')).length).toEqual(0);
expect(fixture.debugElement.queryAll(By.css('[data-automation-id="adf-people-cloud-no-results"]')).length).toEqual(1);
done(); done();
}); });
}); });
@@ -347,7 +348,7 @@ describe('PeopleCloudComponent', () => {
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();
expect(fixture.debugElement.queryAll(By.css('mat-option')).length).toEqual(3); expect(fixture.debugElement.queryAll(By.css('[data-automation-id="adf-people-cloud-row"]')).length).toEqual(3);
expect(checkUserHasAnyClientAppRoleSpy).toHaveBeenCalled(); expect(checkUserHasAnyClientAppRoleSpy).toHaveBeenCalled();
done(); done();
}); });
@@ -366,7 +367,8 @@ describe('PeopleCloudComponent', () => {
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();
expect(fixture.debugElement.queryAll(By.css('mat-option')).length).toEqual(0); expect(fixture.debugElement.queryAll(By.css('[data-automation-id="adf-people-cloud-row"]')).length).toEqual(0);
expect(fixture.debugElement.queryAll(By.css('[data-automation-id="adf-people-cloud-no-results"]')).length).toEqual(1);
expect(checkUserHasAnyClientAppRoleSpy).toHaveBeenCalled(); expect(checkUserHasAnyClientAppRoleSpy).toHaveBeenCalled();
done(); done();
}); });
@@ -486,7 +488,7 @@ describe('PeopleCloudComponent', () => {
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();
expect(fixture.debugElement.queryAll(By.css('mat-option')).length).toEqual(3); expect(fixture.debugElement.queryAll(By.css('[data-automation-id="adf-people-cloud-row"]')).length).toEqual(3);
expect(checkUserHasRoleSpy).toHaveBeenCalledTimes(3); expect(checkUserHasRoleSpy).toHaveBeenCalledTimes(3);
done(); done();
}); });
@@ -504,7 +506,8 @@ describe('PeopleCloudComponent', () => {
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();
expect(element.querySelectorAll('mat-option').length).toEqual(0); expect(fixture.debugElement.queryAll(By.css('[data-automation-id="adf-people-cloud-row"]')).length).toEqual(0);
expect(fixture.debugElement.queryAll(By.css('[data-automation-id="adf-people-cloud-no-results"]')).length).toEqual(1);
expect(checkUserHasRoleSpy).toHaveBeenCalled(); expect(checkUserHasRoleSpy).toHaveBeenCalled();
done(); done();
}); });

View File

@@ -138,7 +138,8 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy {
validateUsersMessage: string; validateUsersMessage: string;
searchedValue = ''; searchedValue = '';
isLoading = false; validationLoading = false;
searchLoading = false;
constructor( constructor(
private identityUserService: IdentityUserService, private identityUserService: IdentityUserService,
@@ -183,6 +184,7 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy {
private initSearch(): void { private initSearch(): void {
this.searchUserCtrl.valueChanges.pipe( this.searchUserCtrl.valueChanges.pipe(
filter((value) => { filter((value) => {
this.searchLoading = true;
return typeof value === 'string'; return typeof value === 'string';
}), }),
tap((value: string) => { tap((value: string) => {
@@ -207,6 +209,7 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy {
this.identityUserService.findUsersByName(search.trim())), this.identityUserService.findUsersByName(search.trim())),
mergeMap((users) => { mergeMap((users) => {
this.resetSearchUsers(); this.resetSearchUsers();
this.searchLoading = false;
return users; return users;
}), }),
filter(user => !this.isUserAlreadySelected(user)), filter(user => !this.isUserAlreadySelected(user)),
@@ -283,9 +286,9 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy {
} }
this.userChipsCtrl.setValue(this.selectedUsers[0].username); this.userChipsCtrl.setValue(this.selectedUsers[0].username);
if (this.isValidationEnabled()) { if (this.isValidationEnabled()) {
this.isLoading = true; this.validationLoading = true;
await this.validatePreselectUsers(); await this.validatePreselectUsers();
this.isLoading = false; this.validationLoading = false;
} }
} }
@@ -381,6 +384,7 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy {
} }
onSelect(user: IdentityUserModel): void { onSelect(user: IdentityUserModel): void {
if (user) {
this.selectUser.emit(user); this.selectUser.emit(user);
if (this.isMultipleMode()) { if (this.isMultipleMode()) {
@@ -399,6 +403,7 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy {
this.changedUsers.emit(this.selectedUsers); this.changedUsers.emit(this.selectedUsers);
this.resetSearchUsers(); this.resetSearchUsers();
} }
}
onRemove(userToRemove: IdentityUserModel): void { onRemove(userToRemove: IdentityUserModel): void {
this.removeUser.emit(userToRemove); this.removeUser.emit(userToRemove);
@@ -522,7 +527,7 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy {
} }
isValidationLoading(): boolean { isValidationLoading(): boolean {
return this.isValidationEnabled() && this.isLoading; return this.isValidationEnabled() && this.validationLoading;
} }
setFocus(isFocused: boolean) { setFocus(isFocused: boolean) {

View File

@@ -97,4 +97,9 @@ export class GroupCloudComponentPage {
} }
} }
async checkNoResultsFoundError(): Promise<void> {
const errorLocator = element(by.css('[data-automation-id="adf-cloud-group-no-results"]'));
await BrowserVisibility.waitUntilElementIsVisible(errorLocator);
}
} }

View File

@@ -133,4 +133,9 @@ export class PeopleCloudComponentPage {
} }
} }
async checkNoResultsFoundError(): Promise<void> {
const errorLocator = element(by.css('[data-automation-id="adf-people-cloud-no-results"]'));
await BrowserVisibility.waitUntilElementIsVisible(errorLocator);
}
} }