From ab56a9c149a16b05f5a4f732afd13504a7c28e47 Mon Sep 17 00:00:00 2001 From: kurilova Date: Mon, 5 Aug 2024 12:10:01 +0000 Subject: [PATCH 1/3] Change Expired profile title on Enter; announce Expired profile title on Enter --- modules/ui/src/app/mocks/profile.mock.ts | 4 ++ .../profile-item/profile-item.component.html | 11 ++++-- .../profile-item.component.spec.ts | 37 +++++++++++++++++-- .../profile-item/profile-item.component.ts | 35 +++++++++++++++++- 4 files changed, 79 insertions(+), 8 deletions(-) diff --git a/modules/ui/src/app/mocks/profile.mock.ts b/modules/ui/src/app/mocks/profile.mock.ts index f2dbe82c8..460e5d1eb 100644 --- a/modules/ui/src/app/mocks/profile.mock.ts +++ b/modules/ui/src/app/mocks/profile.mock.ts @@ -182,3 +182,7 @@ export const COPY_PROFILE_MOCK: Profile = { }, ], }; + +export const EXPIRED_PROFILE_MOCK: Profile = Object.assign({}, PROFILE_MOCK, { + status: ProfileStatus.EXPIRED, +}); diff --git a/modules/ui/src/app/pages/risk-assessment/profile-item/profile-item.component.html b/modules/ui/src/app/pages/risk-assessment/profile-item/profile-item.component.html index 1384226fc..31049cd93 100644 --- a/modules/ui/src/app/pages/risk-assessment/profile-item/profile-item.component.html +++ b/modules/ui/src/app/pages/risk-assessment/profile-item/profile-item.component.html @@ -22,16 +22,21 @@ class="profile-item-info" role="button" tabindex="0" + #tooltip="matTooltip" matTooltip="{{ profile.status === ProfileStatus.EXPIRED - ? 'Expired. Please, create a new Risk profile.' + ? EXPIRED_TOOLTIP : profile.status }}" (click)="profileClicked.emit(profile)" - (keydown.enter)="profileClicked.emit(profile)"> + (keydown.enter)="enterProfileItem(profile)"> + [attr.aria-label]=" + profile.status === ProfileStatus.EXPIRED + ? EXPIRED_TOOLTIP + : profile.status + "> { let component: ProfileItemComponent; @@ -25,11 +29,16 @@ describe('ProfileItemComponent', () => { let compiled: HTMLElement; const testRunServiceMock = jasmine.createSpyObj(['getRiskClass']); - + const mockLiveAnnouncer = jasmine.createSpyObj('mockLiveAnnouncer', [ + 'announce', + ]); beforeEach(async () => { await TestBed.configureTestingModule({ imports: [ProfileItemComponent], - providers: [{ provide: TestRunService, useValue: testRunServiceMock }], + providers: [ + { provide: TestRunService, useValue: testRunServiceMock }, + { provide: LiveAnnouncer, useValue: mockLiveAnnouncer }, + ], }).compileComponents(); fixture = TestBed.createComponent(ProfileItemComponent); @@ -88,4 +97,26 @@ describe('ProfileItemComponent', () => { expect(profileClickedSpy).toHaveBeenCalledWith(PROFILE_MOCK); }); + + describe('with Expired profile', () => { + beforeEach(() => { + component.profile = EXPIRED_PROFILE_MOCK; + }); + + it('should change tooltip on enterProfileItem', () => { + component.enterProfileItem(EXPIRED_PROFILE_MOCK); + + expect(component.tooltip.message).toEqual( + 'This risk profile is outdated. Please create a new risk profile.' + ); + }); + + it('should announce', () => { + component.outEvent(); + + expect(mockLiveAnnouncer.announce).toHaveBeenCalledWith( + 'This risk profile is outdated. Please create a new risk profile.' + ); + }); + }); }); diff --git a/modules/ui/src/app/pages/risk-assessment/profile-item/profile-item.component.ts b/modules/ui/src/app/pages/risk-assessment/profile-item/profile-item.component.ts index 45ea3b2ca..514cbd46e 100644 --- a/modules/ui/src/app/pages/risk-assessment/profile-item/profile-item.component.ts +++ b/modules/ui/src/app/pages/risk-assessment/profile-item/profile-item.component.ts @@ -17,8 +17,10 @@ import { ChangeDetectionStrategy, Component, EventEmitter, + HostListener, Input, Output, + ViewChild, } from '@angular/core'; import { Profile, @@ -29,26 +31,55 @@ import { MatIcon } from '@angular/material/icon'; import { MatButtonModule } from '@angular/material/button'; import { CommonModule } from '@angular/common'; import { TestRunService } from '../../../services/test-run.service'; -import { MatTooltipModule } from '@angular/material/tooltip'; +import { MatTooltip, MatTooltipModule } from '@angular/material/tooltip'; +import { LiveAnnouncer } from '@angular/cdk/a11y'; @Component({ selector: 'app-profile-item', standalone: true, imports: [MatIcon, MatButtonModule, CommonModule, MatTooltipModule], + providers: [MatTooltip], templateUrl: './profile-item.component.html', styleUrl: './profile-item.component.scss', changeDetection: ChangeDetectionStrategy.OnPush, }) export class ProfileItemComponent { public readonly ProfileStatus = ProfileStatus; + public readonly EXPIRED_TOOLTIP = + 'Expired. Please, create a new Risk profile.'; @Input() profile!: Profile; @Output() deleteButtonClicked = new EventEmitter(); @Output() profileClicked = new EventEmitter(); @Output() copyProfileClicked = new EventEmitter(); - constructor(private readonly testRunService: TestRunService) {} + @ViewChild('tooltip') tooltip!: MatTooltip; + + @HostListener('focusout', ['$event']) + outEvent(): void { + if (this.profile.status === ProfileStatus.EXPIRED) { + this.tooltip.message = this.EXPIRED_TOOLTIP; + } + } + + constructor( + private readonly testRunService: TestRunService, + private liveAnnouncer: LiveAnnouncer + ) {} public getRiskClass(riskResult: string): RiskResultClassName { return this.testRunService.getRiskClass(riskResult); } + + public async enterProfileItem(profile: Profile) { + if (profile.status === ProfileStatus.EXPIRED) { + this.tooltip.message = + 'This risk profile is outdated. Please create a new risk profile.'; + this.tooltip.show(); + await this.liveAnnouncer.announce( + 'This risk profile is outdated. Please create a new risk profile.' + ); + } else { + this.profileClicked.emit(profile); + } + } } From 8d1c0cbd83daab32202d181fa524178bea1fb839 Mon Sep 17 00:00:00 2001 From: kurilova Date: Mon, 5 Aug 2024 12:40:02 +0000 Subject: [PATCH 2/3] Fix error --- .../profile-item/profile-item.component.spec.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/modules/ui/src/app/pages/risk-assessment/profile-item/profile-item.component.spec.ts b/modules/ui/src/app/pages/risk-assessment/profile-item/profile-item.component.spec.ts index 75b6855a4..56f9ad6a4 100644 --- a/modules/ui/src/app/pages/risk-assessment/profile-item/profile-item.component.spec.ts +++ b/modules/ui/src/app/pages/risk-assessment/profile-item/profile-item.component.spec.ts @@ -100,20 +100,16 @@ describe('ProfileItemComponent', () => { describe('with Expired profile', () => { beforeEach(() => { - component.profile = EXPIRED_PROFILE_MOCK; + component.enterProfileItem(EXPIRED_PROFILE_MOCK); }); it('should change tooltip on enterProfileItem', () => { - component.enterProfileItem(EXPIRED_PROFILE_MOCK); - expect(component.tooltip.message).toEqual( 'This risk profile is outdated. Please create a new risk profile.' ); }); it('should announce', () => { - component.outEvent(); - expect(mockLiveAnnouncer.announce).toHaveBeenCalledWith( 'This risk profile is outdated. Please create a new risk profile.' ); From 78ca09f017b75d93933b37ba27c480979bf367f7 Mon Sep 17 00:00:00 2001 From: Sofia Kurilova Date: Wed, 7 Aug 2024 14:21:27 +0200 Subject: [PATCH 3/3] The risk profile saved with old format is shown improperly while loading based on a new format (#664) * Fill only fields that are present in profile --- modules/ui/src/app/mocks/profile.mock.ts | 23 +++++++++++++++++++ .../profile-form.component.spec.ts | 19 +++++++++++++++ .../profile-form/profile-form.component.ts | 7 ++++-- 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/modules/ui/src/app/mocks/profile.mock.ts b/modules/ui/src/app/mocks/profile.mock.ts index 460e5d1eb..d53703809 100644 --- a/modules/ui/src/app/mocks/profile.mock.ts +++ b/modules/ui/src/app/mocks/profile.mock.ts @@ -183,6 +183,29 @@ export const COPY_PROFILE_MOCK: Profile = { ], }; +export const OUTDATED_DRAFT_PROFILE_MOCK: Profile = { + name: 'Outdated profile', + status: ProfileStatus.DRAFT, + questions: [ + { + question: 'Old question', + answer: 'qwerty', + }, + { + question: 'What is the email of the device owner(s)?', + answer: 'boddey@google.com, cmeredith@google.com', + }, + { + question: 'What type of device do you need reviewed?', + answer: 'IoT Sensor', + }, + { + question: 'Another old question', + answer: 'qwerty', + }, + ], +}; + export const EXPIRED_PROFILE_MOCK: Profile = Object.assign({}, PROFILE_MOCK, { status: ProfileStatus.EXPIRED, }); diff --git a/modules/ui/src/app/pages/risk-assessment/profile-form/profile-form.component.spec.ts b/modules/ui/src/app/pages/risk-assessment/profile-form/profile-form.component.spec.ts index 4a64d2b2c..54a87e3b9 100644 --- a/modules/ui/src/app/pages/risk-assessment/profile-form/profile-form.component.spec.ts +++ b/modules/ui/src/app/pages/risk-assessment/profile-form/profile-form.component.spec.ts @@ -21,6 +21,7 @@ import { COPY_PROFILE_MOCK, NEW_PROFILE_MOCK, NEW_PROFILE_MOCK_DRAFT, + OUTDATED_DRAFT_PROFILE_MOCK, PROFILE_FORM, PROFILE_MOCK, PROFILE_MOCK_2, @@ -416,6 +417,24 @@ describe('ProfileFormComponent', () => { }); describe('Class tests', () => { + describe('with outdated draft profile', () => { + beforeEach(() => { + component.selectedProfile = OUTDATED_DRAFT_PROFILE_MOCK; + fixture.detectChanges(); + }); + + it('should have an error when uses the name of copy profile', () => { + expect(component.profileForm.value).toEqual({ + 0: '', + 1: 'IoT Sensor', + 2: '', + 3: { 0: false, 1: false, 2: false }, + 4: '', + name: 'Outdated profile', + }); + }); + }); + describe('with profile', () => { beforeEach(() => { component.selectedProfile = PROFILE_MOCK; diff --git a/modules/ui/src/app/pages/risk-assessment/profile-form/profile-form.component.ts b/modules/ui/src/app/pages/risk-assessment/profile-form/profile-form.component.ts index 684bc67da..567eb6c34 100644 --- a/modules/ui/src/app/pages/risk-assessment/profile-form/profile-form.component.ts +++ b/modules/ui/src/app/pages/risk-assessment/profile-form/profile-form.component.ts @@ -207,16 +207,19 @@ export class ProfileFormComponent implements OnInit { fillProfileForm(profileFormat: ProfileFormat[], profile: Profile): void { this.nameControl.setValue(profile.name); profileFormat.forEach((question, index) => { + const answer = profile.questions.find( + answers => answers.question === question.question + ); if (question.type === FormControlType.SELECT_MULTIPLE) { question.options?.forEach((item, idx) => { - if ((profile.questions[index].answer as number[])?.includes(idx)) { + if ((answer?.answer as number[])?.includes(idx)) { this.getFormGroup(index).controls[idx].setValue(true); } else { this.getFormGroup(index).controls[idx].setValue(false); } }); } else { - this.getControl(index).setValue(profile.questions[index].answer); + this.getControl(index).setValue(answer?.answer || ''); } }); this.nameControl.markAsTouched();