Skip to content

Commit

Permalink
feat(stark-ui): implementation of the app sidebar
Browse files Browse the repository at this point in the history
  • Loading branch information
catlabs committed Sep 4, 2018
1 parent 6345040 commit 5c30eb7
Show file tree
Hide file tree
Showing 36 changed files with 774 additions and 112 deletions.
21 changes: 21 additions & 0 deletions packages/stark-ui/assets/themes/_menu-theme.scss
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,24 @@
color: map-get($mat-light-theme-foreground, disabled-button);
}
}

/* TODO: This code should be moved when the app menu is implemented */
stark-app-sidebar {
.stark-app-sidenav-menu {
background-color: #16385a;
.mat-nav-list {
padding-top: 0;
.mat-list-item {
color: #fff;
font-size: 13px;
&:hover a {
background-color: rgba($color: #fff, $alpha: 0.3);
}
}
.mat-list-item-focus.active,
a.active {
background-color: rgba($color: #fff, $alpha: 0.4);
}
}
}
}
3 changes: 2 additions & 1 deletion packages/stark-ui/src/modules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ export * from "./modules/action-bar";
export * from "./modules/app-logo";
export * from "./modules/app-logout";
export * from "./modules/breadcrumb";
export * from "./modules/app-sidebar";
export * from "./modules/keyboard-directives";
export * from "./modules/date-picker";
export * from "./modules/date-range-picker";
export * from "./modules/dropdown";
export * from "./modules/keyboard-directives";
export * from "./modules/pretty-print";
export * from "./modules/slider";
export * from "./modules/svg-view-box";
Expand Down
4 changes: 4 additions & 0 deletions packages/stark-ui/src/modules/app-sidebar.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from "./app-sidebar/app-sidebar.module";
export * from "./app-sidebar/components";
export * from "./app-sidebar/services";
export * from "./app-sidebar/testing";
41 changes: 41 additions & 0 deletions packages/stark-ui/src/modules/app-sidebar/app-sidebar.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { NgModule, Optional, SkipSelf } from "@angular/core";
import { CommonModule } from "@angular/common";
import { MatSidenavModule } from "@angular/material/sidenav";
import { StarkAppSidebarComponent } from "./components";
import { STARK_APP_SIDEBAR_SERVICE, StarkAppSidebarServiceImpl } from "./services";
import { ModuleWithProviders } from "@angular/compiler/src/core";

@NgModule({
declarations: [StarkAppSidebarComponent],
imports: [CommonModule, MatSidenavModule],
exports: [StarkAppSidebarComponent]
})
export class StarkAppSidebarModule {
/**
* Instantiates the services only once since they should be singletons
* so the forRoot() should be called only by the AppModule
* @link https://angular.io/guide/singleton-services#forroot
* @returns a module with providers
*/
public static forRoot(): ModuleWithProviders {
return {
ngModule: StarkAppSidebarModule,
providers: [{ provide: STARK_APP_SIDEBAR_SERVICE, useClass: StarkAppSidebarServiceImpl }]
};
}

/**
* Prevents this module from being re-imported
* @link https://angular.io/guide/singleton-services#prevent-reimport-of-the-coremodule
* @param parentModule - the parent module
*/
public constructor(
@Optional()
@SkipSelf()
parentModule: StarkAppSidebarModule
) {
if (parentModule) {
throw new Error("StarkAppSidebarModule is already loaded. Import it in the AppModule only");
}
}
}
1 change: 1 addition & 0 deletions packages/stark-ui/src/modules/app-sidebar/components.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./components/app-sidebar.component";
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/* ============================================================================== */
/* S t a r k A p p S i d e b a r - T h e m e */
/* ============================================================================== */
/* stark-ui: src/modules/app-sidebar/components/_app-sidebar-theme.scss */

/* END stark-ui: src/modules/app-sidebar/components/_app-sidebar-theme.scss */
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/* ============================================================================== */
/* S t a r k A p p S i d e b a r */
/* ============================================================================== */
/* stark-ui: src/modules/app-sidebar/components/_app-sidebar.component.scss */

.stark-app-sidebar {
.mat-sidenav-content {
margin-top: $stark-header-size;
}

.mat-sidenav-container {
min-height: 100%;
}

.mat-sidenav {
display: flex;
flex-direction: column;
justify-content: space-between;
}

.stark-app-sidenav-left {
width: 150px;
}
.stark-app-sidenav-right {
width: 300px;
}
.stark-app-sidenav-menu {
margin-top: $stark-header-size;
width: 200px;
}
}

@media #{$tablet-query} {
.stark-app-sidebar {
.stark-app-sidenav-menu,
.mat-sidenav-content {
margin-top: $stark-header-size-desktop;
}
}
}

/* END stark-ui: src/modules/app-sidebar/components/_app-sidebar.component.scss */
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<mat-sidenav-container #appSidenavContainer>
<mat-sidenav #appSidenavLeft class="stark-app-sidenav-left" [ngClass]="{'stark-app-sidenav-menu': sidenavLeftMode==='menu'}" mode="over" [fixedInViewport]="true"
[fixedBottomGap]="0">
<ng-container *ngIf="sidenavLeftMode==='regular'">
<ng-content class="regular" select="[stark-app-sidenav-left]"></ng-content>
</ng-container>
<ng-container class="menu" *ngIf="sidenavLeftMode==='menu'">
<ng-content select="[stark-app-sidenav-menu]"></ng-content>
</ng-container>
</mat-sidenav>
<mat-sidenav #appSidenavRight class="stark-app-sidenav-right" mode="over" position="end" [fixedInViewport]="true"
[fixedBottomGap]="0">
<ng-content select="[stark-app-sidenav-right]"></ng-content>
</mat-sidenav>
<mat-sidenav-content>
<div class="stark-container">
<ng-content select="[stark-app-sidenav-content]"></ng-content>
</div>
</mat-sidenav-content>
</mat-sidenav-container>
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { CommonModule } from "@angular/common";
import { NoopAnimationsModule } from "@angular/platform-browser/animations";
import { async, ComponentFixture, TestBed } from "@angular/core/testing";
import { MatSidenavModule } from "@angular/material/sidenav";
import { STARK_LOGGING_SERVICE } from "@nationalbankbelgium/stark-core";
import { MockStarkLoggingService } from "@nationalbankbelgium/stark-core/testing";
import { StarkAppSidebarComponent } from "./app-sidebar.component";
import { MockAppSidebarService } from "./../testing/app-sidebar.mock";
import { STARK_APP_SIDEBAR_SERVICE } from "./../services/app-sidebar.service.intf";

describe("AppSidebarComponent", () => {
let fixture: ComponentFixture<StarkAppSidebarComponent>;
let component: StarkAppSidebarComponent;

beforeEach(async(() => {
const mockLogger: MockStarkLoggingService = new MockStarkLoggingService();
return TestBed.configureTestingModule({
declarations: [StarkAppSidebarComponent],
imports: [CommonModule, MatSidenavModule, NoopAnimationsModule],
providers: [
{ provide: STARK_LOGGING_SERVICE, useValue: mockLogger },
{ provide: STARK_APP_SIDEBAR_SERVICE, useValue: new MockAppSidebarService() }
]
}).compileComponents();
}));

beforeEach(() => {
fixture = TestBed.createComponent(StarkAppSidebarComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

describe("sidenavs opening features ", () => {
it("left sidebar should be opened", () => {
component.onOpenSideNav({
sidebar: "left"
});
expect(component.appSidenavLeft.opened).toBe(true);
});

it("right sidebar should be opened", () => {
component.onOpenSideNav({
sidebar: "right"
});
expect(component.appSidenavRight.opened).toBe(true);
});

it("left sidebar should show the menu in menu mode", () => {
component.onOpenSideNav({
mode: "menu",
sidebar: "left"
});
fixture.detectChanges();
const sidenav: HTMLElement = fixture.nativeElement.querySelector(".stark-app-sidenav-menu");
expect(sidenav).toBeTruthy();
});

it("left sidebar should hide the menu in regular mode", () => {
component.onOpenSideNav({
mode: "regular",
sidebar: "left"
});
fixture.detectChanges();
const sidenav: HTMLElement = fixture.nativeElement.querySelector(".stark-app-sidenav-menu");
expect(sidenav).toBeFalsy();
});
});

describe("sidenavs closing features ", () => {
it("left sidebar should close", () => {
component.onOpenSideNav({
sidebar: "left"
});
component.onCloseSideNavs();
expect(component.appSidenavLeft.opened).toBe(false);
});

it("right sidebar should close", () => {
component.onOpenSideNav({
sidebar: "right"
});
component.onCloseSideNavs();
expect(component.appSidenavRight.opened).toBe(false);
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import { Component, HostBinding, Inject, Input, OnDestroy, OnInit, ViewChild, ViewEncapsulation } from "@angular/core";
import { from, Subscription } from "rxjs";
import { MatSidenav, MatSidenavContainer, MatDrawerToggleResult } from "@angular/material/sidenav";
import { STARK_LOGGING_SERVICE, StarkLoggingService } from "@nationalbankbelgium/stark-core";
import { StarkAppSidebarOpenEvent, StarkAppSidebarService, STARK_APP_SIDEBAR_SERVICE } from "../services";

export type StarkAppSidebarLeftMode = "regular" | "menu";

/**
* Name of the component
*/
const componentName: string = "stark-app-sidebar";

/**
* Component to display the application's sidebar
* Only 2 sidebars are allowed: https://github.com/angular/material2/issues/1514
*/
@Component({
selector: "stark-app-sidebar",
templateUrl: "./app-sidebar.component.html",
encapsulation: ViewEncapsulation.None
})
export class StarkAppSidebarComponent implements OnDestroy, OnInit {
/**
* Adds class="stark-app-sidebar" attribute on the host component
*/
@HostBinding("class")
public class: string = componentName;

/**
* Mode for the left sidebar: either the menu is shown or the regular sidebar
*/
@Input()
public sidenavLeftMode: StarkAppSidebarLeftMode;

/**
* Reference to the MatSidenavContainer embedded in this component
*/
@ViewChild("appSidenavContainer")
public appSidenavContainer: MatSidenavContainer;

/**
* Reference to the left MatSidenav embedded in this component
*/
@ViewChild("appSidenavLeft")
public appSidenavLeft: MatSidenav;

/**
* Reference to the right MatSidenav embedded in this component
*/
@ViewChild("appSidenavRight")
public appSidenavRight: MatSidenav;

/**
* Subscription to the open sidebar Observable
*/
public openSidebarSubscription: Subscription;

/**
* Subscription to the close sidebar Observable
*/
public closeSidebarSubscription: Subscription;

/**
* Class constructor
* @param sidebarService - The sidebar service of the application
*/
public constructor(
@Inject(STARK_LOGGING_SERVICE) public logger: StarkLoggingService,
@Inject(STARK_APP_SIDEBAR_SERVICE) public sidebarService: StarkAppSidebarService
) {}

/**
* Component lifecycle OnInit hook
*/
public ngOnInit(): void {
this.logger.debug(componentName + ": component initialized");
this.openSidebarSubscription = this.sidebarService.openSidebar$.subscribe((event: StarkAppSidebarOpenEvent) => {
this.onOpenSideNav(event);
});

this.closeSidebarSubscription = this.sidebarService.closeSidebar$.subscribe(() => {
this.onCloseSideNavs();
});
}

/**
* Component lifecycle OnDestroy hook
* Prevent memory leak when component destroyed
*/
public ngOnDestroy(): void {
this.openSidebarSubscription.unsubscribe();
this.closeSidebarSubscription.unsubscribe();
}

/**
* Open sidenav handler
*/
public onOpenSideNav(event: StarkAppSidebarOpenEvent): void {
if (event.mode) {
this.sidenavLeftMode = event.mode;
}
switch (event.sidebar) {
case "left":
from(this.appSidenavLeft.open()).subscribe(
(result: MatDrawerToggleResult) => this.logger.debug(componentName + ": left sidenav " + result),
(error: Error) => this.logger.warn(componentName + ": ", error)
);
break;
case "right":
from(this.appSidenavRight.open()).subscribe(
(result: MatDrawerToggleResult) => this.logger.debug(componentName + ": right sidebar " + result),
(error: Error) => this.logger.warn(componentName + ": ", error)
);
break;
default:
break;
}
}

/**
* Close sidenav handler
*/
public onCloseSideNavs(): void {
this.appSidenavContainer.close();
}
}
3 changes: 3 additions & 0 deletions packages/stark-ui/src/modules/app-sidebar/services.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from "./services/app-sidebar.service";
export * from "./services/app-sidebar.service.intf";
export * from "./services/app-sidebar-open-event.intf";
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/**
* StarkAppSidebarOpenEvent interface
*/
export interface StarkAppSidebarOpenEvent {
mode?: "regular" | "menu";
sidebar: "left" | "right";
}
Loading

0 comments on commit 5c30eb7

Please sign in to comment.