diff --git a/modules/ui/src/app/app.component.html b/modules/ui/src/app/app.component.html
index b52462b92..b6262e58d 100644
--- a/modules/ui/src/app/app.component.html
+++ b/modules/ui/src/app/app.component.html
@@ -231,20 +231,6 @@
Testrun
>Please update your Devices to continue testing.
-
- Step 3: Once device is created, you are able to start testing.
-
Testrun
(onCLoseTip)="onCLoseTip($event)"
(onAction)="navigateToAddDevice()">
+
+
diff --git a/modules/ui/src/app/app.component.spec.ts b/modules/ui/src/app/app.component.spec.ts
index 41f1780b4..c9ecd93ba 100644
--- a/modules/ui/src/app/app.component.spec.ts
+++ b/modules/ui/src/app/app.component.spec.ts
@@ -77,6 +77,7 @@ import { ShutdownAppComponent } from './components/shutdown-app/shutdown-app.com
import { TestingCompleteComponent } from './components/testing-complete/testing-complete.component';
import { VersionComponent } from './components/version/version.component';
import { MOCK_MODULES } from './mocks/device.mock';
+import { HelpTips } from './model/tip-config';
const windowMock = {
location: {
@@ -446,9 +447,7 @@ describe('AppComponent', () => {
);
}));
});
- });
- describe('Callout component visibility', () => {
describe('with system status as "Idle"', () => {
beforeEach(() => {
component.appStore.updateIsStatusLoaded(true);
@@ -459,57 +458,84 @@ describe('AppComponent', () => {
fixture.detectChanges();
});
- it('should have callout component with "Step 3" text', () => {
- const callout = compiled.querySelector('app-callout');
- const calloutContent = callout?.innerHTML.trim();
+ it('should have help tip with "Step 3" title', () => {
+ const helpTipTitle = compiled.querySelector('app-help-tip .title');
+ const helpTipTitleContent = helpTipTitle?.innerHTML.trim();
- expect(callout).toBeTruthy();
- expect(calloutContent).toContain('Step 3');
+ expect(helpTipTitleContent).toContain('Step 3');
});
- it('should NOT have callout component with "Step 3" if has reports', () => {
+ it('should NOT have help tip with "Step 3" if has reports', () => {
store.overrideSelector(selectReports, [...HISTORY]);
store.refreshState();
fixture.detectChanges();
- const callout = compiled.querySelector('app-callout');
+ const helpTip = compiled.querySelector('app-help-tip');
- expect(callout).toBeFalsy();
+ expect(helpTip).toBeFalsy();
});
});
- describe('with systemStatus data IN Progress and without riskProfiles', () => {
+ describe('with devices set but without systemStatus data', () => {
beforeEach(() => {
- store.overrideSelector(selectHasConnectionSettings, true);
store.overrideSelector(selectHasDevices, true);
- store.overrideSelector(selectHasRiskProfiles, false);
- store.overrideSelector(
- selectStatus,
- MOCK_PROGRESS_DATA_IN_PROGRESS.status
- );
+ component.appStore.updateIsStatusLoaded(true);
+ store.overrideSelector(selectHasConnectionSettings, true);
+ store.overrideSelector(selectSystemStatus, null);
+
fixture.detectChanges();
});
- it('should have callout component with "The device is now being tested" text', () => {
- const callout = compiled.querySelector('app-callout');
- const calloutContent = callout?.innerHTML.trim();
+ it('should have help tip with "Step 3" text', () => {
+ const helpTipTitle = compiled.querySelector('app-help-tip .title');
+ const helpTipTitleContent = helpTipTitle?.innerHTML.trim();
- expect(callout).toBeTruthy();
- expect(calloutContent).toContain('The device is now being tested');
+ expect(helpTipTitleContent).toContain('Step 3');
});
- it('should have callout component with "Risk Assessment" link', () => {
- const callout = compiled.querySelector('app-callout');
- const calloutLinkEl = compiled.querySelector(
- '.callout-action-link'
+ it('should have help tip with "Start Testrun" link', () => {
+ const helpTipLinkEl = compiled.querySelector(
+ '.tip-action-link'
) as HTMLAnchorElement;
- const calloutLinkContent = calloutLinkEl.innerHTML.trim();
+ const helpTipLinkContent = helpTipLinkEl.innerHTML.trim();
- expect(callout).toBeTruthy();
- expect(calloutLinkContent).toContain('Create risk Assessment');
+ expect(helpTipLinkEl).toBeTruthy();
+ expect(helpTipLinkContent).toContain(HelpTips.step3.action);
+ });
+
+ keyboardCases.forEach(testCase => {
+ it(`should navigate to the testing on keydown ${testCase.name} "Start Testrun" link`, fakeAsync(() => {
+ const helpTipLinkEl = compiled.querySelector(
+ '.tip-action-link'
+ ) as HTMLAnchorElement;
+
+ helpTipLinkEl.dispatchEvent(testCase.event);
+ flush();
+
+ expect(router.url).toBe(Routes.Testing);
+ }));
+ });
+ });
+
+ describe('with devices set and systemStatus data', () => {
+ beforeEach(() => {
+ store.overrideSelector(selectHasDevices, true);
+ store.overrideSelector(
+ selectSystemStatus,
+ MOCK_PROGRESS_DATA_IN_PROGRESS
+ );
+ fixture.detectChanges();
+ });
+
+ it('should not have help tip', () => {
+ const helpTip = compiled.querySelector('app-help-tip');
+
+ expect(helpTip).toBeNull();
});
});
+ });
+ describe('Callout component visibility', () => {
describe('with systemStatus data IN Progress and without riskProfiles', () => {
beforeEach(() => {
store.overrideSelector(selectHasConnectionSettings, true);
@@ -542,25 +568,27 @@ describe('AppComponent', () => {
});
});
- describe('with devices setted but without systemStatus data', () => {
+ describe('with systemStatus data IN Progress and without riskProfiles', () => {
beforeEach(() => {
- store.overrideSelector(selectHasDevices, true);
- component.appStore.updateIsStatusLoaded(true);
store.overrideSelector(selectHasConnectionSettings, true);
- store.overrideSelector(selectSystemStatus, null);
-
+ store.overrideSelector(selectHasDevices, true);
+ store.overrideSelector(selectHasRiskProfiles, false);
+ store.overrideSelector(
+ selectStatus,
+ MOCK_PROGRESS_DATA_IN_PROGRESS.status
+ );
fixture.detectChanges();
});
- it('should have callout component with "Step 3" text', () => {
+ it('should have callout component with "The device is now being tested" text', () => {
const callout = compiled.querySelector('app-callout');
const calloutContent = callout?.innerHTML.trim();
expect(callout).toBeTruthy();
- expect(calloutContent).toContain('Step 3');
+ expect(calloutContent).toContain('The device is now being tested');
});
- it('should have callout component with "testing" link', () => {
+ it('should have callout component with "Risk Assessment" link', () => {
const callout = compiled.querySelector('app-callout');
const calloutLinkEl = compiled.querySelector(
'.callout-action-link'
@@ -568,50 +596,7 @@ describe('AppComponent', () => {
const calloutLinkContent = calloutLinkEl.innerHTML.trim();
expect(callout).toBeTruthy();
- expect(calloutLinkContent).toContain('Testing');
- });
-
- keyboardCases.forEach(testCase => {
- it(`should navigate to the runtime on keydown ${testCase.name} "Run the Test" link`, fakeAsync(() => {
- const calloutLinkEl = compiled.querySelector(
- '.callout-action-link'
- ) as HTMLAnchorElement;
-
- calloutLinkEl.dispatchEvent(testCase.event);
- flush();
-
- expect(router.url).toBe(Routes.Testing);
- }));
- });
- });
-
- describe('with devices setted, without systemStatus data, but run the tests', () => {
- beforeEach(() => {
- store.overrideSelector(selectHasDevices, true);
- fixture.detectChanges();
- });
-
- it('should not have callout component', () => {
- const callout = compiled.querySelector('app-callout');
-
- expect(callout).toBeNull();
- });
- });
-
- describe('with devices setted and systemStatus data', () => {
- beforeEach(() => {
- store.overrideSelector(selectHasDevices, true);
- store.overrideSelector(
- selectSystemStatus,
- MOCK_PROGRESS_DATA_IN_PROGRESS
- );
- fixture.detectChanges();
- });
-
- it('should not have callout component', () => {
- const callout = compiled.querySelector('app-callout');
-
- expect(callout).toBeNull();
+ expect(calloutLinkContent).toContain('Create risk Assessment');
});
});
diff --git a/modules/ui/src/app/app.component.ts b/modules/ui/src/app/app.component.ts
index a6ed281c1..27a22cd5d 100644
--- a/modules/ui/src/app/app.component.ts
+++ b/modules/ui/src/app/app.component.ts
@@ -134,6 +134,7 @@ export class AppComponent implements AfterViewInit {
@ViewChild('settingButton', { static: false }) settingButton!: MatButton;
settingTipTarget!: HTMLElement;
deviceTipTarget!: HTMLElement;
+ testingTipTarget!: HTMLElement;
isClosedTip = false;
@HostListener('mousedown')
@@ -234,6 +235,9 @@ export class AppComponent implements AfterViewInit {
this.deviceTipTarget = document.querySelector(
'.app-sidebar-button.app-sidebar-button-devices'
) as HTMLElement;
+ this.testingTipTarget = document.querySelector(
+ '.app-sidebar-button.app-sidebar-button-testrun'
+ ) as HTMLElement;
this.viewModel$
.pipe(
@@ -282,6 +286,17 @@ export class AppComponent implements AfterViewInit {
onCLoseTip(isClosed: boolean): void {
this.isClosedTip = isClosed;
+ const helpTipButton = window.document.querySelector(
+ '.app-toolbar-button-help-tips'
+ ) as HTMLButtonElement;
+ const helpTipEl = window.document.querySelector('app-help-tip');
+ timer(100).subscribe(() => {
+ if (isClosed) {
+ helpTipButton.focus();
+ } else {
+ this.focusManagerService.focusFirstElementInContainer(helpTipEl);
+ }
+ });
}
consentShown() {
this.appStore.setContent();
diff --git a/modules/ui/src/app/components/help-tip/help-tip.component.spec.ts b/modules/ui/src/app/components/help-tip/help-tip.component.spec.ts
index b3d846ea1..413a86849 100644
--- a/modules/ui/src/app/components/help-tip/help-tip.component.spec.ts
+++ b/modules/ui/src/app/components/help-tip/help-tip.component.spec.ts
@@ -7,15 +7,32 @@ import {
import { HelpTipComponent } from './help-tip.component';
import { HelpTips } from '../../model/tip-config';
+import SpyObj = jasmine.SpyObj;
+import { LiveAnnouncer } from '@angular/cdk/a11y';
+import { FocusManagerService } from '../../services/focus-manager.service';
describe('HelpTipComponent', () => {
let component: HelpTipComponent;
let fixture: ComponentFixture;
let compiled: HTMLElement;
+ const mockLiveAnnouncer: SpyObj = jasmine.createSpyObj([
+ 'announce',
+ 'clear',
+ ]);
+
+ const mockFocusManagerService: SpyObj =
+ jasmine.createSpyObj('mockFocusManagerService', [
+ 'focusFirstElementInContainer',
+ ]);
+
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [HelpTipComponent],
+ providers: [
+ { provide: LiveAnnouncer, useValue: mockLiveAnnouncer },
+ { provide: FocusManagerService, useValue: mockFocusManagerService },
+ ],
}).compileComponents();
fixture = TestBed.createComponent(HelpTipComponent);
@@ -29,6 +46,15 @@ describe('HelpTipComponent', () => {
expect(component).toBeTruthy();
});
+ it('should set focus to first focusable elem', fakeAsync(() => {
+ component.ngOnInit();
+ tick(200);
+
+ expect(
+ mockFocusManagerService.focusFirstElementInContainer
+ ).toHaveBeenCalled();
+ }));
+
it('should have provided data', () => {
const tipTitle = compiled.querySelector('.tip-container .title');
const tipContent = compiled.querySelector('.tip-container .tip-content');
diff --git a/modules/ui/src/app/components/help-tip/help-tip.component.ts b/modules/ui/src/app/components/help-tip/help-tip.component.ts
index 6bfc5a627..444a82ee2 100644
--- a/modules/ui/src/app/components/help-tip/help-tip.component.ts
+++ b/modules/ui/src/app/components/help-tip/help-tip.component.ts
@@ -17,6 +17,7 @@ import {
ChangeDetectionStrategy,
Component,
HostListener,
+ inject,
input,
OnInit,
output,
@@ -25,6 +26,9 @@ import { CommonModule } from '@angular/common';
import { MatIconModule } from '@angular/material/icon';
import { MatButtonModule } from '@angular/material/button';
import { TipConfig } from '../../model/tip-config';
+import { LiveAnnouncer } from '@angular/cdk/a11y';
+import { FocusManagerService } from '../../services/focus-manager.service';
+import { timer } from 'rxjs/internal/observable/timer';
@Component({
selector: 'app-help-tip',
@@ -40,6 +44,8 @@ export class HelpTipComponent implements OnInit {
onAction = output();
onCLoseTip = output();
tipPosition = { top: 0, left: 0 };
+ private readonly liveAnnouncer = inject(LiveAnnouncer);
+ private readonly focusManagerService = inject(FocusManagerService);
@HostListener('window:resize')
onResize() {
@@ -48,6 +54,17 @@ export class HelpTipComponent implements OnInit {
ngOnInit() {
this.updateTipPosition(this.target());
+ this.liveAnnouncer.announce(
+ `${this.data()?.title} ${this.data()?.content}`
+ );
+ this.setFocus();
+ }
+
+ private setFocus(): void {
+ const helpTipEl = window.document.querySelector('.tip');
+ timer(200).subscribe(() => {
+ this.focusManagerService.focusFirstElementInContainer(helpTipEl);
+ });
}
updateTipPosition(target: HTMLElement | undefined | null) {
diff --git a/modules/ui/src/app/model/tip-config.ts b/modules/ui/src/app/model/tip-config.ts
index eab024c0d..e465f027d 100644
--- a/modules/ui/src/app/model/tip-config.ts
+++ b/modules/ui/src/app/model/tip-config.ts
@@ -38,4 +38,11 @@ export const HelpTips = {
position: 'right',
arrowPosition: 'left',
} as TipConfig,
+ step3: {
+ title: 'Step 3:',
+ content: 'You can now start your first test attempt your new device.',
+ action: 'Start Testrun',
+ position: 'right',
+ arrowPosition: 'left',
+ } as TipConfig,
};