Skip to content

Commit 87eee70

Browse files
committed
Merge remote-tracking branch 'contrib/refactor-menu-resolvers-7.6' into main_PR-menus
2 parents be6de64 + 3f7c42c commit 87eee70

98 files changed

Lines changed: 4975 additions & 2120 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/app/admin/admin-sidebar/admin-sidebar-section/admin-sidebar-section.component.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { MenuService } from '../../../shared/menu/menu.service';
1616
import { MenuID } from '../../../shared/menu/menu-id.model';
1717
import { LinkMenuItemModel } from '../../../shared/menu/menu-item/models/link.model';
1818
import { MenuSection } from '../../../shared/menu/menu-section.model';
19-
import { MenuSectionComponent } from '../../../shared/menu/menu-section/menu-section.component';
19+
import { AbstractMenuSectionComponent } from '../../../shared/menu/menu-section/abstract-menu-section.component';
2020
import { BrowserOnlyPipe } from '../../../shared/utils/browser-only.pipe';
2121

2222
/**
@@ -30,7 +30,7 @@ import { BrowserOnlyPipe } from '../../../shared/utils/browser-only.pipe';
3030
imports: [NgClass, RouterLink, TranslateModule, BrowserOnlyPipe],
3131

3232
})
33-
export class AdminSidebarSectionComponent extends MenuSectionComponent implements OnInit {
33+
export class AdminSidebarSectionComponent extends AbstractMenuSectionComponent implements OnInit {
3434

3535
/**
3636
* This section resides in the Admin Sidebar
@@ -44,16 +44,17 @@ export class AdminSidebarSectionComponent extends MenuSectionComponent implement
4444
isDisabled: boolean;
4545

4646
constructor(
47-
@Inject('sectionDataProvider') menuSection: MenuSection,
47+
@Inject('sectionDataProvider') protected section: MenuSection,
4848
protected menuService: MenuService,
4949
protected injector: Injector,
5050
protected router: Router,
5151
) {
52-
super(menuSection, menuService, injector);
53-
this.itemModel = menuSection.model as LinkMenuItemModel;
52+
super(menuService, injector);
53+
this.itemModel = section.model as LinkMenuItemModel;
5454
}
5555

5656
ngOnInit(): void {
57+
// todo: should support all menu entries?
5758
this.isDisabled = this.itemModel?.disabled || isEmpty(this.itemModel?.link);
5859
super.ngOnInit();
5960
}

src/app/admin/admin-sidebar/expandable-admin-sidebar-section/expandable-admin-sidebar-section.component.html

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
<div [ngClass]="{'expanded': (isExpanded$ | async)}"
2-
[@bgColor]="{
1+
<div *ngIf="hasSubSections$ | async"
2+
[ngClass]="{'expanded': (isExpanded$ | async)}"
3+
[@bgColor]="{
34
value: ((isExpanded$ | async) ? 'endBackground' : 'startBackground'),
45
params: {endColor: (sidebarActiveBg$ | async)}
56
}">
@@ -15,7 +16,7 @@
1516
href="javascript:void(0);"
1617
>
1718
<div class="sidebar-fixed-element-wrapper" data-test="sidebar-section-icon" aria-hidden="true">
18-
<i class="fas fa-{{section.icon}} fa-fw"></i>
19+
<i class="fas fa-{{section.icon ?? 'notdef'}} fa-fw"></i>
1920
</div>
2021
<div class="sidebar-collapsible-element-outer-wrapper">
2122
<div class="sidebar-collapsible-element-inner-wrapper sidebar-item toggler-wrapper">

src/app/admin/admin-sidebar/expandable-admin-sidebar-section/expandable-admin-sidebar-section.component.spec.ts

Lines changed: 79 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { TranslateModule } from '@ngx-translate/core';
1111
import { of as observableOf } from 'rxjs';
1212

1313
import { MenuService } from '../../../shared/menu/menu.service';
14+
import { MenuItemModels } from '../../../shared/menu/menu-section.model';
1415
import { CSSVariableService } from '../../../shared/sass-helper/css-variable.service';
1516
import { CSSVariableServiceStub } from '../../../shared/testing/css-variable-service.stub';
1617
import { MenuServiceStub } from '../../../shared/testing/menu-service.stub';
@@ -22,47 +23,92 @@ describe('ExpandableAdminSidebarSectionComponent', () => {
2223
let fixture: ComponentFixture<ExpandableAdminSidebarSectionComponent>;
2324
const menuService = new MenuServiceStub();
2425
const iconString = 'test';
25-
beforeEach(waitForAsync(() => {
26-
TestBed.configureTestingModule({
27-
imports: [NoopAnimationsModule, TranslateModule.forRoot(), ExpandableAdminSidebarSectionComponent, TestComponent],
28-
providers: [
29-
{ provide: 'sectionDataProvider', useValue: { icon: iconString, model: {} } },
30-
{ provide: MenuService, useValue: menuService },
31-
{ provide: CSSVariableService, useClass: CSSVariableServiceStub },
32-
{ provide: Router, useValue: new RouterStub() },
33-
],
34-
}).compileComponents();
35-
}));
3626

37-
beforeEach(() => {
38-
spyOn(menuService, 'getSubSectionsByParentID').and.returnValue(observableOf([]));
39-
fixture = TestBed.createComponent(ExpandableAdminSidebarSectionComponent);
40-
component = fixture.componentInstance;
41-
spyOn(component as any, 'getMenuItemComponent').and.returnValue(TestComponent);
42-
fixture.detectChanges();
43-
});
4427

45-
it('should create', () => {
46-
expect(component).toBeTruthy();
47-
});
28+
describe('when there are subsections', () => {
29+
beforeEach(waitForAsync(() => {
30+
TestBed.configureTestingModule({
31+
imports: [NoopAnimationsModule, TranslateModule.forRoot(), ExpandableAdminSidebarSectionComponent, TestComponent],
32+
providers: [
33+
{ provide: 'sectionDataProvider', useValue: { icon: iconString, model: {} } },
34+
{ provide: MenuService, useValue: menuService },
35+
{ provide: CSSVariableService, useClass: CSSVariableServiceStub },
36+
{ provide: Router, useValue: new RouterStub() },
37+
],
38+
}).compileComponents();
39+
}));
40+
41+
beforeEach(() => {
42+
spyOn(menuService, 'getSubSectionsByParentID').and.returnValue(observableOf([{
43+
id: 'test',
44+
visible: true,
45+
model: {} as MenuItemModels,
46+
}]));
47+
fixture = TestBed.createComponent(ExpandableAdminSidebarSectionComponent);
48+
component = fixture.componentInstance;
49+
spyOn(component as any, 'getMenuItemComponent').and.returnValue(TestComponent);
50+
fixture.detectChanges();
51+
});
52+
53+
it('should create', () => {
54+
expect(component).toBeTruthy();
55+
});
56+
57+
it('should set the right icon', () => {
58+
const icon = fixture.debugElement.query(By.css('[data-test="sidebar-section-icon"] > i.fas'));
59+
expect(icon.nativeElement.getAttribute('class')).toContain('fa-' + iconString);
60+
});
4861

49-
it('should set the right icon', () => {
50-
const icon = fixture.debugElement.query(By.css('[data-test="sidebar-section-icon"] > i.fas'));
51-
expect(icon.nativeElement.getAttribute('class')).toContain('fa-' + iconString);
62+
describe('when the header text is clicked', () => {
63+
beforeEach(() => {
64+
spyOn(menuService, 'toggleActiveSection');
65+
const sidebarToggler = fixture.debugElement.query(By.css('a.sidebar-section-wrapper'));
66+
sidebarToggler.triggerEventHandler('click', {
67+
preventDefault: () => {/**/
68+
},
69+
});
70+
});
71+
72+
it('should call toggleActiveSection on the menuService', () => {
73+
expect(menuService.toggleActiveSection).toHaveBeenCalled();
74+
});
75+
});
5276
});
5377

