Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,23 +1,49 @@
<div>
<a class="sidebar-section-wrapper"
[ngClass]="{ disabled: isDisabled }"
role="menuitem"
[attr.aria-disabled]="isDisabled"
[attr.aria-labelledby]="adminMenuSectionTitleAccessibilityHandle(section)"
[routerLink]="itemModel.link"
(keyup.space)="navigate($event)"
(keyup.enter)="navigate($event)"
href="javascript:void(0);"
>
<div class="sidebar-fixed-element-wrapper" data-test="sidebar-section-icon" aria-hidden="true">
<i class="fas fa-{{section.icon}} fa-fw"></i>
</div>
<div class="sidebar-collapsible-element-outer-wrapper">
<div class="sidebar-collapsible-element-inner-wrapper sidebar-item">
<span [id]="adminMenuSectionTitleAccessibilityHandle(section)" [attr.data-test]="adminMenuSectionTitleAccessibilityHandle(section) | dsBrowserOnly">
{{itemModel.text | translate}}
</span>
@if (isExternalLink) {
<a class="sidebar-section-wrapper"
[ngClass]="{ disabled: isDisabled }"
role="menuitem"
[attr.aria-disabled]="isDisabled"
[attr.aria-labelledby]="adminMenuSectionTitleAccessibilityHandle(section)"
[href]="itemModel.href"
target="_blank"
rel="noopener noreferrer"
(keyup.space)="navigate($event)"
(keyup.enter)="navigate($event)"
>
<div class="sidebar-fixed-element-wrapper" data-test="sidebar-section-icon" aria-hidden="true">
<i class="fas fa-{{section.icon}} fa-fw"></i>
</div>
</div>
</a>
<div class="sidebar-collapsible-element-outer-wrapper">
<div class="sidebar-collapsible-element-inner-wrapper sidebar-item">
<span [id]="adminMenuSectionTitleAccessibilityHandle(section)" [attr.data-test]="adminMenuSectionTitleAccessibilityHandle(section) | dsBrowserOnly">
{{itemModel.text | translate}}
<i class="fas fa-external-link fa-xs ml-1" aria-hidden="true"></i>
</span>
</div>
</div>
</a>
} @else {
<a class="sidebar-section-wrapper"
[ngClass]="{ disabled: isDisabled }"
role="menuitem"
[attr.aria-disabled]="isDisabled"
[attr.aria-labelledby]="adminMenuSectionTitleAccessibilityHandle(section)"
[routerLink]="itemModel.link"
(keyup.space)="navigate($event)"
(keyup.enter)="navigate($event)"
href="javascript:void(0);"
>
<div class="sidebar-fixed-element-wrapper" data-test="sidebar-section-icon" aria-hidden="true">
<i class="fas fa-{{section.icon}} fa-fw"></i>
</div>
<div class="sidebar-collapsible-element-outer-wrapper">
<div class="sidebar-collapsible-element-inner-wrapper sidebar-item">
<span [id]="adminMenuSectionTitleAccessibilityHandle(section)" [attr.data-test]="adminMenuSectionTitleAccessibilityHandle(section) | dsBrowserOnly">
{{itemModel.text | translate}}
</span>
</div>
</div>
</a>
}
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { CSSVariableServiceStub } from '@dspace/core/testing/css-variable-servic
import { TranslateModule } from '@ngx-translate/core';

