HXPMNT-542 display email for people (#10622)

This commit is contained in:
Tomasz Nastaly
2025-02-06 20:14:05 +01:00
committed by GitHub
parent dcb7e4067c
commit e5281e7ff0
3 changed files with 73 additions and 51 deletions

View File

@@ -18,7 +18,6 @@
import { FullNamePipe } from './full-name.pipe'; import { FullNamePipe } from './full-name.pipe';
describe('FullNamePipe', () => { describe('FullNamePipe', () => {
let pipe: FullNamePipe; let pipe: FullNamePipe;
beforeEach(() => { beforeEach(() => {
@@ -31,27 +30,32 @@ describe('FullNamePipe', () => {
}); });
it('should return only firstName as fullName when there is no lastName ', () => { it('should return only firstName as fullName when there is no lastName ', () => {
const user = {firstName : 'Abc'}; const user = { firstName: 'Abc' };
expect(pipe.transform(user)).toBe('Abc'); expect(pipe.transform(user)).toBe('Abc');
}); });
it('should return only lastName as fullName when there is no firstName ', () => { it('should return only lastName as fullName when there is no firstName ', () => {
const user = {lastName : 'Xyz'}; const user = { lastName: 'Xyz' };
expect(pipe.transform(user)).toBe('Xyz'); expect(pipe.transform(user)).toBe('Xyz');
}); });
it('should return fullName when firstName and lastName are available', () => { it('should return fullName when firstName and lastName are available', () => {
const user = {firstName : 'Abc', lastName : 'Xyz'}; const user = { firstName: 'Abc', lastName: 'Xyz' };
expect(pipe.transform(user)).toBe('Abc Xyz'); expect(pipe.transform(user)).toBe('Abc Xyz');
}); });
it('should return username when firstName and lastName are not available', () => { it('should return username when firstName and lastName are not available', () => {
const user = {firstName : '', lastName : '', username: 'username'}; const user = { firstName: '', lastName: '', username: 'username' };
expect(pipe.transform(user)).toBe('username'); expect(pipe.transform(user)).toBe('username');
}); });
it('should return user eamil when firstName, lastName and username are not available', () => { it('should return user eamil when firstName, lastName and username are not available', () => {
const user = {firstName : '', lastName : '', username: '', email: 'abcXyz@gmail.com'}; const user = { firstName: '', lastName: '', username: '', email: 'abcXyz@gmail.com' };
expect(pipe.transform(user)).toBe('abcXyz@gmail.com'); expect(pipe.transform(user)).toBe('abcXyz@gmail.com');
}); });
it('should display email and fullName if emailDisplayed param is true', () => {
const user = { firstName: 'John', lastName: 'Doe', username: '', email: 'abcXyz@gmail.com' };
expect(pipe.transform(user, true)).toBe('John Doe <abcXyz@gmail.com>');
});
}); });

View File

@@ -23,8 +23,9 @@ import { UserLike } from './user-like.interface';
standalone: true standalone: true
}) })
export class FullNamePipe implements PipeTransform { export class FullNamePipe implements PipeTransform {
transform(user: UserLike): string { transform(user: UserLike, emailDisplayed?: boolean): string {
return this.buildFullName(user) ? this.buildFullName(user) : this.buildFromUsernameOrEmail(user); const fullName = this.buildFullName(user) ? this.buildFullName(user) : this.buildFromUsernameOrEmail(user);
return `${fullName} ${emailDisplayed ? '<' + user?.email + '>' : ''}`.trim();
} }
buildFullName(user: UserLike): string { buildFullName(user: UserLike): string {

View File

@@ -11,54 +11,60 @@
<mat-chip-grid #userMultipleChipList data-automation-id="adf-cloud-people-chip-list"> <mat-chip-grid #userMultipleChipList data-automation-id="adf-cloud-people-chip-list">
<mat-chip-row <mat-chip-row
*ngFor="let user of selectedUsers" *ngFor="let user of selectedUsers"
[removable]="!(user.readonly)" [removable]="!user.readonly"
[attr.data-automation-id]="'adf-people-cloud-chip-' + user.username" [attr.data-automation-id]="'adf-people-cloud-chip-' + user.username"
(removed)="onRemove(user)" (removed)="onRemove(user)"
[disabled]="isReadonly() || isValidationLoading()" [disabled]="isReadonly() || isValidationLoading()"
title="{{ (user.readonly ? 'ADF_CLOUD_GROUPS.MANDATORY' : '') | translate }}" title="{{ (user.readonly ? 'ADF_CLOUD_GROUPS.MANDATORY' : '') | translate }}"
[matTooltip]="showFullNameOnHover ? (user | fullName) : ''" [matTooltip]="showFullNameOnHover ? (user | fullName : true) : user.email"
> >
{{user | fullName}} {{ user | fullName }}
<mat-icon <mat-icon
matChipRemove matChipRemove
*ngIf="!(user.readonly || readOnly)" *ngIf="!(user.readonly || readOnly)"
[attr.data-automation-id]="'adf-people-cloud-chip-remove-icon-' + user.username"> [attr.data-automation-id]="'adf-people-cloud-chip-remove-icon-' + user.username"
>
cancel cancel
</mat-icon> </mat-icon>
</mat-chip-row> </mat-chip-row>
<input matInput <input
[disabled]="isReadonly()" matInput
[formControl]="searchUserCtrl" [disabled]="isReadonly()"
[matAutocomplete]="auto" [formControl]="searchUserCtrl"
[matChipInputFor]="userMultipleChipList" [matAutocomplete]="auto"
[required]="required" [matChipInputFor]="userMultipleChipList"
[placeholder]="placeholder" [required]="required"
(focus)="setFocus(true)" [placeholder]="placeholder"
(blur)="setFocus(false); markAsTouched()" (focus)="setFocus(true)"
class="adf-cloud-input" (blur)="setFocus(false); markAsTouched()"
data-automation-id="adf-people-cloud-search-input" class="adf-cloud-input"
#userInput data-automation-id="adf-people-cloud-search-input"
> #userInput
/>
</mat-chip-grid> </mat-chip-grid>
<mat-autocomplete autoActiveFirstOption class="adf-people-cloud-list" <mat-autocomplete
#auto="matAutocomplete" autoActiveFirstOption
(optionSelected)="onSelect($event.option.value)" class="adf-people-cloud-list"
[displayWith]="getDisplayName"> #auto="matAutocomplete"
<ng-container *ngIf="(searchUsers$ | async)?.length else noResults" > (optionSelected)="onSelect($event.option.value)"
<mat-option *ngFor="let user of searchUsers$ | async; let i = index" [value]="user" [displayWith]="getDisplayName"
class="adf-people-cloud-option-active"> >
<div class="adf-people-cloud-row" id="adf-people-cloud-user-{{user.username}}" <ng-container *ngIf="(searchUsers$ | async)?.length; else noResults">
data-automation-id="adf-people-cloud-row"> <mat-option *ngFor="let user of searchUsers$ | async; let i = index" [value]="user" class="adf-people-cloud-option-active">
<div [outerHTML]="user | usernameInitials:'adf-people-cloud-pic'"></div> <div class="adf-people-cloud-row" id="adf-people-cloud-user-{{ user.username }}" data-automation-id="adf-people-cloud-row">
<span class="adf-people-label-name"> {{user | fullName}}</span> <div [outerHTML]="user | usernameInitials : 'adf-people-cloud-pic'"></div>
</div> <span class="adf-people-label-name"> {{ user | fullName : true }}</span>
</mat-option> </div>
</mat-option>
</ng-container> </ng-container>
<ng-template #noResults> <ng-template #noResults>
<mat-option *ngIf="searchUserCtrl.hasError('searchTypingError') && !searchLoading" disabled <mat-option
class="adf-people-cloud-option-not-active" *ngIf="searchUserCtrl.hasError('searchTypingError') && !searchLoading"
data-automation-id="adf-people-cloud-no-results"> 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> <span> {{ 'ADF_CLOUD_USERS.ERROR.NOT_FOUND' | translate : { userName: searchedValue } }}</span>
</mat-option> </mat-option>
</ng-template> </ng-template>
@@ -70,29 +76,40 @@
<div class="adf-error-container" *ngIf="showErrors"> <div class="adf-error-container" *ngIf="showErrors">
<mat-error *ngIf="hasPreselectError() && !isValidationLoading()" [@transitionMessages]="subscriptAnimationState" class="adf-error"> <mat-error *ngIf="hasPreselectError() && !isValidationLoading()" [@transitionMessages]="subscriptAnimationState" class="adf-error">
<mat-icon class="adf-error-icon">error_outline</mat-icon> <mat-icon class="adf-error-icon">error_outline</mat-icon>
<div class="adf-error-text">{{ 'ADF_CLOUD_USERS.ERROR.NOT_FOUND' | translate : { userName : validateUsersMessage } }}</div> <div class="adf-error-text">{{ 'ADF_CLOUD_USERS.ERROR.NOT_FOUND' | translate : { userName: validateUsersMessage } }}</div>
</mat-error> </mat-error>
<mat-error *ngIf="searchUserCtrl.hasError('pattern')" [@transitionMessages]="subscriptAnimationState" class="adf-error"> <mat-error *ngIf="searchUserCtrl.hasError('pattern')" [@transitionMessages]="subscriptAnimationState" class="adf-error">
<mat-icon class="adf-error-icon">error_outline</mat-icon> <mat-icon class="adf-error-icon">error_outline</mat-icon>
<div class="adf-error-text">{{ 'ADF_CLOUD_PEOPLE_GROUPS.ERROR.INVALID_PATTERN' | translate: { pattern: getValidationPattern() } }}</div> <div class="adf-error-text">{{ 'ADF_CLOUD_PEOPLE_GROUPS.ERROR.INVALID_PATTERN' | translate : { pattern: getValidationPattern() } }}</div>
</mat-error> </mat-error>
<mat-error *ngIf="searchUserCtrl.hasError('maxlength')" [@transitionMessages]="subscriptAnimationState" class="adf-error"> <mat-error *ngIf="searchUserCtrl.hasError('maxlength')" [@transitionMessages]="subscriptAnimationState" class="adf-error">
<mat-icon class="adf-error-icon">error_outline</mat-icon> <mat-icon class="adf-error-icon">error_outline</mat-icon>
<div class="adf-error-text">{{ 'ADF_CLOUD_PEOPLE_GROUPS.ERROR.INVALID_MAX_LENGTH' | translate: { requiredLength: getValidationMaxLength() } }}</div> <div class="adf-error-text">
{{ 'ADF_CLOUD_PEOPLE_GROUPS.ERROR.INVALID_MAX_LENGTH' | translate : { requiredLength: getValidationMaxLength() } }}
</div>
</mat-error> </mat-error>
<mat-error *ngIf="searchUserCtrl.hasError('minlength')" [@transitionMessages]="subscriptAnimationState" class="adf-error"> <mat-error *ngIf="searchUserCtrl.hasError('minlength')" [@transitionMessages]="subscriptAnimationState" class="adf-error">
<mat-icon class="adf-error-icon">error_outline</mat-icon> <mat-icon class="adf-error-icon">error_outline</mat-icon>
<div class="adf-error-text">{{ 'ADF_CLOUD_PEOPLE_GROUPS.ERROR.INVALID_MIN_LENGTH' | translate: { requiredLength: getValidationMinLength() } }}</div> <div class="adf-error-text">
{{ 'ADF_CLOUD_PEOPLE_GROUPS.ERROR.INVALID_MIN_LENGTH' | translate : { requiredLength: getValidationMinLength() } }}
</div>
</mat-error> </mat-error>
<mat-error *ngIf="(searchUserCtrl.hasError('required') || userChipsCtrl.hasError('required')) && isDirty()" <mat-error
[@transitionMessages]="subscriptAnimationState" class="adf-error"> *ngIf="(searchUserCtrl.hasError('required') || userChipsCtrl.hasError('required')) && isDirty()"
[@transitionMessages]="subscriptAnimationState"
class="adf-error"
>
<mat-icon class="adf-error-icon">error_outline</mat-icon> <mat-icon class="adf-error-icon">error_outline</mat-icon>
<div class="adf-error-text">{{ 'ADF_CLOUD_PEOPLE_GROUPS.ERROR.REQUIRED' | translate }}</div> <div class="adf-error-text">{{ 'ADF_CLOUD_PEOPLE_GROUPS.ERROR.REQUIRED' | translate }}</div>
</mat-error> </mat-error>
<mat-error *ngIf="searchUserCtrl.hasError('searchTypingError') && !this.isFocused" <mat-error
[@transitionMessages]="subscriptAnimationState" data-automation-id="invalid-users-typing-error" class="adf-error"> *ngIf="searchUserCtrl.hasError('searchTypingError') && !this.isFocused"
[@transitionMessages]="subscriptAnimationState"
data-automation-id="invalid-users-typing-error"
class="adf-error"
>
<mat-icon class="adf-error-icon">error_outline</mat-icon> <mat-icon class="adf-error-icon">error_outline</mat-icon>
<div class="adf-error-text">{{ 'ADF_CLOUD_USERS.ERROR.NOT_FOUND' | translate : { userName : searchedValue } }}</div> <div class="adf-error-text">{{ 'ADF_CLOUD_USERS.ERROR.NOT_FOUND' | translate : { userName: searchedValue } }}</div>
</mat-error> </mat-error>
</div> </div>
</form> </form>