54-
describe('when the header text is clicked', () => {
78+
79+
describe('when there are no subsections', () => {
80+
beforeEach(waitForAsync(() => {
81+
TestBed.configureTestingModule({
82+
imports: [NoopAnimationsModule, TranslateModule.forRoot()],
83+
declarations: [ExpandableAdminSidebarSectionComponent, TestComponent],
84+
providers: [
85+
{ provide: 'sectionDataProvider', useValue: { icon: iconString, model: {} } },
86+
{ provide: MenuService, useValue: menuService },
87+
{ provide: CSSVariableService, useClass: CSSVariableServiceStub },
88+
{ provide: Router, useValue: new RouterStub() },
89+
],
90+
}).overrideComponent(ExpandableAdminSidebarSectionComponent, {
91+
})
92+
.compileComponents();
93+
}));
94+
5595
beforeEach(() => {
56-
spyOn(menuService, 'toggleActiveSection');
57-
const sidebarToggler = fixture.debugElement.query(By.css('a.sidebar-section-wrapper'));
58-
sidebarToggler.triggerEventHandler('click', {
59-
preventDefault: () => {/**/
60-
},
61-
});
96+
spyOn(menuService, 'getSubSectionsByParentID').and.returnValue(observableOf([]));
97+
fixture = TestBed.createComponent(ExpandableAdminSidebarSectionComponent);
98+
component = fixture.componentInstance;
99+
spyOn(component as any, 'getMenuItemComponent').and.returnValue(TestComponent);
100+
fixture.detectChanges();
101+
});
102+
103+
it('should create', () => {
104+
expect(component).toBeTruthy();
62105
});
63106

64-
it('should call toggleActiveSection on the menuService', () => {
65-
expect(menuService.toggleActiveSection).toHaveBeenCalled();
107+
it('should not contain a section', () => {
108+
const icon = fixture.debugElement.query(By.css('.shortcut-icon'));
109+
expect(icon).toBeNull();
110+
const sidebarToggler = fixture.debugElement.query(By.css('.sidebar-section'));
111+
expect(sidebarToggler).toBeNull();
66112
});
67113
});
68114
});

src/app/admin/admin-sidebar/expandable-admin-sidebar-section/expandable-admin-sidebar-section.component.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@ import { map } from 'rxjs/operators';
2222
import { bgColor } from '../../../shared/animations/bgColor';
2323
import { rotate } from '../../../shared/animations/rotate';
2424
import { slide } from '../../../shared/animations/slide';
25+
import { isNotEmpty } from '../../../shared/empty.util';
2526
import { MenuService } from '../../../shared/menu/menu.service';
2627
import { MenuID } from '../../../shared/menu/menu-id.model';
28+
import { MenuSection } from '../../../shared/menu/menu-section.model';
2729
import { CSSVariableService } from '../../../shared/sass-helper/css-variable.service';
2830
import { BrowserOnlyPipe } from '../../../shared/utils/browser-only.pipe';
2931
import { AdminSidebarSectionComponent } from '../admin-sidebar-section/admin-sidebar-section.component';
@@ -67,21 +69,30 @@ export class ExpandableAdminSidebarSectionComponent extends AdminSidebarSectionC
6769
*/
6870
isExpanded$: Observable<boolean>;
6971

72+
/**
73+
* Emits true when the top section has subsections, else emits false
74+
*/
75+
hasSubSections$: Observable<boolean>;
76+
77+
7078
constructor(
71-
@Inject('sectionDataProvider') menuSection,
79+
@Inject('sectionDataProvider') protected section: MenuSection,
7280
protected menuService: MenuService,
7381
private variableService: CSSVariableService,
7482
protected injector: Injector,
7583
protected router: Router,
7684
) {
77-
super(menuSection, menuService, injector, router);
85+
super(section, menuService, injector, router);
7886
}
7987

8088
/**
8189
* Set initial values for instance variables
8290
*/
8391
ngOnInit(): void {
8492
super.ngOnInit();
93+
this.hasSubSections$ = this.subSections$.pipe(
94+
map((subSections) => isNotEmpty(subSections)),
95+
);
8596
this.sidebarActiveBg$ = this.variableService.getVariable('--ds-admin-sidebar-active-bg');
8697
this.isSidebarCollapsed$ = this.menuService.isMenuCollapsed(this.menuID);
8798
this.isSidebarPreviewCollapsed$ = this.menuService.isMenuPreviewCollapsed(this.menuID);

src/app/app.menus.ts

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/**
2+
* The contents of this file are subject to the license and copyright
3+
* detailed in the LICENSE and NOTICE files at the root of the source
4+
* tree and available online at
5+
*
6+
* http://www.dspace.org/license/
7+
*/
8+
import { buildMenuStructure } from './shared/menu/menu.structure';
9+
import { MenuID } from './shared/menu/menu-id.model';
10+
import { MenuRoute } from './shared/menu/menu-route.model';
11+
import { AccessControlMenuProvider } from './shared/menu/providers/access-control.menu';
12+
import { AdminSearchMenuProvider } from './shared/menu/providers/admin-search.menu';
13+
import { BrowseMenuProvider } from './shared/menu/providers/browse.menu';
14+
import { SubscribeMenuProvider } from './shared/menu/providers/comcol-subscribe.menu';
15+
import { CommunityListMenuProvider } from './shared/menu/providers/community-list.menu';
16+
import { CurationMenuProvider } from './shared/menu/providers/curation.menu';
17+
import { DSpaceObjectEditMenuProvider } from './shared/menu/providers/dso-edit.menu';
18+
import { DsoOptionMenuProvider } from './shared/menu/providers/dso-option.menu';
19+
import { EditMenuProvider } from './shared/menu/providers/edit.menu';
20+
import { ExportMenuProvider } from './shared/menu/providers/export.menu';
21+
import { HealthMenuProvider } from './shared/menu/providers/health.menu';
22+
import { ImportMenuProvider } from './shared/menu/providers/import.menu';
23+
import { ClaimMenuProvider } from './shared/menu/providers/item-claim.menu';
24+
import { OrcidMenuProvider } from './shared/menu/providers/item-orcid.menu';
25+
import { VersioningMenuProvider } from './shared/menu/providers/item-versioning.menu';
26+
import { NewMenuProvider } from './shared/menu/providers/new.menu';
27+
import { ProcessesMenuProvider } from './shared/menu/providers/processes.menu';
28+
import { RegistriesMenuProvider } from './shared/menu/providers/registries.menu';
29+
import { StatisticsMenuProvider } from './shared/menu/providers/statistics.menu';
30+
import { SystemWideAlertMenuProvider } from './shared/menu/providers/system-wide-alert.menu';
31+
import { WorkflowMenuProvider } from './shared/menu/providers/workflow.menu';
32+
33+
export const MENUS = buildMenuStructure({
34+
[MenuID.PUBLIC]: [
35+
CommunityListMenuProvider,
36+
BrowseMenuProvider,
37+
StatisticsMenuProvider,
38+
],
39+
[MenuID.ADMIN]: [
40+
NewMenuProvider,
41+
EditMenuProvider,
42+
ImportMenuProvider,
43+
ExportMenuProvider,
44+
AccessControlMenuProvider,
45+
AdminSearchMenuProvider,
46+
RegistriesMenuProvider,
47+
CurationMenuProvider,
48+
ProcessesMenuProvider,
49+
WorkflowMenuProvider,
50+
HealthMenuProvider,
51+
SystemWideAlertMenuProvider,
52+
],
53+
[MenuID.DSO_EDIT]: [
54+
DsoOptionMenuProvider.withSubs([
55+
SubscribeMenuProvider.onRoute(
56+
MenuRoute.SIMPLE_COMMUNITY_PAGE,
57+
MenuRoute.SIMPLE_COLLECTION_PAGE,
58+
),
59+
DSpaceObjectEditMenuProvider.onRoute(
60+
MenuRoute.SIMPLE_COMMUNITY_PAGE,
61+
MenuRoute.SIMPLE_COLLECTION_PAGE,
62+
MenuRoute.SIMPLE_ITEM_PAGE,
63+
MenuRoute.FULL_ITEM_PAGE,
64+
),
65+
VersioningMenuProvider.onRoute(
66+
MenuRoute.SIMPLE_ITEM_PAGE,
67+
MenuRoute.FULL_ITEM_PAGE,
68+
),
69+
OrcidMenuProvider.onRoute(
70+
MenuRoute.SIMPLE_ITEM_PAGE,
71+
MenuRoute.FULL_ITEM_PAGE,
72+
),
73+
ClaimMenuProvider.onRoute(
74+
MenuRoute.SIMPLE_ITEM_PAGE,
75+
MenuRoute.FULL_ITEM_PAGE,
76+
),
77+
]),
78+
],
79+
});

src/app/init.service.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,10 @@ import { HeadTagService } from './core/metadata/head-tag.service';
4242
import { CorrelationIdService } from './correlation-id/correlation-id.service';
4343
import { dsDynamicFormControlMapFn } from './shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-map-fn';
4444
import { MenuService } from './shared/menu/menu.service';
45+
import { MenuProviderService } from './shared/menu/menu-provider.service';
4546
import { ThemeService } from './shared/theme-support/theme.service';
4647
import { Angulartics2DSpace } from './statistics/angulartics/dspace-provider';
4748

48-
4949
/**
5050
* Performs the initialization of the app.
5151
*
@@ -74,6 +74,7 @@ export abstract class InitService {
7474
protected breadcrumbsService: BreadcrumbsService,
7575
protected themeService: ThemeService,
7676
protected menuService: MenuService,
77+
protected menuProviderService: MenuProviderService,
7778

7879
) {
7980
}
@@ -216,7 +217,7 @@ export abstract class InitService {
216217
this.headTagService.listenForRouteChange();
217218
this.breadcrumbsService.listenForRouteChanges();
218219
this.themeService.listenForRouteChanges();
219-
this.menuService.listenForRouteChanges();
220+
this.menuProviderService.listenForRouteChanges();
220221
}
221222

222223
/**

src/app/item-page/item-page-routing-paths.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ export function getItemModuleRoute() {
88
return `/${ITEM_MODULE_PATH}`;
99
}
1010

11+
export const ENTITY_MODULE_PATH = 'entities';
12+
1113
/**
1214
* Get the route to an item's page
1315
* Depending on the item's entity type, the route will either start with /items or /entities

0 commit comments

Comments
 (0)