From ce024096ea67ccaacdec26a2d26e53e4b8c97c35 Mon Sep 17 00:00:00 2001
From: AleksanderSklorz <115619721+AleksanderSklorz@users.noreply.github.com>
Date: Fri, 24 Apr 2026 13:11:20 +0200
Subject: [PATCH] [ACS-11350] Fixed focusing after collapsing sidenav menu
(#5168)
* [ACS-11350] Fixed focusing after collapsing sidenav menu
* [ACS-11350] Addressed copilot comments
* [ACS-11350] Addressed copilot comments
* [ACS-11350] Addressed copilot comments
---
.../components/sidenav-header.component.html | 3 +-
.../sidenav-header.component.spec.ts | 83 +++++++++++++++++++
.../components/sidenav-header.component.ts | 13 ++-
.../page-layout/page-layout.component.html | 4 +-
.../page-layout/page-layout.component.spec.ts | 34 ++++++--
.../page-layout/page-layout.component.ts | 3 +-
6 files changed, 128 insertions(+), 12 deletions(-)
create mode 100644 projects/aca-content/src/lib/components/sidenav/components/sidenav-header.component.spec.ts
diff --git a/projects/aca-content/src/lib/components/sidenav/components/sidenav-header.component.html b/projects/aca-content/src/lib/components/sidenav/components/sidenav-header.component.html
index 68ae4ba54..0a83605fc 100644
--- a/projects/aca-content/src/lib/components/sidenav/components/sidenav-header.component.html
+++ b/projects/aca-content/src/lib/components/sidenav/components/sidenav-header.component.html
@@ -4,7 +4,8 @@
class="aca-sidenav-header-title-logo"
data-automation-id="app-sidenav-header-title-logo"
(click)="toggleNavBar.emit()"
- [attr.aria-label]="'APP.TOOLTIPS.COLLAPSE_NAVIGATION' | translate">
+ [attr.aria-label]="'APP.TOOLTIPS.COLLAPSE_NAVIGATION' | translate"
+ #toggleNavbarButton>
.
+ */
+
+import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
+import { SidenavHeaderComponent } from './sidenav-header.component';
+import { NoopAuthModule, NoopTranslateModule, UnitTestingUtils } from '@alfresco/adf-core';
+import { AppService } from '@alfresco/aca-shared';
+import { Subject } from 'rxjs';
+import { provideHttpClientTesting } from '@angular/common/http/testing';
+import { provideHttpClient } from '@angular/common/http';
+import { provideMockStore } from '@ngrx/store/testing';
+
+describe('SidenavHeaderComponent', () => {
+ let fixture: ComponentFixture;
+ let unitTestingUtils: UnitTestingUtils;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ imports: [SidenavHeaderComponent, NoopAuthModule, NoopTranslateModule],
+ providers: [
+ {
+ provide: AppService,
+ useValue: {
+ toggleAppNavBar$: new Subject()
+ }
+ },
+ provideHttpClient(),
+ provideHttpClientTesting(),
+ provideMockStore({
+ initialState: {
+ app: {
+ selection: {}
+ }
+ }
+ })
+ ]
+ });
+ fixture = TestBed.createComponent(SidenavHeaderComponent);
+ unitTestingUtils = new UnitTestingUtils(fixture.debugElement);
+ });
+
+ describe('Toggle navbar button', () => {
+ let toggleSidenavButton: Element;
+
+ beforeEach(() => {
+ document.body.focus();
+ fixture.detectChanges();
+ toggleSidenavButton = unitTestingUtils.getByCSS('.aca-sidenav-header-title-logo').nativeElement;
+ });
+
+ it('should be focused when toggleAppNavBar$ emits on AppService', fakeAsync(() => {
+ TestBed.inject(AppService).toggleAppNavBar$.next();
+ tick();
+
+ expect(toggleSidenavButton).toBe(document.activeElement);
+ }));
+
+ it('should not be focused when toggleAppNavBar$ does not emit on AppService', () => {
+ expect(toggleSidenavButton).not.toBe(document.activeElement);
+ });
+ });
+});
diff --git a/projects/aca-content/src/lib/components/sidenav/components/sidenav-header.component.ts b/projects/aca-content/src/lib/components/sidenav/components/sidenav-header.component.ts
index a34d3bc09..65c5a395e 100644
--- a/projects/aca-content/src/lib/components/sidenav/components/sidenav-header.component.ts
+++ b/projects/aca-content/src/lib/components/sidenav/components/sidenav-header.component.ts
@@ -22,9 +22,9 @@
* from Hyland Software. If not, see .
*/
-import { Component, DestroyRef, EventEmitter, inject, OnInit, Output, ViewEncapsulation } from '@angular/core';
+import { Component, DestroyRef, ElementRef, EventEmitter, inject, OnInit, Output, ViewChild, ViewEncapsulation } from '@angular/core';
import { ContentActionRef } from '@alfresco/adf-extensions';
-import { AppExtensionService, AppSettingsService, ToolbarComponent } from '@alfresco/aca-shared';
+import { AppExtensionService, AppService, AppSettingsService, ToolbarComponent } from '@alfresco/aca-shared';
import { CommonModule } from '@angular/common';
import { TranslatePipe } from '@ngx-translate/core';
import { RouterModule } from '@angular/router';
@@ -38,6 +38,7 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
host: { class: 'app-sidenav-header' }
})
export class SidenavHeaderComponent implements OnInit {
+ private readonly appService = inject(AppService);
private readonly appSettings = inject(AppSettingsService);
private readonly appExtensions = inject(AppExtensionService);
@@ -51,6 +52,9 @@ export class SidenavHeaderComponent implements OnInit {
@Output()
toggleNavBar = new EventEmitter();
+ @ViewChild('toggleNavbarButton')
+ private readonly toggleNavbarButton!: ElementRef;
+
ngOnInit() {
this.appExtensions
.getHeaderActions()
@@ -58,5 +62,10 @@ export class SidenavHeaderComponent implements OnInit {
.subscribe((actions) => {
this.actions = actions;
});
+ this.appService.toggleAppNavBar$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => {
+ setTimeout(() => {
+ this.toggleNavbarButton.nativeElement.focus();
+ });
+ });
}
}
diff --git a/projects/aca-shared/src/lib/components/page-layout/page-layout.component.html b/projects/aca-shared/src/lib/components/page-layout/page-layout.component.html
index 509d4b572..c180fc516 100644
--- a/projects/aca-shared/src/lib/components/page-layout/page-layout.component.html
+++ b/projects/aca-shared/src/lib/components/page-layout/page-layout.component.html
@@ -3,7 +3,9 @@
mat-icon-button
class="aca-content-header-button"
(click)="toggleClick()"
- title="{{'APP.TOOLTIPS.EXPAND_NAVIGATION' | translate}}">
+ [attr.aria-label]="'APP.TOOLTIPS.EXPAND_NAVIGATION' | translate"
+ title="{{'APP.TOOLTIPS.EXPAND_NAVIGATION' | translate}}"
+ adf-auto-focus>
keyboard_double_arrow_right
diff --git a/projects/aca-shared/src/lib/components/page-layout/page-layout.component.spec.ts b/projects/aca-shared/src/lib/components/page-layout/page-layout.component.spec.ts
index 6819a85ab..ca06fdb3b 100644
--- a/projects/aca-shared/src/lib/components/page-layout/page-layout.component.spec.ts
+++ b/projects/aca-shared/src/lib/components/page-layout/page-layout.component.spec.ts
@@ -26,10 +26,14 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { PageLayoutComponent } from './page-layout.component';
import { AppService } from '../../services/app.service';
import { BehaviorSubject, Subject } from 'rxjs';
+import { NoopTranslateModule, UnitTestingUtils } from '@alfresco/adf-core';
+import { AutoFocusDirective } from '@alfresco/adf-content-services';
+import { DebugElement } from '@angular/core';
describe('PageLayoutComponent', () => {
let fixture: ComponentFixture;
- let component: PageLayoutComponent;
+ let unitTestingUtils: UnitTestingUtils;
+
const appServiceMock = {
toggleAppNavBar$: new Subject(),
appNavNarMode$: new BehaviorSubject<'collapsed' | 'expanded'>('expanded')
@@ -37,7 +41,7 @@ describe('PageLayoutComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
- imports: [PageLayoutComponent],
+ imports: [PageLayoutComponent, NoopTranslateModule],
providers: [
{
provide: AppService,
@@ -46,12 +50,28 @@ describe('PageLayoutComponent', () => {
]
});
fixture = TestBed.createComponent(PageLayoutComponent);
- component = fixture.componentInstance;
+ unitTestingUtils = new UnitTestingUtils(fixture.debugElement);
});
- it('should toggle the appService toggleAppNavBar$ Subject', () => {
- spyOn(appServiceMock.toggleAppNavBar$, 'next');
- component.toggleClick();
- expect(appServiceMock.toggleAppNavBar$.next).toHaveBeenCalled();
+ describe('Expand button', () => {
+ let expandButton: DebugElement;
+
+ beforeEach(() => {
+ appServiceMock.appNavNarMode$.next('collapsed');
+ fixture.detectChanges();
+ expandButton = unitTestingUtils.getByCSS('.aca-content-header-button');
+ });
+
+ it('should toggle the appService toggleAppNavBar$ Subject', () => {
+ spyOn(appServiceMock.toggleAppNavBar$, 'next');
+
+ expandButton.nativeElement.click();
+ expect(appServiceMock.toggleAppNavBar$.next).toHaveBeenCalled();
+ });
+
+ it('should have AutoFocusDirective', () => {
+ fixture.detectChanges();
+ expect(expandButton.injector.get(AutoFocusDirective, null)).not.toBeNull();
+ });
});
});
diff --git a/projects/aca-shared/src/lib/components/page-layout/page-layout.component.ts b/projects/aca-shared/src/lib/components/page-layout/page-layout.component.ts
index 68cfc216f..00856fc4b 100644
--- a/projects/aca-shared/src/lib/components/page-layout/page-layout.component.ts
+++ b/projects/aca-shared/src/lib/components/page-layout/page-layout.component.ts
@@ -30,9 +30,10 @@ import { TranslatePipe } from '@ngx-translate/core';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
+import { AutoFocusDirective } from '@alfresco/adf-content-services';
@Component({
- imports: [CommonModule, TranslatePipe, MatButtonModule, MatIconModule],
+ imports: [CommonModule, TranslatePipe, MatButtonModule, MatIconModule, AutoFocusDirective],
selector: 'aca-page-layout',
templateUrl: './page-layout.component.html',
styleUrls: ['./page-layout.component.scss'],