mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-14 05:23:06 +00:00
57053: started on menu
This commit is contained in:
37
resources/images/dspace-logo.svg
Normal file
37
resources/images/dspace-logo.svg
Normal file
@@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="231.892px" height="167.458px" viewBox="0 0 231.892 167.458" enable-background="new 0 0 231.892 167.458"
|
||||
xml:space="preserve">
|
||||
<path fill="#6D6E71" d="M51.733,143.32c0-1.941,1.651-3.203,4.563-3.203c3.302,0,6.798,1.116,9.419,3.543l3.835-5.146
|
||||
c-3.203-2.962-7.476-4.516-12.622-4.516c-7.622,0-12.284,4.468-12.284,9.856c0,12.187,18.644,8.254,18.644,13.886
|
||||
c0,1.894-1.797,3.593-5.632,3.593c-4.466,0-8.011-2.039-10.292-4.419l-3.787,5.39c3.058,3.059,7.525,5.154,13.788,5.154
|
||||
c8.691,0,12.964-4.474,12.964-10.397C70.329,144.971,51.733,148.418,51.733,143.32z M100.682,134.484H85.534v32.385h6.894v-11.556
|
||||
h8.254c6.991,0,10.875-4.759,10.875-10.391C111.558,139.242,107.722,134.484,100.682,134.484z M99.71,149.244h-7.283v-8.69h7.283
|
||||
c2.719,0,4.807,1.651,4.807,4.369C104.518,147.593,102.43,149.244,99.71,149.244z M180.759,140.067c3.301,0,6.214,2.089,7.573,4.71
|
||||
l5.923-2.913c-2.281-4.078-6.408-7.914-13.496-7.914c-9.759,0-17.284,6.75-17.284,16.75c0,9.954,7.525,16.759,17.284,16.759
|
||||
c7.088,0,11.215-3.94,13.496-7.97l-5.923-2.865c-1.359,2.622-4.272,4.71-7.573,4.71c-5.924,0-10.195-4.516-10.195-10.634
|
||||
C170.564,144.583,174.835,140.067,180.759,140.067z M131.958,134.484l-12.486,32.385h7.824l2.04-5.486h13.886l2.039,5.486h7.816
|
||||
l-12.479-32.385H131.958z M131.228,155.313l5.05-13.935l5.05,13.935H131.228z M231.892,140.553v-6.069h-22.916v32.385h22.916v-6.069
|
||||
H215.87v-7.38h15.683v-6.069H215.87v-6.797H231.892z"/>
|
||||
<path fill="#92C642" d="M29.956,150.652c0-9.71-7.04-16.168-17.187-16.168H0v32.385h12.817
|
||||
C22.916,166.869,29.956,160.458,29.956,150.652z M12.769,160.799H6.894v-20.246h5.924c6.603,0,10.098,4.418,10.098,10.099
|
||||
C22.916,156.187,19.177,160.799,12.769,160.799z"/>
|
||||
<path fill="#92C642" d="M120.726,58.569l0.11-0.006l0.116-0.01l0.106-0.013l0.111-0.01l0.11-0.023l0.109-0.019l0.107-0.023
|
||||
l0.106-0.029l0.106-0.023l0.106-0.033l0.103-0.034l0.096-0.035l0.104-0.04l0.101-0.042l0.099-0.042v-0.001l0.096-0.045v0
|
||||
l0.095-0.044l0.096-0.049l0.091-0.056v-0.001l0.094-0.05v-0.002l0.09-0.056v-0.001l0.092-0.06l0.083-0.056v-0.001l0.085-0.063
|
||||
l0.088-0.065v-0.002l0.087-0.063v-0.001c0.817-0.683,1.393-1.646,1.561-2.738l0.012-0.104v-0.009l0.014-0.101v-0.011l0.009-0.098
|
||||
v-0.012l0.009-0.101V54.38l0.005-0.095v-0.016l0.002-0.105v-16.46l-0.002-0.105v-0.016l-0.005-0.095v-0.013l-0.009-0.101v-0.012
|
||||
l-0.009-0.098v-0.011l-0.014-0.1v-0.01l-0.012-0.104c-0.167-1.092-0.744-2.057-1.561-2.738v-0.001l-0.087-0.063v-0.002l-0.088-0.065
|
||||
l-0.085-0.063v-0.001l-0.083-0.056l-0.092-0.061v0l-0.09-0.056v-0.003l-0.094-0.05v-0.001l-0.091-0.056l-0.096-0.049l-0.095-0.043
|
||||
v-0.001l-0.096-0.045v-0.001l-0.099-0.043l-0.101-0.042l-0.104-0.04l-0.096-0.035l-0.103-0.031l-0.106-0.036l-0.106-0.023
|
||||
l-0.106-0.028l-0.107-0.024l-0.109-0.019l-0.11-0.023l-0.111-0.009l-0.106-0.014l-0.116-0.01l-0.11-0.006l-0.114-0.005h-7.89
|
||||
c-9.715,0-15.858-7.838-15.858-17.15V6.92c0-3.812-3.102-6.915-6.914-6.915H74.085c-3.814,0-6.92,3.106-6.92,6.915v16.682
|
||||
c0,3.806,3.104,6.909,6.92,6.909h8.414c9.169,0,16.906,5.95,17.146,15.403v0.04c-0.24,9.453-7.977,15.402-17.146,15.402h-8.414
|
||||
c-3.816,0-6.92,3.103-6.92,6.909v16.682c0,3.809,3.106,6.915,6.92,6.915H89.95c3.812,0,6.914-3.104,6.914-6.915v-9.223
|
||||
c0-9.312,6.144-17.149,15.858-17.149h7.89L120.726,58.569z M154.772,9.956C148.631,3.814,140.15,0,130.816,0h-15.024v17.424h15.024
|
||||
c4.527,0,8.648,1.858,11.64,4.849c2.99,2.99,4.848,7.112,4.848,11.639v24.042c0,4.538-1.852,8.665-4.832,11.655l-0.016-0.016
|
||||
c-2.991,2.991-7.113,4.849-11.64,4.849h-15.024v17.424h15.024c9.333,0,17.815-3.814,23.956-9.956v-0.033
|
||||
c6.142-6.143,9.955-14.614,9.955-23.923V33.912C164.727,24.578,160.914,16.097,154.772,9.956z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 3.9 KiB |
@@ -1,6 +1,7 @@
|
||||
<div class="outer-wrapper">
|
||||
<div class="inner-wrapper">
|
||||
<ds-header></ds-header>
|
||||
<ds-navbar></ds-navbar>
|
||||
|
||||
<ds-notifications-board
|
||||
[options]="config.notifications">
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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
|
||||
];
|
||||
|
@@ -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,
|
||||
|
@@ -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<AppState> = {
|
||||
router: fromRouter.routerReducer,
|
||||
hostWindow: hostWindowReducer,
|
||||
header: headerReducer,
|
||||
navbar: navbarReducer,
|
||||
forms: formReducer,
|
||||
notifications: notificationsReducer,
|
||||
searchSidebar: sidebarReducer,
|
||||
|
@@ -1,18 +1,10 @@
|
||||
<header>
|
||||
<nav class="navbar navbar-dark bg-primary navbar-expand-md">
|
||||
<div [ngClass]="{'clearfix': !(isNavBarCollapsed | async)}">
|
||||
<a class="navbar-brand" routerLink="/home">{{ 'title' | translate }}</a>
|
||||
</div>
|
||||
<button class="navbar-toggler" type="button" (click)="toggle()" aria-controls="collapsingNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon fa fa-bars fa-fw" aria-hidden="true"></span>
|
||||
</button>
|
||||
<div [ngbCollapse]="(isNavBarCollapsed | async)" class="collapse navbar-collapse" id="collapsingNav">
|
||||
<ul class="navbar-nav mr-auto">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" routerLink="/home" routerLinkActive="active"><i class="fa fa-home fa-fw" aria-hidden="true"></i> {{ 'nav.home' | translate }}<span class="sr-only">(current)</span></a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="container">
|
||||
<a class="navbar-brand my-2" routerLink="/home">
|
||||
<img src="assets/images/dspace-logo.svg"/>
|
||||
</a>
|
||||
<nav class="navbar navbar-expand-md float-right p-0">
|
||||
<ds-auth-nav-menu></ds-auth-nav-menu>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
@@ -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;
|
||||
}
|
@@ -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<string>}
|
||||
*/
|
||||
public isAuthenticated: Observable<boolean>;
|
||||
public isNavBarCollapsed: Observable<boolean>;
|
||||
public showAuth = false;
|
||||
|
||||
constructor(
|
||||
private store: Store<AppState>,
|
||||
private windowService: HostWindowService
|
||||
) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
// set loading
|
||||
this.isNavBarCollapsed = this.store.select(navCollapsedSelector);
|
||||
}
|
||||
|
||||
public toggle(): void {
|
||||
this.store.dispatch(new HeaderToggleAction());
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -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
|
46
src/app/navbar/navbar.component.html
Normal file
46
src/app/navbar/navbar.component.html
Normal file
@@ -0,0 +1,46 @@
|
||||
<nav class="navbar navbar-expand-md p-0">
|
||||
<div class="container">
|
||||
<div class="reset-padding">
|
||||
<button class="navbar-toggler" type="button" (click)="toggle()"
|
||||
aria-controls="collapsingNav"
|
||||
aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon fa fa-bars fa-fw" aria-hidden="true"></span>
|
||||
</button>
|
||||
<div [ngbCollapse]="(isNavBarCollapsed | async)" class="collapse navbar-collapse"
|
||||
id="collapsingNav">
|
||||
<ul class="navbar-nav mr-auto">
|
||||
<li ngbDropdown class="nav-item">
|
||||
<a href="#" ngbDropdownToggle class="nav-link" routerLinkActive="active"
|
||||
id="browseDropdown" (click)="$event.preventDefault();"
|
||||
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">{{
|
||||
'nav.browse.header' | translate }}</a>
|
||||
<div ngbDropdownMenu aria-labelledby="browseDropdown"
|
||||
class="rounded-0 m-0 border-0">
|
||||
<a class="dropdown-item" href="/communitylist">Communities &
|
||||
Collections</a>
|
||||
<a class="dropdown-item" href="/browse?type=dateissued">By Issue
|
||||
Date</a>
|
||||
<a class="dropdown-item" href="/browse?type=author">Authors</a>
|
||||
</div>
|
||||
</li>
|
||||
<li ngbDropdown class="nav-item">
|
||||
<a href="#" ngbDropdownToggle class="nav-link" routerLinkActive="active"
|
||||
id="scopeBrowseDropdown" (click)="$event.preventDefault();"
|
||||
data-toggle="dropdown" aria-haspopup="true"
|
||||
aria-expanded="false">{{ 'nav.communityBrowse.header' | translate }}</a>
|
||||
<div ngbDropdownMenu aria-labelledby="scopeBrowseDropdown"
|
||||
class="rounded-0 m-0 border-0">
|
||||
<a class="dropdown-item" href="/browse?type=dateissued">By Issue
|
||||
Date</a>
|
||||
<a class="dropdown-item" href="/browse?type=author">Authors</a>
|
||||
</div>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" routerLink="/home" routerLinkActive="active">{{
|
||||
'nav.statistics' | translate }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
18
src/app/navbar/navbar.component.scss
Normal file
18
src/app/navbar/navbar.component.scss
Normal file
@@ -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;
|
||||
}
|
@@ -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<HeaderComponent>;
|
||||
let store: Store<HeaderState>;
|
||||
let comp: NavbarComponent;
|
||||
let fixture: ComponentFixture<NavbarComponent>;
|
||||
let store: Store<NavbarState>;
|
||||
|
||||
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());
|
||||
});
|
||||
|
||||
});
|
34
src/app/navbar/navbar.component.ts
Normal file
34
src/app/navbar/navbar.component.ts
Normal file
@@ -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<boolean>;
|
||||
|
||||
constructor(
|
||||
private store: Store<AppState>,
|
||||
) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
// set loading
|
||||
this.isNavBarCollapsed = this.store.select(navCollapsedSelector);
|
||||
}
|
||||
|
||||
public toggle(): void {
|
||||
this.store.dispatch(new NavbarToggleAction());
|
||||
}
|
||||
|
||||
}
|
@@ -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<any>;
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
});
|
@@ -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) {
|
||||
|
@@ -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);
|
||||
});
|
||||
|
||||
});
|
@@ -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
|
||||
});
|
@@ -1,6 +1,6 @@
|
||||
<ul class="navbar-nav" [ngClass]="{'mr-auto': (isXsOrSm$ | async)}">
|
||||
<li *ngIf="!(isAuthenticated | async) && !(isXsOrSm$ | async) && (showAuth | async)" class="nav-item dropdown" (click)="$event.stopPropagation();">
|
||||
<div ngbDropdown placement="bottom-right" class="d-inline-block float-right" @fadeInOut>
|
||||
<li *ngIf="!(isAuthenticated | async) && !(isXsOrSm$ | async) && (showAuth | async)" class="nav-item" (click)="$event.stopPropagation();">
|
||||
<div ngbDropdown placement="bottom-right" class="d-inline-block" @fadeInOut>
|
||||
<a href="#" id="dropdownLogin" class="nav-link" (click)="$event.preventDefault()" ngbDropdownToggle><i class="fa fa-sign-in fa-fw" aria-hidden="true"></i> {{ 'nav.login' | translate }}<span class="caret"></span></a>
|
||||
<div id="loginDropdownMenu" [ngClass]="{'pl-3 pr-3': (loading | async)}" ngbDropdownMenu aria-labelledby="dropdownLogin">
|
||||
<ds-log-in></ds-log-in>
|
||||
@@ -11,7 +11,7 @@
|
||||
<a id="loginLink" class="nav-link" routerLink="/login" routerLinkActive="active"><i class="fa fa-sign-in fa-fw" aria-hidden="true"></i> {{ 'nav.login' | translate }}<span class="sr-only">(current)</span></a>
|
||||
</li>
|
||||
<li *ngIf="(isAuthenticated | async) && !(isXsOrSm$ | async) && (showAuth | async)" class="nav-item">
|
||||
<div ngbDropdown placement="bottom-right" class="d-inline-block" [ngClass]="{'float-right': !(isXsOrSm$ | async)}" @fadeInOut>
|
||||
<div ngbDropdown placement="bottom-right" class="d-inline-block" @fadeInOut>
|
||||
<a href="#" id="dropdownUser" class="nav-link" (click)="$event.preventDefault()" ngbDropdownToggle><i class="fa fa-user fa-fw" aria-hidden="true"></i>Hello {{(user | async).name}}<span class="caret"></span></a>
|
||||
<div id="logoutDropdownMenu" ngbDropdownMenu aria-labelledby="dropdownUser">
|
||||
<ds-log-out></ds-log-out>
|
||||
|
@@ -9,3 +9,7 @@ $drop-zone-area-inner-z-index: 1021;
|
||||
$login-logo-height:72px;
|
||||
$login-logo-width:72px;
|
||||
|
||||
$main-z-index: 0;
|
||||
$nav-z-index: 10;
|
||||
|
||||
$header-logo-height: 80px;
|
||||
|
Reference in New Issue
Block a user