import { MenuService } from '../../../shared/menu/menu.service';
import { MenuItemType } from '../../../shared/menu/menu-item-type.model';
import { MenuSection } from '../../../shared/menu/menu-section.model';
import { MenuServiceStub } from '../../../shared/menu/menu-service.stub';
import { CSSVariableService } from '../../../shared/sass-helper/css-variable.service';
Expand All @@ -28,8 +29,18 @@ describe('AdminSidebarSectionComponent', () => {

beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
imports: [NoopAnimationsModule, RouterModule.forRoot([]), TranslateModule.forRoot(), AdminSidebarSectionComponent, TestComponent],
imports: [
NoopAnimationsModule,
RouterModule.forRoot([]),
TranslateModule.forRoot(),
AdminSidebarSectionComponent,
TestComponent,
],
providers: [
{
provide: 'sectionDataProvider',
useValue: { model: { link: 'google.com' }, icon: iconString },
},
{ provide: MenuService, useValue: menuService },
{ provide: CSSVariableService, useClass: CSSVariableServiceStub },
{ provide: ThemeService, useValue: getMockThemeService() },
Expand All @@ -56,21 +67,42 @@ describe('AdminSidebarSectionComponent', () => {
});

it('should set the right icon', () => {
const icon = fixture.debugElement.query(By.css('[data-test="sidebar-section-icon"]')).query(By.css('i.fas'));
const icon = fixture.debugElement
.query(By.css('[data-test="sidebar-section-icon"]'))
.query(By.css('i.fas'));
expect(icon.nativeElement.getAttribute('class')).toContain('fa-' + iconString);
});

it('should not contain the disabled class', () => {
const disabled = fixture.debugElement.query(By.css('.disabled'));
expect(disabled).toBeFalsy();
});

it('should navigate on keypress', () => {
const routerSpy = spyOn((component as any).router, 'navigate');
const event = { preventDefault: jasmine.createSpy() };
component.navigate(event);
expect(routerSpy).toHaveBeenCalled();
});

});

describe('when disabled', () => {

beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
imports: [NoopAnimationsModule, RouterModule.forRoot([]), TranslateModule.forRoot(), AdminSidebarSectionComponent, TestComponent],
imports: [
NoopAnimationsModule,
RouterModule.forRoot([]),
TranslateModule.forRoot(),
AdminSidebarSectionComponent,
TestComponent,
],
providers: [
{
provide: 'sectionDataProvider',
useValue: { model: { link: 'google.com', disabled: true }, icon: iconString },
},
{ provide: MenuService, useValue: menuService },
{ provide: CSSVariableService, useClass: CSSVariableServiceStub },
{ provide: ThemeService, useValue: getMockThemeService() },
Expand Down Expand Up @@ -98,24 +130,101 @@ describe('AdminSidebarSectionComponent', () => {
});

it('should set the right icon', () => {
const icon = fixture.debugElement.query(By.css('[data-test="sidebar-section-icon"]')).query(By.css('i.fas'));
const icon = fixture.debugElement
.query(By.css('[data-test="sidebar-section-icon"]'))
.query(By.css('i.fas'));
expect(icon.nativeElement.getAttribute('class')).toContain('fa-' + iconString);
});

it('should contain the disabled class', () => {
const disabled = fixture.debugElement.query(By.css('.disabled'));
expect(disabled).toBeTruthy();
});

it('should not navigate when disabled', () => {
const routerSpy = spyOn((component as any).router, 'navigate');
spyOn(window, 'open');
const event = { preventDefault: jasmine.createSpy() };
component.navigate(event);
expect(routerSpy).not.toHaveBeenCalled();
expect(window.open).not.toHaveBeenCalled();
});

});

describe('when external link', () => {

beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
imports: [
NoopAnimationsModule,
RouterModule.forRoot([]),
TranslateModule.forRoot(),
AdminSidebarSectionComponent,
TestComponent,
],
providers: [
{
provide: 'sectionDataProvider',
useValue: {
model: {
type: MenuItemType.EXTERNAL,
href: 'https://test.com',
},
icon: iconString,
},
},
{ provide: MenuService, useValue: menuService },
{ provide: CSSVariableService, useClass: CSSVariableServiceStub },
{ provide: ThemeService, useValue: getMockThemeService() },
],
}).compileComponents();
}));

beforeEach(() => {
fixture = TestBed.createComponent(AdminSidebarSectionComponent);
component = fixture.componentInstance;
component.section = {
model: {
type: MenuItemType.EXTERNAL,
href: 'https://test.com',
},
icon: iconString,
} as MenuSection;
spyOn(component as any, 'getMenuItemComponent').and.returnValue(Promise.resolve(TestComponent));
fixture.detectChanges();
});

it('should detect external link', () => {
expect(component.isExternalLink).toBeTrue();
});

it('should render external link icon', () => {
const icon = fixture.debugElement.query(By.css('.fa-external-link'));
expect(icon).toBeTruthy();
});

it('should not be disabled when external href exists', () => {
expect(component.isDisabled).toBeFalse();
});

