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/app.component.html b/src/app/app.component.html index 95db61ab5a..24ce6dc843 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,6 +1,7 @@
+ diff --git a/src/app/app.component.scss b/src/app/app.component.scss index e4c51ae37b..1b2a21b0c6 100644 --- a/src/app/app.component.scss +++ b/src/app/app.component.scss @@ -28,7 +28,12 @@ body { } .main-content { + z-index: $main-z-index; flex: 1 0 auto; margin-top: $content-spacing; margin-bottom: $content-spacing; } + +ds-navbar { + z-index: $nav-z-index; +} diff --git a/src/app/app.effects.ts b/src/app/app.effects.ts index 6a53d7b619..3ee483ccba 100644 --- a/src/app/app.effects.ts +++ b/src/app/app.effects.ts @@ -1,10 +1,9 @@ - -import { HeaderEffects } from './header/header.effects'; import { StoreEffects } from './store.effects'; import { NotificationsEffects } from './shared/notifications/notifications.effects'; +import { NavbarEffects } from './navbar/navbar.effects'; export const appEffects = [ StoreEffects, - HeaderEffects, + NavbarEffects, NotificationsEffects ]; diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 528c84fd3b..987ce77dd1 100755 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -31,6 +31,7 @@ import { DSpaceRouterStateSerializer } from './shared/ngrx/dspace-router-state-s import { NotificationsBoardComponent } from './shared/notifications/notifications-board/notifications-board.component'; import { NotificationComponent } from './shared/notifications/notification/notification.component'; import { SharedModule } from './shared/shared.module'; +import { NavbarComponent } from './navbar/navbar.component'; export function getConfig() { return ENV_CONFIG; @@ -87,6 +88,7 @@ if (!ENV_CONFIG.production) { declarations: [ AppComponent, HeaderComponent, + NavbarComponent, FooterComponent, PageNotFoundComponent, NotificationComponent, diff --git a/src/app/app.reducer.ts b/src/app/app.reducer.ts index 8dc82dfb6f..6bdb1a1f58 100644 --- a/src/app/app.reducer.ts +++ b/src/app/app.reducer.ts @@ -1,7 +1,5 @@ import { ActionReducerMap } from '@ngrx/store'; import * as fromRouter from '@ngrx/router-store'; - -import { headerReducer, HeaderState } from './header/header.reducer'; import { hostWindowReducer, HostWindowState } from './shared/host-window.reducer'; import { formReducer, FormState } from './shared/form/form.reducer'; import { @@ -12,13 +10,17 @@ import { filterReducer, SearchFiltersState } from './+search-page/search-filters/search-filter/search-filter.reducer'; -import { notificationsReducer, NotificationsState } from './shared/notifications/notifications.reducers'; +import { + notificationsReducer, + NotificationsState +} from './shared/notifications/notifications.reducers'; import { truncatableReducer, TruncatablesState } from './shared/truncatable/truncatable.reducer'; +import { navbarReducer, NavbarState } from './navbar/navbar.reducer'; export interface AppState { router: fromRouter.RouterReducerState; hostWindow: HostWindowState; - header: HeaderState; + navbar: NavbarState; forms: FormState; notifications: NotificationsState; searchSidebar: SearchSidebarState; @@ -29,7 +31,7 @@ export interface AppState { export const appReducers: ActionReducerMap = { router: fromRouter.routerReducer, hostWindow: hostWindowReducer, - header: headerReducer, + navbar: navbarReducer, forms: formReducer, notifications: notificationsReducer, searchSidebar: sidebarReducer, diff --git a/src/app/header/header.component.html b/src/app/header/header.component.html index f47696609c..22e2a307a5 100644 --- a/src/app/header/header.component.html +++ b/src/app/header/header.component.html @@ -1,18 +1,10 @@
-
diff --git a/src/app/header/header.component.scss b/src/app/header/header.component.scss index 81f6a809bf..627ce93c6c 100644 --- a/src/app/header/header.component.scss +++ b/src/app/header/header.component.scss @@ -1,14 +1,5 @@ @import '../../styles/variables.scss'; -header nav.navbar { - border-radius: 0; -} - -header nav.navbar .navbar-toggler:hover { - cursor: pointer; -} - -header nav.navbar .navbar-toggler .navbar-toggler-icon { - background-image: none !important; - line-height: 1.5; -} +.navbar-brand img { + height: $header-logo-height; +} \ No newline at end of file diff --git a/src/app/header/header.component.ts b/src/app/header/header.component.ts index 93cb329f4f..8ccc779873 100644 --- a/src/app/header/header.component.ts +++ b/src/app/header/header.component.ts @@ -1,43 +1,16 @@ -import { Component, OnInit } from '@angular/core'; -import { createSelector, Store } from '@ngrx/store'; +import { Component } from '@angular/core'; import { Observable } from 'rxjs/Observable'; -import { RouterReducerState } from '@ngrx/router-store'; - -import { HeaderState } from './header.reducer'; -import { HeaderToggleAction } from './header.actions'; -import { AppState } from '../app.reducer'; -import { HostWindowService } from '../shared/host-window.service'; - -const headerStateSelector = (state: AppState) => state.header; -const navCollapsedSelector = createSelector(headerStateSelector, (header: HeaderState) => header.navCollapsed); @Component({ selector: 'ds-header', styleUrls: ['header.component.scss'], templateUrl: 'header.component.html', }) -export class HeaderComponent implements OnInit { +export class HeaderComponent { /** * Whether user is authenticated. * @type {Observable} */ public isAuthenticated: Observable; - public isNavBarCollapsed: Observable; public showAuth = false; - - constructor( - private store: Store, - private windowService: HostWindowService - ) { - } - - ngOnInit(): void { - // set loading - this.isNavBarCollapsed = this.store.select(navCollapsedSelector); - } - - public toggle(): void { - this.store.dispatch(new HeaderToggleAction()); - } - } diff --git a/src/app/header/header.actions.ts b/src/app/navbar/navbar.actions.ts similarity index 52% rename from src/app/header/header.actions.ts rename to src/app/navbar/navbar.actions.ts index 7176474a4a..c588c41f0c 100644 --- a/src/app/header/header.actions.ts +++ b/src/app/navbar/navbar.actions.ts @@ -10,23 +10,23 @@ import { type } from '../shared/ngrx/type'; * literal types and runs a simple check to guarantee all * action types in the application are unique. */ -export const HeaderActionTypes = { - COLLAPSE: type('dspace/header/COLLAPSE'), - EXPAND: type('dspace/header/EXPAND'), - TOGGLE: type('dspace/header/TOGGLE') +export const NavbarActionTypes = { + COLLAPSE: type('dspace/navbar/COLLAPSE'), + EXPAND: type('dspace/navbar/EXPAND'), + TOGGLE: type('dspace/navbar/TOGGLE') }; /* tslint:disable:max-classes-per-file */ -export class HeaderCollapseAction implements Action { - type = HeaderActionTypes.COLLAPSE; +export class NavbarCollapseAction implements Action { + type = NavbarActionTypes.COLLAPSE; } -export class HeaderExpandAction implements Action { - type = HeaderActionTypes.EXPAND; +export class NavbarExpandAction implements Action { + type = NavbarActionTypes.EXPAND; } -export class HeaderToggleAction implements Action { - type = HeaderActionTypes.TOGGLE; +export class NavbarToggleAction implements Action { + type = NavbarActionTypes.TOGGLE; } /* tslint:enable:max-classes-per-file */ @@ -34,7 +34,7 @@ export class HeaderToggleAction implements Action { * Export a type alias of all actions in this action group * so that reducers can easily compose action types */ -export type HeaderAction - = HeaderCollapseAction - | HeaderExpandAction - | HeaderToggleAction +export type NavbarAction + = NavbarCollapseAction + | NavbarExpandAction + | NavbarToggleAction diff --git a/src/app/navbar/navbar.component.html b/src/app/navbar/navbar.component.html new file mode 100644 index 0000000000..7c4de41098 --- /dev/null +++ b/src/app/navbar/navbar.component.html @@ -0,0 +1,46 @@ + diff --git a/src/app/navbar/navbar.component.scss b/src/app/navbar/navbar.component.scss new file mode 100644 index 0000000000..10e97834f3 --- /dev/null +++ b/src/app/navbar/navbar.component.scss @@ -0,0 +1,18 @@ +@import '../../styles/variables.scss'; + +nav.navbar { + border-bottom: 1px $gray-400 solid; +} + +nav.navbar .navbar-toggler .navbar-toggler-icon { + background-image: none !important; + line-height: 1.5; +} + +.dropdown-menu { + min-width: 100%; +} +.reset-padding { + margin-left: -$spacer/2; + margin-right: -$spacer/2; +} \ No newline at end of file diff --git a/src/app/header/header.component.spec.ts b/src/app/navbar/navbar.component.spec.ts similarity index 74% rename from src/app/header/header.component.spec.ts rename to src/app/navbar/navbar.component.spec.ts index 9d0dd04e40..0a0ed43716 100644 --- a/src/app/header/header.component.spec.ts +++ b/src/app/navbar/navbar.component.spec.ts @@ -1,4 +1,4 @@ -import { ComponentFixture, TestBed, async } from '@angular/core/testing'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { Store, StoreModule } from '@ngrx/store'; import { TranslateModule } from '@ngx-translate/core'; @@ -6,13 +6,7 @@ import { NgbCollapseModule } from '@ng-bootstrap/ng-bootstrap'; import { Observable } from 'rxjs/Observable'; -import { HeaderComponent } from './header.component'; -import { HeaderState } from './header.reducer'; -import { HeaderToggleAction } from './header.actions'; -import { AuthNavMenuComponent } from '../shared/auth-nav-menu/auth-nav-menu.component'; -import { LogInComponent } from '../shared/log-in/log-in.component'; -import { LogOutComponent } from '../shared/log-out/log-out.component'; -import { LoadingComponent } from '../shared/loading/loading.component'; +import { NavbarComponent } from './navbar.component'; import { ReactiveFormsModule } from '@angular/forms'; import { HostWindowService } from '../shared/host-window.service'; import { HostWindowServiceStub } from '../shared/testing/host-window-service-stub'; @@ -20,12 +14,14 @@ import { RouterStub } from '../shared/testing/router-stub'; import { Router } from '@angular/router'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { NavbarState } from './navbar.reducer'; +import { NavbarToggleAction } from './navbar.actions'; -let comp: HeaderComponent; -let fixture: ComponentFixture; -let store: Store; +let comp: NavbarComponent; +let fixture: ComponentFixture; +let store: Store; -describe('HeaderComponent', () => { +describe('NavbarComponent', () => { // async beforeEach beforeEach(async(() => { @@ -36,7 +32,7 @@ describe('HeaderComponent', () => { NgbCollapseModule.forRoot(), NoopAnimationsModule, ReactiveFormsModule], - declarations: [HeaderComponent], + declarations: [NavbarComponent], providers: [ { provide: HostWindowService, useValue: new HostWindowServiceStub(800) }, { provide: Router, useClass: RouterStub }, @@ -48,7 +44,7 @@ describe('HeaderComponent', () => { // synchronous beforeEach beforeEach(() => { - fixture = TestBed.createComponent(HeaderComponent); + fixture = TestBed.createComponent(NavbarComponent); comp = fixture.componentInstance; @@ -63,8 +59,8 @@ describe('HeaderComponent', () => { navbarToggler.triggerEventHandler('click', null); }); - it('should dispatch a HeaderToggleAction', () => { - expect(store.dispatch).toHaveBeenCalledWith(new HeaderToggleAction()); + it('should dispatch a NavbarToggleAction', () => { + expect(store.dispatch).toHaveBeenCalledWith(new NavbarToggleAction()); }); }); diff --git a/src/app/navbar/navbar.component.ts b/src/app/navbar/navbar.component.ts new file mode 100644 index 0000000000..e1f8d0bb82 --- /dev/null +++ b/src/app/navbar/navbar.component.ts @@ -0,0 +1,34 @@ +import { Component, OnInit } from '@angular/core'; +import { createSelector, Store } from '@ngrx/store'; +import { Observable } from 'rxjs/Observable'; + +import { NavbarState } from './navbar.reducer'; +import { NavbarToggleAction } from './navbar.actions'; +import { AppState } from '../app.reducer'; + +const navbarStateSelector = (state: AppState) => state.navbar; +const navCollapsedSelector = createSelector(navbarStateSelector, (navbar: NavbarState) => navbar.navCollapsed); + +@Component({ + selector: 'ds-navbar', + styleUrls: ['navbar.component.scss'], + templateUrl: 'navbar.component.html', +}) +export class NavbarComponent implements OnInit { + public isNavBarCollapsed: Observable; + + constructor( + private store: Store, + ) { + } + + ngOnInit(): void { + // set loading + this.isNavBarCollapsed = this.store.select(navCollapsedSelector); + } + + public toggle(): void { + this.store.dispatch(new NavbarToggleAction()); + } + +} diff --git a/src/app/header/header.effects.spec.ts b/src/app/navbar/navbar.effects.spec.ts similarity index 66% rename from src/app/header/header.effects.spec.ts rename to src/app/navbar/navbar.effects.spec.ts index e67043dcba..261d24a850 100644 --- a/src/app/header/header.effects.spec.ts +++ b/src/app/navbar/navbar.effects.spec.ts @@ -1,26 +1,26 @@ import { TestBed } from '@angular/core/testing'; -import { HeaderEffects } from './header.effects'; -import { HeaderCollapseAction } from './header.actions'; +import { NavbarEffects } from './navbar.effects'; +import { NavbarCollapseAction } from './navbar.actions'; import { HostWindowResizeAction } from '../shared/host-window.actions'; import { Observable } from 'rxjs/Observable'; import { provideMockActions } from '@ngrx/effects/testing'; import { cold, hot } from 'jasmine-marbles'; import * as fromRouter from '@ngrx/router-store'; -describe('HeaderEffects', () => { - let headerEffects: HeaderEffects; +describe('NavbarEffects', () => { + let navbarEffects: NavbarEffects; let actions: Observable; beforeEach(() => { TestBed.configureTestingModule({ providers: [ - HeaderEffects, + NavbarEffects, provideMockActions(() => actions), // other providers ], }); - headerEffects = TestBed.get(HeaderEffects); + navbarEffects = TestBed.get(NavbarEffects); }); describe('resize$', () => { @@ -28,9 +28,9 @@ describe('HeaderEffects', () => { it('should return a COLLAPSE action in response to a RESIZE action', () => { actions = hot('--a-', { a: new HostWindowResizeAction(800, 600) }); - const expected = cold('--b-', { b: new HeaderCollapseAction() }); + const expected = cold('--b-', { b: new NavbarCollapseAction() }); - expect(headerEffects.resize$).toBeObservable(expected); + expect(navbarEffects.resize$).toBeObservable(expected); }); }); @@ -40,9 +40,9 @@ describe('HeaderEffects', () => { it('should return a COLLAPSE action in response to an UPDATE_LOCATION action', () => { actions = hot('--a-', { a: { type: fromRouter.ROUTER_NAVIGATION } }); - const expected = cold('--b-', { b: new HeaderCollapseAction() }); + const expected = cold('--b-', { b: new NavbarCollapseAction() }); - expect(headerEffects.routeChange$).toBeObservable(expected); + expect(navbarEffects.routeChange$).toBeObservable(expected); }); }); diff --git a/src/app/header/header.effects.ts b/src/app/navbar/navbar.effects.ts similarity index 71% rename from src/app/header/header.effects.ts rename to src/app/navbar/navbar.effects.ts index e1d281958b..bc6cf876e1 100644 --- a/src/app/header/header.effects.ts +++ b/src/app/navbar/navbar.effects.ts @@ -3,18 +3,18 @@ import { Effect, Actions } from '@ngrx/effects' import * as fromRouter from '@ngrx/router-store'; import { HostWindowActionTypes } from '../shared/host-window.actions'; -import { HeaderCollapseAction } from './header.actions'; +import { NavbarCollapseAction } from './navbar.actions'; @Injectable() -export class HeaderEffects { +export class NavbarEffects { @Effect() resize$ = this.actions$ .ofType(HostWindowActionTypes.RESIZE) - .map(() => new HeaderCollapseAction()); + .map(() => new NavbarCollapseAction()); @Effect() routeChange$ = this.actions$ .ofType(fromRouter.ROUTER_NAVIGATION) - .map(() => new HeaderCollapseAction()); + .map(() => new NavbarCollapseAction()); constructor(private actions$: Actions) { diff --git a/src/app/header/header.reducer.spec.ts b/src/app/navbar/navbar.reducer.spec.ts similarity index 65% rename from src/app/header/header.reducer.spec.ts rename to src/app/navbar/navbar.reducer.spec.ts index b2630ec960..b9afbd743e 100644 --- a/src/app/header/header.reducer.spec.ts +++ b/src/app/navbar/navbar.reducer.spec.ts @@ -1,13 +1,9 @@ import * as deepFreeze from 'deep-freeze'; -import { headerReducer } from './header.reducer'; -import { - HeaderCollapseAction, - HeaderExpandAction, - HeaderToggleAction -} from './header.actions'; +import { navbarReducer } from './navbar.reducer'; +import { NavbarCollapseAction, NavbarExpandAction, NavbarToggleAction } from './navbar.actions'; -class NullAction extends HeaderCollapseAction { +class NullAction extends NavbarCollapseAction { type = null; constructor() { @@ -15,18 +11,18 @@ class NullAction extends HeaderCollapseAction { } } -describe('headerReducer', () => { +describe('navbarReducer', () => { it('should return the current state when no valid actions have been made', () => { const state = { navCollapsed: false }; const action = new NullAction(); - const newState = headerReducer(state, action); + const newState = navbarReducer(state, action); expect(newState).toEqual(state); }); it('should start with navCollapsed = true', () => { const action = new NullAction(); - const initialState = headerReducer(undefined, action); + const initialState = navbarReducer(undefined, action); // The navigation starts collapsed expect(initialState.navCollapsed).toEqual(true); @@ -34,8 +30,8 @@ describe('headerReducer', () => { it('should set navCollapsed to true in response to the COLLAPSE action', () => { const state = { navCollapsed: false }; - const action = new HeaderCollapseAction(); - const newState = headerReducer(state, action); + const action = new NavbarCollapseAction(); + const newState = navbarReducer(state, action); expect(newState.navCollapsed).toEqual(true); }); @@ -44,8 +40,8 @@ describe('headerReducer', () => { const state = { navCollapsed: false }; deepFreeze(state); - const action = new HeaderCollapseAction(); - headerReducer(state, action); + const action = new NavbarCollapseAction(); + navbarReducer(state, action); // no expect required, deepFreeze will ensure an exception is thrown if the state // is mutated, and any uncaught exception will cause the test to fail @@ -53,8 +49,8 @@ describe('headerReducer', () => { it('should set navCollapsed to false in response to the EXPAND action', () => { const state = { navCollapsed: true }; - const action = new HeaderExpandAction(); - const newState = headerReducer(state, action); + const action = new NavbarExpandAction(); + const newState = navbarReducer(state, action); expect(newState.navCollapsed).toEqual(false); }); @@ -63,16 +59,16 @@ describe('headerReducer', () => { const state = { navCollapsed: true }; deepFreeze(state); - const action = new HeaderExpandAction(); - headerReducer(state, action); + const action = new NavbarExpandAction(); + navbarReducer(state, action); }); it('should flip the value of navCollapsed in response to the TOGGLE action', () => { const state1 = { navCollapsed: true }; - const action = new HeaderToggleAction(); + const action = new NavbarToggleAction(); - const state2 = headerReducer(state1, action); - const state3 = headerReducer(state2, action); + const state2 = navbarReducer(state1, action); + const state3 = navbarReducer(state2, action); expect(state2.navCollapsed).toEqual(false); expect(state3.navCollapsed).toEqual(true); @@ -82,8 +78,8 @@ describe('headerReducer', () => { const state = { navCollapsed: true }; deepFreeze(state); - const action = new HeaderToggleAction(); - headerReducer(state, action); + const action = new NavbarToggleAction(); + navbarReducer(state, action); }); }); diff --git a/src/app/header/header.reducer.ts b/src/app/navbar/navbar.reducer.ts similarity index 54% rename from src/app/header/header.reducer.ts rename to src/app/navbar/navbar.reducer.ts index 2c8a89249b..c3edc80115 100644 --- a/src/app/header/header.reducer.ts +++ b/src/app/navbar/navbar.reducer.ts @@ -1,30 +1,30 @@ -import { HeaderAction, HeaderActionTypes } from './header.actions'; +import { NavbarAction, NavbarActionTypes } from './navbar.actions'; -export interface HeaderState { +export interface NavbarState { navCollapsed: boolean; } -const initialState: HeaderState = { +const initialState: NavbarState = { navCollapsed: true }; -export function headerReducer(state = initialState, action: HeaderAction): HeaderState { +export function navbarReducer(state = initialState, action: NavbarAction): NavbarState { switch (action.type) { - case HeaderActionTypes.COLLAPSE: { + case NavbarActionTypes.COLLAPSE: { return Object.assign({}, state, { navCollapsed: true }); } - case HeaderActionTypes.EXPAND: { + case NavbarActionTypes.EXPAND: { return Object.assign({}, state, { navCollapsed: false }); } - case HeaderActionTypes.TOGGLE: { + case NavbarActionTypes.TOGGLE: { return Object.assign({}, state, { navCollapsed: !state.navCollapsed }); diff --git a/src/app/shared/auth-nav-menu/auth-nav-menu.component.html b/src/app/shared/auth-nav-menu/auth-nav-menu.component.html index cc9b8c410b..217b731e0c 100644 --- a/src/app/shared/auth-nav-menu/auth-nav-menu.component.html +++ b/src/app/shared/auth-nav-menu/auth-nav-menu.component.html @@ -1,6 +1,6 @@