diff --git a/modules/ui/src/app/pages/risk-assessment/risk-assessment.component.html b/modules/ui/src/app/pages/risk-assessment/risk-assessment.component.html
index 0e19b944b..053add44f 100644
--- a/modules/ui/src/app/pages/risk-assessment/risk-assessment.component.html
+++ b/modules/ui/src/app/pages/risk-assessment/risk-assessment.component.html
@@ -43,7 +43,11 @@
{
let component: RiskAssessmentComponent;
let fixture: ComponentFixture;
let mockService: SpyObj;
- let mockFocusManagerService: SpyObj;
let mockRiskAssessmentStore: SpyObj;
const mockLiveAnnouncer: SpyObj = jasmine.createSpyObj([
@@ -52,16 +50,15 @@ describe('RiskAssessmentComponent', () => {
mockService = jasmine.createSpyObj(['fetchProfiles', 'deleteProfile']);
mockService.deleteProfile.and.returnValue(of(true));
- mockFocusManagerService = jasmine.createSpyObj('mockFocusManagerService', [
- 'focusFirstElementInContainer',
- ]);
-
mockRiskAssessmentStore = jasmine.createSpyObj('RiskAssessmentStore', [
'deleteProfile',
'setFocus',
'getProfilesFormat',
'saveProfile',
'updateSelectedProfile',
+ 'setFocusOnCreateButton',
+ 'setFocusOnSelectedProfile',
+ 'setFocusOnProfileForm',
]);
await TestBed.configureTestingModule({
@@ -73,7 +70,6 @@ describe('RiskAssessmentComponent', () => {
imports: [MatToolbarModule, MatSidenavModule, BrowserAnimationsModule],
providers: [
{ provide: TestRunService, useValue: mockService },
- { provide: FocusManagerService, useValue: mockFocusManagerService },
{ provide: RiskAssessmentStore, useValue: mockRiskAssessmentStore },
{ provide: LiveAnnouncer, useValue: mockLiveAnnouncer },
],
@@ -213,10 +209,10 @@ describe('RiskAssessmentComponent', () => {
);
});
- it('should focus first element in container', async () => {
+ it('should focus first element in profile form', async () => {
await component.openForm();
expect(
- mockFocusManagerService.focusFirstElementInContainer
+ mockRiskAssessmentStore.setFocusOnProfileForm
).toHaveBeenCalled();
});
});
diff --git a/modules/ui/src/app/pages/risk-assessment/risk-assessment.component.ts b/modules/ui/src/app/pages/risk-assessment/risk-assessment.component.ts
index 1336a67de..503d87a52 100644
--- a/modules/ui/src/app/pages/risk-assessment/risk-assessment.component.ts
+++ b/modules/ui/src/app/pages/risk-assessment/risk-assessment.component.ts
@@ -23,7 +23,6 @@ import { RiskAssessmentStore } from './risk-assessment.store';
import { SimpleDialogComponent } from '../../components/simple-dialog/simple-dialog.component';
import { Subject, takeUntil } from 'rxjs';
import { MatDialog } from '@angular/material/dialog';
-import { FocusManagerService } from '../../services/focus-manager.service';
import { LiveAnnouncer } from '@angular/cdk/a11y';
import { Profile } from '../../model/profile';
import { Observable } from 'rxjs/internal/Observable';
@@ -42,7 +41,6 @@ export class RiskAssessmentComponent implements OnInit, OnDestroy {
constructor(
private store: RiskAssessmentStore,
public dialog: MatDialog,
- private focusManagerService: FocusManagerService,
private liveAnnouncer: LiveAnnouncer
) {}
@@ -59,8 +57,7 @@ export class RiskAssessmentComponent implements OnInit, OnDestroy {
this.isOpenProfileForm = true;
this.store.updateSelectedProfile(profile);
await this.liveAnnouncer.announce('Risk assessment questionnaire');
- const profileForm = window.document.querySelector('app-profile-form');
- this.focusManagerService.focusFirstElementInContainer(profileForm);
+ this.store.setFocusOnProfileForm();
}
deleteProfile(
@@ -95,17 +92,23 @@ export class RiskAssessmentComponent implements OnInit, OnDestroy {
saveProfileClicked(profile: Profile, selectedProfile: Profile | null): void {
if (!selectedProfile) {
this.saveProfile(profile);
+ this.store.setFocusOnCreateButton();
} else {
this.openSaveDialog(selectedProfile.name)
.pipe(takeUntil(this.destroy$))
.subscribe(saveProfile => {
if (saveProfile) {
this.saveProfile(profile);
+ this.store.setFocusOnSelectedProfile();
}
});
}
}
+ trackByIndex = (index: number): number => {
+ return index;
+ };
+
private closeFormAfterDelete(name: string, selectedProfile: Profile | null) {
if (selectedProfile?.name === name) {
this.isOpenProfileForm = false;
diff --git a/modules/ui/src/app/pages/risk-assessment/risk-assessment.store.spec.ts b/modules/ui/src/app/pages/risk-assessment/risk-assessment.store.spec.ts
index ab8fdf4be..3bc123929 100644
--- a/modules/ui/src/app/pages/risk-assessment/risk-assessment.store.spec.ts
+++ b/modules/ui/src/app/pages/risk-assessment/risk-assessment.store.spec.ts
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import { TestBed } from '@angular/core/testing';
+import { fakeAsync, TestBed, tick } from '@angular/core/testing';
import { of, skip, take } from 'rxjs';
import { MockStore, provideMockStore } from '@ngrx/store/testing';
import { TestRunService } from '../../services/test-run.service';
@@ -69,6 +69,7 @@ describe('RiskAssessmentStore', () => {
store = TestBed.inject(MockStore);
riskAssessmentStore = TestBed.inject(RiskAssessmentStore);
+ mockFocusManagerService.focusFirstElementInContainer.calls.reset();
spyOn(store, 'dispatch').and.callFake(() => {});
});
@@ -170,6 +171,49 @@ describe('RiskAssessmentStore', () => {
});
});
+ describe('setFocusOnCreateButton', () => {
+ const container = document.createElement('div') as HTMLElement;
+ container.classList.add('risk-assessment-content-empty');
+ document.querySelector('body')?.appendChild(container);
+
+ it('should call focusFirstElementInContainer', fakeAsync(() => {
+ riskAssessmentStore.setFocusOnCreateButton();
+
+ tick(11);
+ expect(
+ mockFocusManagerService.focusFirstElementInContainer
+ ).toHaveBeenCalledWith(container);
+ }));
+ });
+
+ describe('setFocusOnSelectedProfile', () => {
+ const container = document.createElement('div') as HTMLElement;
+ container.classList.add('profiles-drawer-content');
+ const inner = document.createElement('div') as HTMLElement;
+ inner.classList.add('selected');
+ container.appendChild(inner);
+ document.querySelector('body')?.appendChild(container);
+
+ it('should call focusFirstElementInContainer', () => {
+ riskAssessmentStore.setFocusOnSelectedProfile();
+
+ expect(
+ mockFocusManagerService.focusFirstElementInContainer
+ ).toHaveBeenCalledWith(inner);
+ });
+ });
+
+ describe('setFocusOnProfileForm', () => {
+ const profileForm = window.document.querySelector('app-profile-form');
+ it('should call focusFirstElementInContainer', () => {
+ riskAssessmentStore.setFocusOnProfileForm();
+
+ expect(
+ mockFocusManagerService.focusFirstElementInContainer
+ ).toHaveBeenCalledWith(profileForm);
+ });
+ });
+
describe('getProfilesFormat', () => {
it('should update store', done => {
mockService.fetchProfilesFormat.and.returnValue(of(PROFILE_FORM));
diff --git a/modules/ui/src/app/pages/risk-assessment/risk-assessment.store.ts b/modules/ui/src/app/pages/risk-assessment/risk-assessment.store.ts
index b544fdf31..93e89b434 100644
--- a/modules/ui/src/app/pages/risk-assessment/risk-assessment.store.ts
+++ b/modules/ui/src/app/pages/risk-assessment/risk-assessment.store.ts
@@ -17,7 +17,7 @@
import { Injectable } from '@angular/core';
import { ComponentStore } from '@ngrx/component-store';
import { tap, withLatestFrom } from 'rxjs/operators';
-import { exhaustMap } from 'rxjs';
+import { delay, exhaustMap } from 'rxjs';
import { TestRunService } from '../../services/test-run.service';
import { Profile, ProfileFormat } from '../../model/profile';
import { FocusManagerService } from '../../services/focus-manager.service';
@@ -88,6 +88,37 @@ export class RiskAssessmentStore extends ComponentStore {
}
);
+ setFocusOnCreateButton = this.effect(trigger$ => {
+ return trigger$.pipe(
+ delay(10),
+ tap(() => {
+ this.focusManagerService.focusFirstElementInContainer(
+ window.document.querySelector('.risk-assessment-content-empty')
+ );
+ })
+ );
+ });
+
+ setFocusOnSelectedProfile = this.effect(trigger$ => {
+ return trigger$.pipe(
+ tap(() => {
+ this.focusManagerService.focusFirstElementInContainer(
+ window.document.querySelector('.profiles-drawer-content .selected')
+ );
+ })
+ );
+ });
+
+ setFocusOnProfileForm = this.effect(trigger$ => {
+ return trigger$.pipe(
+ tap(() => {
+ this.focusManagerService.focusFirstElementInContainer(
+ window.document.querySelector('app-profile-form')
+ );
+ })
+ );
+ });
+
getProfilesFormat = this.effect(trigger$ => {
return trigger$.pipe(
exhaustMap(() => {
diff --git a/modules/ui/src/app/services/focus-manager.service.ts b/modules/ui/src/app/services/focus-manager.service.ts
index c145fc71f..ebe3ad939 100644
--- a/modules/ui/src/app/services/focus-manager.service.ts
+++ b/modules/ui/src/app/services/focus-manager.service.ts
@@ -12,7 +12,6 @@ export class FocusManagerService {
const dialogOpened = window.document.querySelector('.mdc-dialog--open');
const parentElem = dialogOpened ? dialogOpened : container;
const firstInteractiveElem = this.findFirstInteractiveElem(parentElem);
-
if (firstInteractiveElem) {
firstInteractiveElem.focus();
}
@@ -22,7 +21,7 @@ export class FocusManagerService {
parentEl: Document | Element | null
): HTMLElement | undefined | null {
return parentEl?.querySelector(
- 'button:not([disabled="true"]):not([tabindex="-1"]), a:not([disabled="true"]), input:not([disabled="true"]), table'
+ 'button:not([disabled="true"]):not([tabindex="-1"]), a:not([disabled="true"]), input:not([disabled="true"]), table, [tabindex="0"]'
);
}
}