Skip to content

Commit 1b6ddb4

Browse files
fix #5488: add ARIA labeling to hide edit menu from screen readers when user has no permissions (#5514)
1 parent bd203c9 commit 1b6ddb4

3 files changed

Lines changed: 82 additions & 23 deletions

File tree

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1-
<div class="dso-edit-menu d-flex" role="menubar">
1+
<div class="dso-edit-menu d-flex"
2+
[attr.role]="(menuVisibleWithSections$ | async) ? 'menubar' : null"
3+
[attr.aria-hidden]="(menuVisibleWithSections$ | async) === false ? 'true' : null">
24
@for (section of (sections | async); track section) {
35
<div class="ms-1">
46
<ng-container
5-
*ngComponentOutlet="(sectionMap$ | async).get(section.id)?.component; injector: (sectionMap$ | async).get(section.id)?.injector;"></ng-container>
7+
*ngComponentOutlet="(sectionMap$ | async).get(section.id)?.component; injector: (sectionMap$ | async).get(section.id)?.injector;">
8+
</ng-container>
69
</div>
710
}
811
</div>

src/app/shared/dso-page/dso-edit-menu/dso-edit-menu.component.spec.ts

Lines changed: 46 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,25 @@ describe('DsoEditMenuComponent', () => {
4545
index: 1,
4646
};
4747

48+
const subSection = {
49+
id: 'edit-dso-sub',
50+
active: false,
51+
visible: true,
52+
model: {
53+
text: 'sub-section-text',
54+
type: null,
55+
disabled: false,
56+
} as TextMenuItemModel,
57+
icon: 'pencil',
58+
index: 0,
59+
};
4860

4961
beforeEach(waitForAsync(() => {
5062
authorizationService = jasmine.createSpyObj('authorizationService', {
5163
isAuthorized: of(true),
5264
});
5365
spyOn(menuService, 'getMenuTopSections').and.returnValue(of([section]));
66+
spyOn(menuService, 'getSubSectionsByParentID').and.returnValue(of([subSection]));
5467
TestBed.configureTestingModule({
5568
imports: [TranslateModule.forRoot(), RouterTestingModule, DsoEditMenuComponent],
5669
providers: [
@@ -65,17 +78,42 @@ describe('DsoEditMenuComponent', () => {
6578
}).compileComponents();
6679
}));
6780

68-
beforeEach(() => {
69-
fixture = TestBed.createComponent(DsoEditMenuComponent);
70-
comp = fixture.componentInstance;
71-
comp.sections = of([]);
72-
fixture.detectChanges();
73-
});
74-
7581
describe('onInit', () => {
7682
it('should create', () => {
83+
fixture = TestBed.createComponent(DsoEditMenuComponent);
84+
comp = fixture.componentInstance;
85+
fixture.detectChanges();
7786
expect(comp).toBeTruthy();
7887
});
88+
89+
it('should have role menubar when subsections exist', () => {
90+
(menuService.getSubSectionsByParentID as jasmine.Spy).and.returnValue(of([subSection]));
91+
fixture = TestBed.createComponent(DsoEditMenuComponent);
92+
comp = fixture.componentInstance;
93+
fixture.detectChanges();
94+
95+
const menu = fixture.nativeElement.querySelector('.dso-edit-menu');
96+
expect(menu.getAttribute('role')).toBe('menubar');
97+
});
98+
99+
it('should NOT have role menubar when no subsections exist', () => {
100+
(menuService.getSubSectionsByParentID as jasmine.Spy).and.returnValue(of([]));
101+
fixture = TestBed.createComponent(DsoEditMenuComponent);
102+
comp = fixture.componentInstance;
103+
fixture.detectChanges();
104+
105+
const menu = fixture.nativeElement.querySelector('.dso-edit-menu');
106+
expect(menu.getAttribute('role')).toBeNull();
107+
});
108+
109+
it('should have aria-hidden when no subsections exist', () => {
110+
(menuService.getSubSectionsByParentID as jasmine.Spy).and.returnValue(of([]));
111+
fixture = TestBed.createComponent(DsoEditMenuComponent);
112+
comp = fixture.componentInstance;
113+
fixture.detectChanges();
114+
115+
const menu = fixture.nativeElement.querySelector('.dso-edit-menu');
116+
expect(menu.getAttribute('aria-hidden')).toBe('true');
117+
});
79118
});
80119
});
81-

src/app/shared/dso-page/dso-edit-menu/dso-edit-menu.component.ts

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,21 @@ import {
88
} from '@angular/core';
99
import { ActivatedRoute } from '@angular/router';
1010
import { AuthorizationDataService } from '@dspace/core/data/feature-authorization/authorization-data.service';
11+
import {
12+
combineLatest,
13+
Observable,
14+
of,
15+
} from 'rxjs';
16+
import {
17+
map,
18+
switchMap,
19+
} from 'rxjs/operators';
1120

1221
import { MenuComponent } from '../../menu/menu.component';
1322
import { MenuService } from '../../menu/menu.service';
1423
import { MenuID } from '../../menu/menu-id.model';
1524
import { ThemeService } from '../../theme-support/theme.service';
1625

17-
/**
18-
* Component representing the edit menu and other menus on the dspace object pages
19-
*/
2026
@Component({
2127
selector: 'ds-dso-edit-menu',
2228
styleUrls: ['./dso-edit-menu.component.scss'],
@@ -27,20 +33,32 @@ import { ThemeService } from '../../theme-support/theme.service';
2733
],
2834
})
2935
export class DsoEditMenuComponent extends MenuComponent {
30-
/**
31-
* The menu ID of this component is DSO_EDIT
32-
* @type {MenuID.DSO_EDIT}
33-
*/
36+
3437
menuID = MenuID.DSO_EDIT;
3538

39+
menuVisibleWithSections$: Observable<boolean>;
3640

37-
constructor(protected menuService: MenuService,
38-
protected injector: Injector,
39-
public authorizationService: AuthorizationDataService,
40-
public route: ActivatedRoute,
41-
protected themeService: ThemeService,
41+
constructor(
42+
protected menuService: MenuService,
43+
protected injector: Injector,
44+
public authorizationService: AuthorizationDataService,
45+
public route: ActivatedRoute,
46+
protected themeService: ThemeService,
4247
) {
4348
super(menuService, injector, authorizationService, route, themeService);
49+
this.menuVisibleWithSections$ = this.menuService.getMenuTopSections(MenuID.DSO_EDIT).pipe(
50+
switchMap((sections) => {
51+
if (sections.length === 0) {return of(false);}
52+
return combineLatest(
53+
sections.map((section) =>
54+
this.menuService.getSubSectionsByParentID(MenuID.DSO_EDIT, section.id).pipe(
55+
map((subSections) => subSections.length > 0),
56+
),
57+
),
58+
).pipe(
59+
map((results) => results.some((hasVisible) => hasVisible)),
60+
);
61+
}),
62+
);
4463
}
45-
4664
}

0 commit comments

Comments
 (0)