diff --git a/package.json b/package.json
index 6aead3664a..fd037de3f7 100644
--- a/package.json
+++ b/package.json
@@ -131,6 +131,7 @@
"devDependencies": {
"@angular/compiler": "^6.1.4",
"@angular/compiler-cli": "^6.1.4",
+ "@fortawesome/fontawesome-free": "^5.5.0",
"@ngrx/entity": "^6.1.0",
"@ngrx/schematics": "^6.1.0",
"@ngrx/store-devtools": "^6.1.0",
@@ -183,7 +184,7 @@
"karma-webdriver-launcher": "1.0.5",
"karma-webpack": "3.0.0",
"ngrx-store-freeze": "^0.2.4",
- "node-sass": "^4.7.2",
+ "node-sass": "^4.11.0",
"nodemon": "^1.15.0",
"npm-run-all": "4.1.3",
"postcss": "^7.0.2",
diff --git a/resources/i18n/en.json b/resources/i18n/en.json
index 7223cb739b..6b0c028a37 100644
--- a/resources/i18n/en.json
+++ b/resources/i18n/en.json
@@ -62,6 +62,9 @@
},
"sub-collection-list": {
"head": "Collections of this Community"
+ },
+ "sub-community-list": {
+ "head": "Communities of this Community"
}
},
"item": {
@@ -183,9 +186,19 @@
}
},
"nav": {
- "home": "Home",
+ "browse": {
+ "header": "All of DSpace"
+ },
+ "community-browse": {
+ "header": "By Community"
+ },
+ "statistics": {
+ "header": "Statistics"
+ },
"login": "Log In",
- "logout": "Log Out"
+ "logout": "Log Out",
+ "language": "Language switch",
+ "search": "Search"
},
"pagination": {
"results-per-page": "Results Per Page",
@@ -341,12 +354,92 @@
}
}
},
+ "menu": {
+ "header": {
+ "admin": "Admin",
+ "image": {
+ "logo": "Repository logo"
+ }
+ },
+ "section": {
+ "pin": "Pin sidebar",
+ "unpin": "Unpin sidebar",
+ "new": "New",
+ "new_community": "Community",
+ "new_collection": "Collection",
+ "new_item": "Item",
+ "new_item_version": "Item Version",
+ "edit": "Edit",
+ "edit_community": "Community",
+ "edit_collection": "Collection",
+ "edit_item": "Item",
+ "import": "Import",
+ "import_metadata": "Metadata",
+ "import_batch": "Batch Import (ZIP)",
+ "export": "Export",
+ "export_community": "Community",
+ "export_collection": "Collection",
+ "export_item": "Item",
+ "export_metadata": "Metadata",
+ "access_control": "Access Control",
+ "access_control_people": "People",
+ "access_control_groups": "Groups",
+ "access_control_authorizations": "Authorizations",
+ "find": "Find",
+ "find_items": "Items",
+ "find_withdrawn_items": "Withdrawn Items",
+ "find_private_items": "Private Items",
+ "registries": "Registries",
+ "registries_metadata": "Metadata",
+ "registries_format": "Format",
+ "curation_task": "Curation Task",
+ "statistics_task": "Statistics Task",
+ "control_panel": "Control Panel",
+ "browse_global": "All of DSpace",
+ "browse_global_communities_and_collections": "Communities & Collections",
+ "browse_global_by_issue_date": "By Issue Date",
+ "browse_global_by_author": "By Author",
+ "browse_global_by_title": "By Title",
+ "statistics": "Statistics",
+ "browse_community": "This Community",
+ "browse_community_by_issue_date": "By Issue Date",
+ "browse_community_by_author": "By Author",
+ "browse_community_by_title": "By Title",
+ "icon": {
+ "pin": "Pin sidebar",
+ "unpin": "Unpin sidebar",
+ "new": "New menu section",
+ "edit": "Edit menu section",
+ "import": "Import menu section",
+ "export": "Export menu section",
+ "access_control": "Access Control menu section",
+ "find": "Find menu section",
+ "registries": "Registries menu section",
+ "curation_task": "Curation Task menu section",
+ "statistics_task": "Statistics Task menu section",
+ "control_panel": "Control Panel menu section"
+ },
+ "toggle": {
+ "new": "Toggle New section",
+ "edit": "Toggle Edit section",
+ "import": "Toggle Import section",
+ "export": "Toggle Export section",
+ "access_control": "Toggle Access Control section",
+ "find": "Toggle Find section",
+ "registries": "Toggle Registries section",
+ "curation_task": "Toggle Curation Task section",
+ "statistics_task": "Toggle Statistics Task section",
+ "control_panel": "Toggle Control Panel section"
+ }
+ }
+ },
"loading": {
"default": "Loading...",
"top-level-communities": "Loading top-level communities...",
"community": "Loading community...",
"collection": "Loading collection...",
"sub-collections": "Loading sub-collections...",
+ "sub-communities": "Loading sub-communities...",
"recent-submissions": "Loading recent submissions...",
"item": "Loading item...",
"objects": "Loading...",
@@ -359,6 +452,7 @@
"community": "Error fetching community",
"collection": "Error fetching collection",
"sub-collections": "Error fetching sub-collections",
+ "sub-communities": "Error fetching sub-communities",
"recent-submissions": "Error fetching recent submissions",
"item": "Error fetching item",
"objects": "Error fetching objects",
@@ -411,5 +505,8 @@
"errors": {
"invalid-user": "Invalid email address or password."
}
+ },
+ "chips": {
+ "remove": "Remove chip"
}
}
diff --git a/resources/images/dspace-logo-mini.svg b/resources/images/dspace-logo-mini.svg
new file mode 100644
index 0000000000..6ca41addc8
--- /dev/null
+++ b/resources/images/dspace-logo-mini.svg
@@ -0,0 +1,23 @@
+
+
+
+
diff --git a/resources/images/dspace-logo.svg b/resources/images/dspace-logo.svg
new file mode 100644
index 0000000000..60df1ed46d
--- /dev/null
+++ b/resources/images/dspace-logo.svg
@@ -0,0 +1,37 @@
+
+
+
+
diff --git a/src/app/+admin/admin-registries/metadata-schema/metadata-schema.component.ts b/src/app/+admin/admin-registries/metadata-schema/metadata-schema.component.ts
index b2cc5129ce..6c5e01b37d 100644
--- a/src/app/+admin/admin-registries/metadata-schema/metadata-schema.component.ts
+++ b/src/app/+admin/admin-registries/metadata-schema/metadata-schema.component.ts
@@ -7,7 +7,6 @@ import { PaginatedList } from '../../../core/data/paginated-list';
import { MetadataField } from '../../../core/metadata/metadatafield.model';
import { MetadataSchema } from '../../../core/metadata/metadataschema.model';
import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
-import { SortOptions } from '../../../core/cache/models/sort-options.model';
@Component({
selector: 'ds-metadata-schema',
diff --git a/src/app/+admin/admin-sidebar/admin-sidebar-section/admin-sidebar-section.component.html b/src/app/+admin/admin-sidebar/admin-sidebar-section/admin-sidebar-section.component.html
new file mode 100644
index 0000000000..e72a17aac1
--- /dev/null
+++ b/src/app/+admin/admin-sidebar/admin-sidebar-section/admin-sidebar-section.component.html
@@ -0,0 +1,11 @@
+
\ No newline at end of file
diff --git a/src/app/+admin/admin-sidebar/admin-sidebar-section/admin-sidebar-section.component.scss b/src/app/+admin/admin-sidebar/admin-sidebar-section/admin-sidebar-section.component.scss
new file mode 100644
index 0000000000..88eb98509a
--- /dev/null
+++ b/src/app/+admin/admin-sidebar/admin-sidebar-section/admin-sidebar-section.component.scss
@@ -0,0 +1 @@
+@import '../../../../styles/variables.scss';
\ No newline at end of file
diff --git a/src/app/+admin/admin-sidebar/admin-sidebar-section/admin-sidebar-section.component.spec.ts b/src/app/+admin/admin-sidebar/admin-sidebar-section/admin-sidebar-section.component.spec.ts
new file mode 100644
index 0000000000..30c57c17ea
--- /dev/null
+++ b/src/app/+admin/admin-sidebar/admin-sidebar-section/admin-sidebar-section.component.spec.ts
@@ -0,0 +1,60 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { MenuService } from '../../../shared/menu/menu.service';
+import { MenuServiceStub } from '../../../shared/testing/menu-service-stub';
+import { CSSVariableService } from '../../../shared/sass-helper/sass-helper.service';
+import { CSSVariableServiceStub } from '../../../shared/testing/css-variable-service-stub';
+import { Component } from '@angular/core';
+import { NoopAnimationsModule } from '@angular/platform-browser/animations';
+import { AdminSidebarSectionComponent } from './admin-sidebar-section.component';
+import { RouterTestingModule } from '@angular/router/testing';
+import { By } from '@angular/platform-browser';
+import { TranslateModule } from '@ngx-translate/core';
+
+describe('AdminSidebarSectionComponent', () => {
+ let component: AdminSidebarSectionComponent;
+ let fixture: ComponentFixture;
+ const menuService = new MenuServiceStub();
+ const iconString = 'test';
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ imports: [NoopAnimationsModule, RouterTestingModule, TranslateModule.forRoot()],
+ declarations: [AdminSidebarSectionComponent, TestComponent],
+ providers: [
+ { provide: 'sectionDataProvider', useValue: { model: { link: 'google.com' }, icon: iconString } },
+ { provide: MenuService, useValue: menuService },
+ { provide: CSSVariableService, useClass: CSSVariableServiceStub },
+ ]
+ }).overrideComponent(AdminSidebarSectionComponent, {
+ set: {
+ entryComponents: [TestComponent]
+ }
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(AdminSidebarSectionComponent);
+ component = fixture.componentInstance;
+ spyOn(component as any, 'getMenuItemComponent').and.returnValue(TestComponent);
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+
+ it('should set the right icon', () => {
+ const icon = fixture.debugElement.query(By.css('.shortcut-icon')).query(By.css('i.fas'));
+ expect(icon.nativeElement.getAttribute('class')).toContain('fa-' + iconString);
+ });
+});
+
+// declare a test component
+@Component({
+ selector: 'ds-test-cmp',
+ template: ``
+})
+class TestComponent {
+}
diff --git a/src/app/+admin/admin-sidebar/admin-sidebar-section/admin-sidebar-section.component.ts b/src/app/+admin/admin-sidebar/admin-sidebar-section/admin-sidebar-section.component.ts
new file mode 100644
index 0000000000..a19a1f95e4
--- /dev/null
+++ b/src/app/+admin/admin-sidebar/admin-sidebar-section/admin-sidebar-section.component.ts
@@ -0,0 +1,34 @@
+import { Component, Inject, Injector, OnInit } from '@angular/core';
+import { MenuSectionComponent } from '../../../shared/menu/menu-section/menu-section.component';
+import { MenuID } from '../../../shared/menu/initial-menus-state';
+import { MenuService } from '../../../shared/menu/menu.service';
+import { rendersSectionForMenu } from '../../../shared/menu/menu-section.decorator';
+import { LinkMenuItemModel } from '../../../shared/menu/menu-item/models/link.model';
+import { MenuSection } from '../../../shared/menu/menu.reducer';
+
+/**
+ * Represents a non-expandable section in the admin sidebar
+ */
+@Component({
+ selector: 'ds-admin-sidebar-section',
+ templateUrl: './admin-sidebar-section.component.html',
+ styleUrls: ['./admin-sidebar-section.component.scss'],
+
+})
+@rendersSectionForMenu(MenuID.ADMIN, false)
+export class AdminSidebarSectionComponent extends MenuSectionComponent implements OnInit {
+
+ /**
+ * This section resides in the Admin Sidebar
+ */
+ menuID: MenuID = MenuID.ADMIN;
+ itemModel;
+ constructor(@Inject('sectionDataProvider') menuSection: MenuSection, protected menuService: MenuService, protected injector: Injector,) {
+ super(menuSection, menuService, injector);
+ this.itemModel = menuSection.model as LinkMenuItemModel;
+ }
+
+ ngOnInit(): void {
+ super.ngOnInit();
+ }
+}
diff --git a/src/app/+admin/admin-sidebar/admin-sidebar.component.html b/src/app/+admin/admin-sidebar/admin-sidebar.component.html
new file mode 100644
index 0000000000..fc9e707bcd
--- /dev/null
+++ b/src/app/+admin/admin-sidebar/admin-sidebar.component.html
@@ -0,0 +1,52 @@
+
\ No newline at end of file
diff --git a/src/app/+admin/admin-sidebar/admin-sidebar.component.scss b/src/app/+admin/admin-sidebar/admin-sidebar.component.scss
new file mode 100644
index 0000000000..9236cd2a0d
--- /dev/null
+++ b/src/app/+admin/admin-sidebar/admin-sidebar.component.scss
@@ -0,0 +1,77 @@
+@import '../../../styles/variables.scss';
+@import '../../../styles/mixins.scss';
+$icon-z-index: 10;
+
+:host {
+ left: 0;
+ top: 0;
+ height: 100vh;
+ flex: 1 1 auto;
+ nav {
+ height: 100%;
+ flex-direction: column;
+ > div {
+ width: 100%;
+ &.sidebar-top-level-items {
+ flex: 1;
+ overflow: auto;
+ @include dark-scrollbar;
+ }
+ }
+
+ &.inactive ::ng-deep .sidebar-collapsible {
+ margin-left: -#{$sidebar-items-width};
+ }
+
+ .navbar-nav {
+ .admin-menu-header {
+ background-color: $admin-sidebar-header-bg;
+ .logo-wrapper {
+ img {
+ height: 20px;
+ }
+ }
+ .section-header-text {
+ line-height: 1.5;
+ }
+
+ }
+ }
+
+
+ ::ng-deep {
+ .navbar-nav {
+ .sidebar-section {
+ display: flex;
+ align-content: stretch;
+ background-color: $dark;
+ .nav-item {
+ padding-top: $spacer;
+ padding-bottom: $spacer;
+ }
+ .shortcut-icon {
+ padding-left: $icon-padding;
+ padding-right: $icon-padding;
+ }
+ .shortcut-icon, .icon-wrapper {
+ background-color: inherit;
+ z-index: $icon-z-index;
+ }
+ .sidebar-collapsible {
+ width: $sidebar-items-width;
+ position: relative;
+ a {
+ padding-right: $spacer;
+ width: 100%;
+ }
+ }
+ &.active > .sidebar-collapsible > .nav-link {
+ color: $navbar-dark-active-color;
+ }
+ }
+ }
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/src/app/+admin/admin-sidebar/admin-sidebar.component.spec.ts b/src/app/+admin/admin-sidebar/admin-sidebar.component.spec.ts
new file mode 100644
index 0000000000..c99e8adc58
--- /dev/null
+++ b/src/app/+admin/admin-sidebar/admin-sidebar.component.spec.ts
@@ -0,0 +1,142 @@
+import { async, ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
+import { NoopAnimationsModule } from '@angular/platform-browser/animations';
+import { TranslateModule } from '@ngx-translate/core';
+import { ChangeDetectionStrategy, Injector, NO_ERRORS_SCHEMA } from '@angular/core';
+import { AdminSidebarComponent } from './admin-sidebar.component';
+import { MenuService } from '../../shared/menu/menu.service';
+import { MenuServiceStub } from '../../shared/testing/menu-service-stub';
+import { CSSVariableService } from '../../shared/sass-helper/sass-helper.service';
+import { CSSVariableServiceStub } from '../../shared/testing/css-variable-service-stub';
+import { AuthServiceStub } from '../../shared/testing/auth-service-stub';
+import { AuthService } from '../../core/auth/auth.service';
+
+import { of as observableOf } from 'rxjs';
+import { By } from '@angular/platform-browser';
+
+describe('AdminSidebarComponent', () => {
+ let comp: AdminSidebarComponent;
+ let fixture: ComponentFixture;
+ const menuService = new MenuServiceStub();
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ imports: [TranslateModule.forRoot(), NoopAnimationsModule],
+ declarations: [AdminSidebarComponent],
+ providers: [
+ { provide: Injector, useValue: {} },
+ { provide: MenuService, useValue: menuService },
+ { provide: CSSVariableService, useClass: CSSVariableServiceStub },
+ { provide: AuthService, useClass: AuthServiceStub }
+ ],
+ schemas: [NO_ERRORS_SCHEMA]
+ }).overrideComponent(AdminSidebarComponent, {
+ set: {
+ changeDetection: ChangeDetectionStrategy.Default,
+ }
+ }).compileComponents();
+ }));
+
+ beforeEach(() => {
+ spyOn(menuService, 'getMenuTopSections').and.returnValue(observableOf([]));
+ fixture = TestBed.createComponent(AdminSidebarComponent);
+ comp = fixture.componentInstance; // SearchPageComponent test instance
+ comp.sections = observableOf([]);
+ fixture.detectChanges();
+ });
+
+ describe('startSlide', () => {
+ describe('when expanding', () => {
+ beforeEach(() => {
+ comp.sidebarClosed = true;
+ comp.startSlide({ toState: 'expanded' } as any);
+ });
+
+ it('should set the sidebarClosed to false', () => {
+ expect(comp.sidebarClosed).toBeFalsy();
+ })
+ });
+
+ describe('when collapsing', () => {
+ beforeEach(() => {
+ comp.sidebarClosed = false;
+ comp.startSlide({ toState: 'collapsed' } as any);
+ });
+
+ it('should set the sidebarOpen to false', () => {
+ expect(comp.sidebarOpen).toBeFalsy();
+ })
+ })
+ });
+
+ describe('finishSlide', () => {
+ describe('when expanding', () => {
+ beforeEach(() => {
+ comp.sidebarClosed = true;
+ comp.startSlide({ fromState: 'expanded' } as any);
+ });
+
+ it('should set the sidebarClosed to true', () => {
+ expect(comp.sidebarClosed).toBeTruthy();
+ })
+ });
+
+ describe('when collapsing', () => {
+ beforeEach(() => {
+ comp.sidebarClosed = false;
+ comp.startSlide({ fromState: 'collapsed' } as any);
+ });
+
+ it('should set the sidebarOpen to true', () => {
+ expect(comp.sidebarOpen).toBeTruthy();
+ })
+ })
+ });
+
+ describe('when the collapse icon is clicked', () => {
+ beforeEach(() => {
+ spyOn(menuService, 'toggleMenu');
+ const sidebarToggler = fixture.debugElement.query(By.css('#sidebar-collapse-toggle')).query(By.css('a.shortcut-icon'));
+ sidebarToggler.triggerEventHandler('click', {preventDefault: () => {/**/}});
+ });
+
+ it('should call toggleMenu on the menuService', () => {
+ expect(menuService.toggleMenu).toHaveBeenCalled();
+ });
+ });
+
+ describe('when the collapse link is clicked', () => {
+ beforeEach(() => {
+ spyOn(menuService, 'toggleMenu');
+ const sidebarToggler = fixture.debugElement.query(By.css('#sidebar-collapse-toggle')).query(By.css('.sidebar-collapsible')).query(By.css('a'));
+ sidebarToggler.triggerEventHandler('click', {preventDefault: () => {/**/}});
+ });
+
+ it('should call toggleMenu on the menuService', () => {
+ expect(menuService.toggleMenu).toHaveBeenCalled();
+ });
+ });
+
+ describe('when the the mouse enters the nav tag', () => {
+ it('should call expandPreview on the menuService after 100ms', fakeAsync(() => {
+ spyOn(menuService, 'expandMenuPreview');
+ const sidebarToggler = fixture.debugElement.query(By.css('nav.navbar'));
+ sidebarToggler.triggerEventHandler('mouseenter', {preventDefault: () => {/**/}});
+ tick(99);
+ expect(menuService.expandMenuPreview).not.toHaveBeenCalled();
+ tick(1);
+ expect(menuService.expandMenuPreview).toHaveBeenCalled();
+ }));
+ });
+
+ describe('when the the mouse leaves the nav tag', () => {
+ it('should call collapseMenuPreview on the menuService after 400ms', fakeAsync(() => {
+ spyOn(menuService, 'collapseMenuPreview');
+ const sidebarToggler = fixture.debugElement.query(By.css('nav.navbar'));
+ sidebarToggler.triggerEventHandler('mouseleave', {preventDefault: () => {/**/}});
+ tick(399);
+ expect(menuService.collapseMenuPreview).not.toHaveBeenCalled();
+ tick(1);
+ expect(menuService.collapseMenuPreview).toHaveBeenCalled();
+ }));
+ });
+});
diff --git a/src/app/+admin/admin-sidebar/admin-sidebar.component.ts b/src/app/+admin/admin-sidebar/admin-sidebar.component.ts
new file mode 100644
index 0000000000..eb48f64d4d
--- /dev/null
+++ b/src/app/+admin/admin-sidebar/admin-sidebar.component.ts
@@ -0,0 +1,479 @@
+import { Component, Injector, OnInit } from '@angular/core';
+import { Observable } from 'rxjs/internal/Observable';
+import { slide, slideHorizontal, slideSidebar } from '../../shared/animations/slide';
+import { CSSVariableService } from '../../shared/sass-helper/sass-helper.service';
+import { MenuService } from '../../shared/menu/menu.service';
+import { MenuID, MenuItemType } from '../../shared/menu/initial-menus-state';
+import { MenuComponent } from '../../shared/menu/menu.component';
+import { TextMenuItemModel } from '../../shared/menu/menu-item/models/text.model';
+import { LinkMenuItemModel } from '../../shared/menu/menu-item/models/link.model';
+import { AuthService } from '../../core/auth/auth.service';
+import { first, map } from 'rxjs/operators';
+import { combineLatest as combineLatestObservable } from 'rxjs';
+
+/**
+ * Component representing the admin sidebar
+ */
+@Component({
+ selector: 'ds-admin-sidebar',
+ templateUrl: './admin-sidebar.component.html',
+ styleUrls: ['./admin-sidebar.component.scss'],
+ animations: [slideHorizontal, slideSidebar]
+})
+export class AdminSidebarComponent extends MenuComponent implements OnInit {
+ /**
+ * The menu ID of the Navbar is PUBLIC
+ * @type {MenuID.ADMIN}
+ */
+ menuID = MenuID.ADMIN;
+
+ /**
+ * Observable that emits the width of the collapsible menu sections
+ */
+ sidebarWidth: Observable;
+
+ /**
+ * Is true when the sidebar is open, is false when the sidebar is animating or closed
+ * @type {boolean}
+ */
+ sidebarOpen = true; // Open in UI, animation finished
+
+ /**
+ * Is true when the sidebar is closed, is false when the sidebar is animating or open
+ * @type {boolean}
+ */
+ sidebarClosed = !this.sidebarOpen; // Closed in UI, animation finished
+
+ /**
+ * Emits true when either the menu OR the menu's preview is expanded, else emits false
+ */
+ sidebarExpanded: Observable;
+
+ constructor(protected menuService: MenuService,
+ protected injector: Injector,
+ private variableService: CSSVariableService,
+ private authService: AuthService
+ ) {
+ super(menuService, injector);
+ }
+
+ /**
+ * Set and calculate all initial values of the instance variables
+ */
+ ngOnInit(): void {
+ this.createMenu();
+ super.ngOnInit();
+ this.sidebarWidth = this.variableService.getVariable('sidebarItemsWidth');
+ this.authService.isAuthenticated()
+ .subscribe((loggedIn: boolean) => {
+ if (loggedIn) {
+ this.menuService.showMenu(this.menuID);
+ }
+ });
+ this.menuCollapsed.pipe(first())
+ .subscribe((collapsed: boolean) => {
+ this.sidebarOpen = !collapsed;
+ this.sidebarClosed = collapsed;
+ });
+ this.sidebarExpanded = combineLatestObservable(this.menuCollapsed, this.menuPreviewCollapsed)
+ .pipe(
+ map(([collapsed, previewCollapsed]) => (!collapsed || !previewCollapsed))
+ );
+ }
+
+ /**
+ * Initialize all menu sections and items for this menu
+ */
+ private createMenu() {
+ const menuList = [
+ /* News */
+ {
+ id: 'new',
+ active: false,
+ visible: true,
+ model: {
+ type: MenuItemType.TEXT,
+ text: 'menu.section.new'
+ } as TextMenuItemModel,
+ icon: 'plus-circle',
+ index: 0
+ },
+ {
+ id: 'new_community',
+ parentID: 'new',
+ active: false,
+ visible: true,
+ model: {
+ type: MenuItemType.LINK,
+ text: 'menu.section.new_community',
+ link: '/communities/submission'
+ } as LinkMenuItemModel,
+ },
+ {
+ id: 'new_collection',
+ parentID: 'new',
+ active: false,
+ visible: true,
+ model: {
+ type: MenuItemType.LINK,
+ text: 'menu.section.new_collection',
+ link: '/collections/submission'
+ } as LinkMenuItemModel,
+ },
+ {
+ id: 'new_item',
+ parentID: 'new',
+ active: false,
+ visible: true,
+ model: {
+ type: MenuItemType.LINK,
+ text: 'menu.section.new_item',
+ link: '/items/submission'
+ } as LinkMenuItemModel,
+ },
+ {
+ id: 'new_item_version',
+ parentID: 'new',
+ active: false,
+ visible: true,
+ model: {
+ type: MenuItemType.LINK,
+ text: 'menu.section.new_item_version',
+ link: '#'
+ } as LinkMenuItemModel,
+ },
+
+ /* Edit */
+ {
+ id: 'edit',
+ active: false,
+ visible: true,
+ model: {
+ type: MenuItemType.TEXT,
+ text: 'menu.section.edit'
+ } as TextMenuItemModel,
+ icon: 'pencil-alt',
+ index: 1
+ },
+ {
+ id: 'edit_community',
+ parentID: 'edit',
+ active: false,
+ visible: true,
+ model: {
+ type: MenuItemType.LINK,
+ text: 'menu.section.edit_community',
+ link: '#'
+ } as LinkMenuItemModel,
+ },
+ {
+ id: 'edit_collection',
+ parentID: 'edit',
+ active: false,
+ visible: true,
+ model: {
+ type: MenuItemType.LINK,
+ text: 'menu.section.edit_collection',
+ link: '#'
+ } as LinkMenuItemModel,
+ },
+ {
+ id: 'edit_item',
+ parentID: 'edit',
+ active: false,
+ visible: true,
+ model: {
+ type: MenuItemType.LINK,
+ text: 'menu.section.edit_item',
+ link: '#'
+ } as LinkMenuItemModel,
+ },
+
+ /* Import */
+ {
+ id: 'import',
+ active: false,
+ visible: true,
+ model: {
+ type: MenuItemType.TEXT,
+ text: 'menu.section.import'
+ } as TextMenuItemModel,
+ icon: 'sign-in-alt',
+ index: 2
+ },
+ {
+ id: 'import_metadata',
+ parentID: 'import',
+ active: false,
+ visible: true,
+ model: {
+ type: MenuItemType.LINK,
+ text: 'menu.section.import_metadata',
+ link: '#'
+ } as LinkMenuItemModel,
+ },
+ {
+ id: 'import_batch',
+ parentID: 'import',
+ active: false,
+ visible: true,
+ model: {
+ type: MenuItemType.LINK,
+ text: 'menu.section.import_batch',
+ link: '#'
+ } as LinkMenuItemModel,
+ },
+
+ /* Export */
+ {
+ id: 'export',
+ active: false,
+ visible: true,
+ model: {
+ type: MenuItemType.TEXT,
+ text: 'menu.section.export'
+ } as TextMenuItemModel,
+ icon: 'sign-out-alt',
+ index: 3
+ },
+ {
+ id: 'export_community',
+ parentID: 'export',
+ active: false,
+ visible: true,
+ model: {
+ type: MenuItemType.LINK,
+ text: 'menu.section.export_community',
+ link: '#'
+ } as LinkMenuItemModel,
+ },
+ {
+ id: 'export_collection',
+ parentID: 'export',
+ active: false,
+ visible: true,
+ model: {
+ type: MenuItemType.LINK,
+ text: 'menu.section.export_collection',
+ link: '#'
+ } as LinkMenuItemModel,
+ },
+ {
+ id: 'export_item',
+ parentID: 'export',
+ active: false,
+ visible: true,
+ model: {
+ type: MenuItemType.LINK,
+ text: 'menu.section.export_item',
+ link: '#'
+ } as LinkMenuItemModel,
+ }, {
+ id: 'export_metadata',
+ parentID: 'export',
+ active: false,
+ visible: true,
+ model: {
+ type: MenuItemType.LINK,
+ text: 'menu.section.export_metadata',
+ link: '#'
+ } as LinkMenuItemModel,
+ },
+
+ /* Access Control */
+ {
+ id: 'access_control',
+ active: false,
+ visible: true,
+ model: {
+ type: MenuItemType.TEXT,
+ text: 'menu.section.access_control'
+ } as TextMenuItemModel,
+ icon: 'key',
+ index: 4
+ },
+ {
+ id: 'access_control_people',
+ parentID: 'access_control',
+ active: false,
+ visible: true,
+ model: {
+ type: MenuItemType.LINK,
+ text: 'menu.section.access_control_people',
+ link: '#'
+ } as LinkMenuItemModel,
+ },
+ {
+ id: 'access_control_groups',
+ parentID: 'access_control',
+ active: false,
+ visible: true,
+ model: {
+ type: MenuItemType.LINK,
+ text: 'menu.section.access_control_groups',
+ link: '#'
+ } as LinkMenuItemModel,
+ },
+ {
+ id: 'access_control_authorizations',
+ parentID: 'access_control',
+ active: false,
+ visible: true,
+ model: {
+ type: MenuItemType.LINK,
+ text: 'menu.section.access_control_authorizations',
+ link: '#'
+ } as LinkMenuItemModel,
+ },
+
+ /* Search */
+ {
+ id: 'find',
+ active: false,
+ visible: true,
+ model: {
+ type: MenuItemType.TEXT,
+ text: 'menu.section.find'
+ } as TextMenuItemModel,
+ icon: 'search',
+ index: 5
+ },
+ {
+ id: 'find_items',
+ parentID: 'find',
+ active: false,
+ visible: true,
+ model: {
+ type: MenuItemType.LINK,
+ text: 'menu.section.find_items',
+ link: '/search'
+ } as LinkMenuItemModel,
+ },
+ {
+ id: 'find_withdrawn_items',
+ parentID: 'find',
+ active: false,
+ visible: true,
+ model: {
+ type: MenuItemType.LINK,
+ text: 'menu.section.find_withdrawn_items',
+ link: '#'
+ } as LinkMenuItemModel,
+ },
+ {
+ id: 'find_private_items',
+ parentID: 'find',
+ active: false,
+ visible: true,
+ model: {
+ type: MenuItemType.LINK,
+ text: 'menu.section.find_private_items',
+ link: '/admin/items'
+ } as LinkMenuItemModel,
+ },
+
+ /* Registries */
+ {
+ id: 'registries',
+ active: false,
+ visible: true,
+ model: {
+ type: MenuItemType.TEXT,
+ text: 'menu.section.registries'
+ } as TextMenuItemModel,
+ icon: 'list',
+ index: 6
+ },
+ {
+ id: 'registries_metadata',
+ parentID: 'registries',
+ active: false,
+ visible: true,
+ model: {
+ type: MenuItemType.LINK,
+ text: 'menu.section.registries_metadata',
+ link: 'admin/registries/metadata'
+ } as LinkMenuItemModel,
+ },
+ {
+ id: 'registries_format',
+ parentID: 'registries',
+ active: false,
+ visible: true,
+ model: {
+ type: MenuItemType.LINK,
+ text: 'menu.section.registries_format',
+ link: 'admin/registries/bitstream-formats'
+ } as LinkMenuItemModel,
+ },
+
+ /* Curation tasks */
+ {
+ id: 'curation_tasks',
+ active: false,
+ visible: true,
+ model: {
+ type: MenuItemType.LINK,
+ text: 'menu.section.curation_task',
+ link: '/curation'
+ } as LinkMenuItemModel,
+ icon: 'filter',
+ index: 7
+ },
+
+ /* Statistics */
+ {
+ id: 'statistics_task',
+ active: false,
+ visible: true,
+ model: {
+ type: MenuItemType.LINK,
+ text: 'menu.section.statistics_task',
+ link: '#'
+ } as LinkMenuItemModel,
+ icon: 'chart-bar',
+ index: 8
+ },
+
+ /* Control Panel */
+ {
+ id: 'control_panel',
+ active: false,
+ visible: true,
+ model: {
+ type: MenuItemType.LINK,
+ text: 'menu.section.control_panel',
+ link: '#'
+ } as LinkMenuItemModel,
+ icon: 'cogs',
+ index: 9
+ },
+ ];
+ menuList.forEach((menuSection) => this.menuService.addSection(this.menuID, menuSection));
+
+ }
+
+ /**
+ * Method to change this.collapsed to false when the slide animation ends and is sliding open
+ * @param event The animation event
+ */
+ startSlide(event: any): void {
+ if (event.toState === 'expanded') {
+ this.sidebarClosed = false;
+ } else if (event.toState === 'collapsed') {
+ this.sidebarOpen = false;
+ }
+ }
+
+ /**
+ * Method to change this.collapsed to false when the slide animation ends and is sliding open
+ * @param event The animation event
+ */
+ finishSlide(event: any): void {
+ if (event.fromState === 'expanded') {
+ this.sidebarClosed = true;
+ } else if (event.fromState === 'collapsed') {
+ this.sidebarOpen = true;
+ }
+ }
+}
diff --git a/src/app/+admin/admin-sidebar/expandable-admin-sidebar-section/expandable-admin-sidebar-section.component.html b/src/app/+admin/admin-sidebar/expandable-admin-sidebar-section/expandable-admin-sidebar-section.component.html
new file mode 100644
index 0000000000..808683910e
--- /dev/null
+++ b/src/app/+admin/admin-sidebar/expandable-admin-sidebar-section/expandable-admin-sidebar-section.component.html
@@ -0,0 +1,27 @@
+
\ No newline at end of file
diff --git a/src/app/+admin/admin-sidebar/expandable-admin-sidebar-section/expandable-admin-sidebar-section.component.scss b/src/app/+admin/admin-sidebar/expandable-admin-sidebar-section/expandable-admin-sidebar-section.component.scss
new file mode 100644
index 0000000000..779cba09d9
--- /dev/null
+++ b/src/app/+admin/admin-sidebar/expandable-admin-sidebar-section/expandable-admin-sidebar-section.component.scss
@@ -0,0 +1,21 @@
+@import '../../../../styles/variables.scss';
+
+::ng-deep {
+ .fa-chevron-right {
+ padding-left: $spacer/2;
+ font-size: 0.5rem;
+ line-height: 3;
+ }
+
+ .sidebar-sub-level-items {
+ list-style: disc;
+ color: $navbar-dark-color;
+ overflow: hidden;
+
+ }
+
+ .sidebar-collapsible {
+ display: flex;
+ flex-direction: column;
+ }
+}
\ No newline at end of file
diff --git a/src/app/+admin/admin-sidebar/expandable-admin-sidebar-section/expandable-admin-sidebar-section.component.spec.ts b/src/app/+admin/admin-sidebar/expandable-admin-sidebar-section/expandable-admin-sidebar-section.component.spec.ts
new file mode 100644
index 0000000000..787386932a
--- /dev/null
+++ b/src/app/+admin/admin-sidebar/expandable-admin-sidebar-section/expandable-admin-sidebar-section.component.spec.ts
@@ -0,0 +1,84 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { ExpandableAdminSidebarSectionComponent } from './expandable-admin-sidebar-section.component';
+import { MenuService } from '../../../shared/menu/menu.service';
+import { MenuServiceStub } from '../../../shared/testing/menu-service-stub';
+import { CSSVariableService } from '../../../shared/sass-helper/sass-helper.service';
+import { CSSVariableServiceStub } from '../../../shared/testing/css-variable-service-stub';
+import { of as observableOf } from 'rxjs';
+import { Component } from '@angular/core';
+import { NoopAnimationsModule } from '@angular/platform-browser/animations';
+import { By } from '@angular/platform-browser';
+import { TranslateModule } from '@ngx-translate/core';
+
+describe('ExpandableAdminSidebarSectionComponent', () => {
+ let component: ExpandableAdminSidebarSectionComponent;
+ let fixture: ComponentFixture;
+ const menuService = new MenuServiceStub();
+ const iconString = 'test';
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ imports: [NoopAnimationsModule, TranslateModule.forRoot()],
+ declarations: [ExpandableAdminSidebarSectionComponent, TestComponent],
+ providers: [
+ { provide: 'sectionDataProvider', useValue: {icon: iconString} },
+ { provide: MenuService, useValue: menuService },
+ { provide: CSSVariableService, useClass: CSSVariableServiceStub },
+ ]
+ }).overrideComponent(ExpandableAdminSidebarSectionComponent, {
+ set: {
+ entryComponents: [TestComponent]
+ }
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ spyOn(menuService, 'getSubSectionsByParentID').and.returnValue(observableOf([]));
+ fixture = TestBed.createComponent(ExpandableAdminSidebarSectionComponent);
+ component = fixture.componentInstance;
+ spyOn(component as any, 'getMenuItemComponent').and.returnValue(TestComponent);
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+
+ it('should set the right icon', () => {
+ const icon = fixture.debugElement.query(By.css('.icon-wrapper')).query(By.css('i.fas'));
+ expect(icon.nativeElement.getAttribute('class')).toContain('fa-' + iconString);
+ });
+
+ describe('when the icon is clicked', () => {
+ beforeEach(() => {
+ spyOn(menuService, 'toggleActiveSection');
+ const sidebarToggler = fixture.debugElement.query(By.css('a.shortcut-icon'));
+ sidebarToggler.triggerEventHandler('click', {preventDefault: () => {/**/}});
+ });
+
+ it('should call toggleActiveSection on the menuService', () => {
+ expect(menuService.toggleActiveSection).toHaveBeenCalled();
+ });
+ });
+
+ describe('when the header text is clicked', () => {
+ beforeEach(() => {
+ spyOn(menuService, 'toggleActiveSection');
+ const sidebarToggler = fixture.debugElement.query(By.css('.sidebar-collapsible')).query(By.css('a'));
+ sidebarToggler.triggerEventHandler('click', {preventDefault: () => {/**/}});
+ });
+
+ it('should call toggleActiveSection on the menuService', () => {
+ expect(menuService.toggleActiveSection).toHaveBeenCalled();
+ });
+ });
+});
+
+// declare a test component
+@Component({
+ selector: 'ds-test-cmp',
+ template: ``
+})
+class TestComponent {
+}
diff --git a/src/app/+admin/admin-sidebar/expandable-admin-sidebar-section/expandable-admin-sidebar-section.component.ts b/src/app/+admin/admin-sidebar/expandable-admin-sidebar-section/expandable-admin-sidebar-section.component.ts
new file mode 100644
index 0000000000..4921be77e2
--- /dev/null
+++ b/src/app/+admin/admin-sidebar/expandable-admin-sidebar-section/expandable-admin-sidebar-section.component.ts
@@ -0,0 +1,69 @@
+import { Component, Inject, Injector, OnInit } from '@angular/core';
+import { rotate } from '../../../shared/animations/rotate';
+import { AdminSidebarSectionComponent } from '../admin-sidebar-section/admin-sidebar-section.component';
+import { slide } from '../../../shared/animations/slide';
+import { CSSVariableService } from '../../../shared/sass-helper/sass-helper.service';
+import { bgColor } from '../../../shared/animations/bgColor';
+import { MenuID } from '../../../shared/menu/initial-menus-state';
+import { MenuService } from '../../../shared/menu/menu.service';
+import { combineLatest as combineLatestObservable, Observable } from 'rxjs';
+import { map } from 'rxjs/operators';
+import { rendersSectionForMenu } from '../../../shared/menu/menu-section.decorator';
+
+/**
+ * Represents a expandable section in the sidebar
+ */
+@Component({
+ selector: 'ds-expandable-admin-sidebar-section',
+ templateUrl: './expandable-admin-sidebar-section.component.html',
+ styleUrls: ['./expandable-admin-sidebar-section.component.scss'],
+ animations: [rotate, slide, bgColor]
+
+})
+@rendersSectionForMenu(MenuID.ADMIN, true)
+export class ExpandableAdminSidebarSectionComponent extends AdminSidebarSectionComponent implements OnInit {
+ /**
+ * This section resides in the Admin Sidebar
+ */
+ menuID = MenuID.ADMIN;
+
+ /**
+ * The background color of the section when it's active
+ */
+ sidebarActiveBg;
+
+ /**
+ * Emits true when the sidebar is currently collapsed, true when it's expanded
+ */
+ sidebarCollapsed: Observable;
+
+ /**
+ * Emits true when the sidebar's preview is currently collapsed, true when it's expanded
+ */
+ sidebarPreviewCollapsed: Observable;
+
+ /**
+ * Emits true when the menu section is expanded, else emits false
+ * This is true when the section is active AND either the sidebar or it's preview is open
+ */
+ expanded: Observable;
+
+ constructor(@Inject('sectionDataProvider') menuSection, protected menuService: MenuService,
+ private variableService: CSSVariableService, protected injector: Injector) {
+ super(menuSection, menuService, injector);
+ }
+
+ /**
+ * Set initial values for instance variables
+ */
+ ngOnInit(): void {
+ super.ngOnInit();
+ this.sidebarActiveBg = this.variableService.getVariable('adminSidebarActiveBg');
+ this.sidebarCollapsed = this.menuService.isMenuCollapsed(this.menuID);
+ this.sidebarPreviewCollapsed = this.menuService.isMenuPreviewCollapsed(this.menuID);
+ this.expanded = combineLatestObservable(this.active, this.sidebarCollapsed, this.sidebarPreviewCollapsed)
+ .pipe(
+ map(([active, sidebarCollapsed, sidebarPreviewCollapsed]) => (active && (!sidebarCollapsed || !sidebarPreviewCollapsed)))
+ );
+ }
+}
diff --git a/src/app/+admin/admin.module.ts b/src/app/+admin/admin.module.ts
index b979813376..41d00223ab 100644
--- a/src/app/+admin/admin.module.ts
+++ b/src/app/+admin/admin.module.ts
@@ -5,7 +5,7 @@ import { AdminRoutingModule } from './admin-routing.module';
@NgModule({
imports: [
AdminRegistriesModule,
- AdminRoutingModule
+ AdminRoutingModule,
]
})
export class AdminModule {
diff --git a/src/app/+community-page/community-page.component.html b/src/app/+community-page/community-page.component.html
index 1bf322a688..844250f58f 100644
--- a/src/app/+community-page/community-page.component.html
+++ b/src/app/+community-page/community-page.component.html
@@ -24,8 +24,8 @@
[content]="communityPayload.copyrightText"
[hasInnerHtml]="true">
-
+
+
diff --git a/src/app/+community-page/community-page.module.ts b/src/app/+community-page/community-page.module.ts
index e00c3910c5..d7f97755c2 100644
--- a/src/app/+community-page/community-page.module.ts
+++ b/src/app/+community-page/community-page.module.ts
@@ -6,6 +6,7 @@ import { SharedModule } from '../shared/shared.module';
import { CommunityPageComponent } from './community-page.component';
import { CommunityPageSubCollectionListComponent } from './sub-collection-list/community-page-sub-collection-list.component';
import { CommunityPageRoutingModule } from './community-page-routing.module';
+import {CommunityPageSubCommunityListComponent} from './sub-community-list/community-page-sub-community-list.component';
@NgModule({
imports: [
@@ -16,6 +17,7 @@ import { CommunityPageRoutingModule } from './community-page-routing.module';
declarations: [
CommunityPageComponent,
CommunityPageSubCollectionListComponent,
+ CommunityPageSubCommunityListComponent,
]
})
export class CommunityPageModule {
diff --git a/src/app/+community-page/sub-collection-list/community-page-sub-collection-list.component.html b/src/app/+community-page/sub-collection-list/community-page-sub-collection-list.component.html
index 12c2578d9c..9156a99b18 100644
--- a/src/app/+community-page/sub-collection-list/community-page-sub-collection-list.component.html
+++ b/src/app/+community-page/sub-collection-list/community-page-sub-collection-list.component.html
@@ -1,5 +1,5 @@
-
+
0" @fadeIn>
{{'community.sub-collection-list.head' | translate}}
-
diff --git a/src/app/+community-page/sub-community-list/community-page-sub-community-list.component.html b/src/app/+community-page/sub-community-list/community-page-sub-community-list.component.html
new file mode 100644
index 0000000000..6cd62ba48d
--- /dev/null
+++ b/src/app/+community-page/sub-community-list/community-page-sub-community-list.component.html
@@ -0,0 +1,15 @@
+
+
0" @fadeIn>
+
{{'community.sub-community-list.head' | translate}}
+
+
+
+
+
diff --git a/src/app/+community-page/sub-community-list/community-page-sub-community-list.component.scss b/src/app/+community-page/sub-community-list/community-page-sub-community-list.component.scss
new file mode 100644
index 0000000000..50be6f5ad0
--- /dev/null
+++ b/src/app/+community-page/sub-community-list/community-page-sub-community-list.component.scss
@@ -0,0 +1 @@
+@import '../../../styles/variables.scss';
diff --git a/src/app/+community-page/sub-community-list/community-page-sub-community-list.component.spec.ts b/src/app/+community-page/sub-community-list/community-page-sub-community-list.component.spec.ts
new file mode 100644
index 0000000000..0c985e37f9
--- /dev/null
+++ b/src/app/+community-page/sub-community-list/community-page-sub-community-list.component.spec.ts
@@ -0,0 +1,96 @@
+import {async, ComponentFixture, TestBed} from '@angular/core/testing';
+import {TranslateModule} from '@ngx-translate/core';
+import {NO_ERRORS_SCHEMA} from '@angular/core';
+import {CommunityPageSubCommunityListComponent} from './community-page-sub-community-list.component';
+import {Community} from '../../core/shared/community.model';
+import {RemoteData} from '../../core/data/remote-data';
+import {PaginatedList} from '../../core/data/paginated-list';
+import {PageInfo} from '../../core/shared/page-info.model';
+import {SharedModule} from '../../shared/shared.module';
+import {RouterTestingModule} from '@angular/router/testing';
+import {NoopAnimationsModule} from '@angular/platform-browser/animations';
+import {By} from '@angular/platform-browser';
+import {of as observableOf, Observable } from 'rxjs';
+
+describe('SubCommunityList Component', () => {
+ let comp: CommunityPageSubCommunityListComponent;
+ let fixture: ComponentFixture;
+
+ const subcommunities = [Object.assign(new Community(), {
+ name: 'SubCommunity 1',
+ id: '123456789-1',
+ metadata: [
+ {
+ key: 'dc.title',
+ language: 'en_US',
+ value: 'SubCommunity 1'
+ }]
+ }),
+ Object.assign(new Community(), {
+ name: 'SubCommunity 2',
+ id: '123456789-2',
+ metadata: [
+ {
+ key: 'dc.title',
+ language: 'en_US',
+ value: 'SubCommunity 2'
+ }]
+ })
+ ];
+
+ const emptySubCommunitiesCommunity = Object.assign(new Community(), {
+ metadata: [
+ {
+ key: 'dc.title',
+ language: 'en_US',
+ value: 'Test title'
+ }],
+ subcommunities: observableOf(new RemoteData(true, true, true,
+ undefined, new PaginatedList(new PageInfo(), [])))
+ });
+
+ const mockCommunity = Object.assign(new Community(), {
+ metadata: [
+ {
+ key: 'dc.title',
+ language: 'en_US',
+ value: 'Test title'
+ }],
+ subcommunities: observableOf(new RemoteData(true, true, true,
+ undefined, new PaginatedList(new PageInfo(), subcommunities)))
+ })
+ ;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ imports: [TranslateModule.forRoot(), SharedModule,
+ RouterTestingModule.withRoutes([]),
+ NoopAnimationsModule],
+ declarations: [CommunityPageSubCommunityListComponent],
+ schemas: [NO_ERRORS_SCHEMA]
+ }).compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(CommunityPageSubCommunityListComponent);
+ comp = fixture.componentInstance;
+ });
+
+ it('should display a list of subCommunities', () => {
+ comp.community = mockCommunity;
+ fixture.detectChanges();
+
+ const subComList = fixture.debugElement.queryAll(By.css('li'));
+ expect(subComList.length).toEqual(2);
+ expect(subComList[0].nativeElement.textContent).toContain('SubCommunity 1');
+ expect(subComList[1].nativeElement.textContent).toContain('SubCommunity 2');
+ });
+
+ it('should not display the header when subCommunities are empty', () => {
+ comp.community = emptySubCommunitiesCommunity;
+ fixture.detectChanges();
+
+ const subComHead = fixture.debugElement.queryAll(By.css('h2'));
+ expect(subComHead.length).toEqual(0);
+ });
+});
diff --git a/src/app/+community-page/sub-community-list/community-page-sub-community-list.component.ts b/src/app/+community-page/sub-community-list/community-page-sub-community-list.component.ts
new file mode 100644
index 0000000000..91f6d7bac1
--- /dev/null
+++ b/src/app/+community-page/sub-community-list/community-page-sub-community-list.component.ts
@@ -0,0 +1,26 @@
+import { Component, Input, OnInit } from '@angular/core';
+
+import { RemoteData } from '../../core/data/remote-data';
+import { Community } from '../../core/shared/community.model';
+
+import { fadeIn } from '../../shared/animations/fade';
+import { PaginatedList } from '../../core/data/paginated-list';
+import {Observable} from 'rxjs';
+
+@Component({
+ selector: 'ds-community-page-sub-community-list',
+ styleUrls: ['./community-page-sub-community-list.component.scss'],
+ templateUrl: './community-page-sub-community-list.component.html',
+ animations:[fadeIn]
+})
+/**
+ * Component to render the sub-communities of a Community
+ */
+export class CommunityPageSubCommunityListComponent implements OnInit {
+ @Input() community: Community;
+ subCommunitiesRDObs: Observable>>;
+
+ ngOnInit(): void {
+ this.subCommunitiesRDObs = this.community.subcommunities;
+ }
+}
diff --git a/src/app/+home-page/home-news/home-news.component.scss b/src/app/+home-page/home-news/home-news.component.scss
index 4f4c6df128..a1bb43af91 100644
--- a/src/app/+home-page/home-news/home-news.component.scss
+++ b/src/app/+home-page/home-news/home-news.component.scss
@@ -8,6 +8,9 @@
.dspace-logo-container {
margin: 10px 20px 0px 20px;
+ .display-3 {
+ word-break: break-word;
+ }
}
.dspace-logo-container img {
diff --git a/src/app/+logout-page/logout-page.component.html b/src/app/+logout-page/logout-page.component.html
index 9c6185b665..b5012ed53b 100644
--- a/src/app/+logout-page/logout-page.component.html
+++ b/src/app/+logout-page/logout-page.component.html
@@ -1,6 +1,6 @@
-
+
{{"logout.form.header" | translate}}
diff --git a/src/app/+search-page/search-filters/search-filter/search-filter.component.html b/src/app/+search-page/search-filters/search-filter/search-filter.component.html
index f5dc5fff38..1013bf7e28 100644
--- a/src/app/+search-page/search-filters/search-filter/search-filter.component.html
+++ b/src/app/+search-page/search-filters/search-filter/search-filter.component.html
@@ -1,5 +1,5 @@
-
{{'search.filters.filter.' + filter.name + '.head'| translate}}
{{'search.filters.filter.' + filter.name + '.head'| translate}}
diff --git a/src/app/+search-page/search-filters/search-filter/search-filter.component.scss b/src/app/+search-page/search-filters/search-filter/search-filter.component.scss
index 6e49172a48..4e45f49468 100644
--- a/src/app/+search-page/search-filters/search-filter/search-filter.component.scss
+++ b/src/app/+search-page/search-filters/search-filter/search-filter.component.scss
@@ -2,11 +2,12 @@
@import '../../../../styles/mixins.scss';
:host {
- border: 1px solid map-get($theme-colors, light);
- .search-filter-wrapper.closed {
- overflow: hidden;
- }
- .filter-toggle {
- line-height: $line-height-base;
- }
-}
\ No newline at end of file
+ border: 1px solid map-get($theme-colors, light);
+ cursor: pointer;
+ .search-filter-wrapper.closed {
+ overflow: hidden;
+ }
+ .filter-toggle {
+ line-height: $line-height-base;
+ }
+}
diff --git a/src/app/+search-page/search-page.component.html b/src/app/+search-page/search-page.component.html
index 653f5e8cd4..6476f8bd68 100644
--- a/src/app/+search-page/search-page.component.html
+++ b/src/app/+search-page/search-page.component.html
@@ -26,7 +26,7 @@
diff --git a/src/app/+search-page/search-sidebar/search-sidebar.component.html b/src/app/+search-page/search-sidebar/search-sidebar.component.html
index 71959b558b..5ff1e3c8fa 100644
--- a/src/app/+search-page/search-sidebar/search-sidebar.component.html
+++ b/src/app/+search-page/search-sidebar/search-sidebar.component.html
@@ -4,7 +4,7 @@