it('should open external link on navigate', () => {
spyOn(window, 'open');
const event = { preventDefault: jasmine.createSpy() };
component.navigate(event);
expect(window.open).toHaveBeenCalledWith('https://test.com', '_blank');
});

});

});

// declare a test component
// test component
@Component({
selector: 'ds-test-cmp',
template: ``,
imports: [
RouterModule,
],
})
class TestComponent {
}
class TestComponent {}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ import { TranslateModule } from '@ngx-translate/core';

import { MenuService } from '../../../shared/menu/menu.service';
import { MenuID } from '../../../shared/menu/menu-id.model';
import { ExternalLinkMenuItemModel } from '../../../shared/menu/menu-item/models/external-link.model';
import { LinkMenuItemModel } from '../../../shared/menu/menu-item/models/link.model';
import { MenuItemType } from '../../../shared/menu/menu-item-type.model';
import { rendersSectionForMenu } from '../../../shared/menu/menu-section.decorator';
import { MenuSection } from '../../../shared/menu/menu-section.model';
import { AbstractMenuSectionComponent } from '../../../shared/menu/menu-section/abstract-menu-section.component';
Expand Down Expand Up @@ -48,6 +51,11 @@ export class AdminSidebarSectionComponent extends AbstractMenuSectionComponent i
*/
isDisabled: boolean;

/**
* Whether this section links to an external URL
*/
isExternalLink: boolean;

constructor(
protected menuService: MenuService,
protected injector: Injector,
Expand All @@ -62,15 +70,24 @@ export class AdminSidebarSectionComponent extends AbstractMenuSectionComponent i
}

ngOnInit(): void {
// todo: should support all menu entries?
this.isDisabled = this.itemModel?.disabled || isEmpty(this.itemModel?.link);
this.isExternalLink = this.section.model.type === MenuItemType.EXTERNAL;
if (this.isExternalLink) {
this.itemModel = this.section.model as ExternalLinkMenuItemModel;
} else {
this.itemModel = this.section.model as LinkMenuItemModel;
}
this.isDisabled = this.itemModel?.disabled || isEmpty(this.isExternalLink ? (this.itemModel as ExternalLinkMenuItemModel)?.href : this.itemModel?.link);
super.ngOnInit();
}

navigate(event: any): void {
event.preventDefault();
if (!this.isDisabled) {
this.router.navigate(this.itemModel.link);
if (this.isExternalLink) {
window.open((this.itemModel as ExternalLinkMenuItemModel).href, '_blank');
} else {
this.router.navigate(this.itemModel.link);
}
}
}

Expand Down
7 changes: 5 additions & 2 deletions src/app/app.menus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { SubscribeMenuProvider } from './shared/menu/providers/comcol-subscribe.
import { CommunityListMenuProvider } from './shared/menu/providers/community-list.menu';
import { CreateReportMenuProvider } from './shared/menu/providers/create-report.menu';
import { CurationMenuProvider } from './shared/menu/providers/curation.menu';
import { DocumentationMenuProvider } from './shared/menu/providers/documentation.menu';
import { DSpaceObjectEditMenuProvider } from './shared/menu/providers/dso-edit.menu';
import { DsoOptionMenuProvider } from './shared/menu/providers/dso-option.menu';
import { EditMenuProvider } from './shared/menu/providers/edit.menu';
Expand Down Expand Up @@ -65,6 +66,8 @@ export const MENUS = buildMenuStructure({
[MenuID.ADMIN]: [
NewMenuProvider,
EditMenuProvider,
EditCMSMetadataMenuProvider,
EditUserAgreementMenuProvider,
ImportMenuProvider,
ExportMenuProvider,
NotificationsMenuProvider,
Expand All @@ -79,8 +82,7 @@ export const MENUS = buildMenuStructure({
SystemWideAlertMenuProvider,
CoarNotifyMenuProvider,
AuditOverviewMenuProvider,
EditCMSMetadataMenuProvider,
EditUserAgreementMenuProvider,
Comment on lines -82 to -83

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't tested this PR, but I noticed this PR removed these 2 providers, could you please restore them?

DocumentationMenuProvider,
],
[MenuID.DSO_EDIT]: [
DsoOptionMenuProvider.withSubs([
Expand Down Expand Up @@ -119,3 +121,4 @@ export const MENUS = buildMenuStructure({
]),
],
});

Loading
Loading