Skip to content

Commit

Permalink
feat(settings): Toggle sticky header
Browse files Browse the repository at this point in the history
  • Loading branch information
Jeremy Kairis committed Sep 17, 2018
1 parent f8cc284 commit 006d749
Show file tree
Hide file tree
Showing 13 changed files with 130 additions and 69 deletions.
110 changes: 55 additions & 55 deletions src/app/app.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,70 +16,70 @@
</mat-nav-list>
</mat-sidenav>

<div class="toolbar">
<mat-toolbar color="primary">
<button mat-icon-button class="d-md-none" (click)="sidenav.open()">
<mat-icon fontSet="fas" fontIcon="fa-bars"></mat-icon>
</button>
<div class="wrapper">

<span routerLink="" class="branding spacer center d-inline d-sm-none">
<img [src]="logo"/></span>
<span routerLink=""
class="branding spacer center d-none d-sm-inline d-md-none"><img
[src]="logo"/> {{ 'anms.title.short' | translate }}</span>
<span routerLink="" class="branding spacer d-none d-md-inline"><img
[src]="logo"/> {{ 'anms.title' | translate }}</span>
<div class="toolbar" [style.position]="isHeaderSticky ? 'fixed' : 'inherit'" [class.mat-elevation-z4]="isHeaderSticky">
<mat-toolbar color="primary">
<button mat-icon-button class="d-md-none" (click)="sidenav.open()">
<mat-icon fontSet="fas" fontIcon="fa-bars"></mat-icon>
</button>

<span class="d-none d-md-inline">
<button mat-button class="nav-button" *ngFor="let item of navigation"
[routerLink]="[item.link]" routerLinkActive="active">
{{item.label | translate}}
</button>
</span>
<span routerLink="" class="branding spacer center d-inline d-sm-none">
<img [src]="logo"/></span>
<span routerLink=""
class="branding spacer center d-none d-sm-inline d-md-none"><img
[src]="logo"/> {{ 'anms.title.short' | translate }}</span>
<span routerLink="" class="branding spacer d-none d-md-inline"><img
[src]="logo"/> {{ 'anms.title' | translate }}</span>

<button mat-button class="sign-in-button "
*ngIf="!isAuthenticated"
(click)="onLoginClick()">
{{ 'anms.menu.login' | translate }}
</button>
<span class="d-none d-md-inline">
<button mat-button class="nav-button" *ngFor="let item of navigation"
[routerLink]="[item.link]" routerLinkActive="active">
{{item.label | translate}}
</button>
</span>

<button *ngIf="isAuthenticated"
mat-icon-button
[matMenuTriggerFor]="toolbarUserMenu">
<mat-icon fontSet="fas" fontIcon="fa-user-circle"></mat-icon>
</button>
<mat-menu #toolbarUserMenu="matMenu">
<button mat-menu-item (click)="onLogoutClick()">
<mat-icon fontSet="fas" fontIcon="fa-power-off"></mat-icon>
<span>{{ 'anms.menu.logout' | translate }}</span>
<button mat-button class="sign-in-button "
*ngIf="!isAuthenticated"
(click)="onLoginClick()">
{{ 'anms.menu.login' | translate }}
</button>
</mat-menu>

<button mat-icon-button routerLink="settings" class="d-none d-sm-inline">
<mat-icon fontSet="fas" fontIcon="fa-cog"></mat-icon>
</button>
<button *ngIf="isAuthenticated"
mat-icon-button
[matMenuTriggerFor]="toolbarUserMenu">
<mat-icon fontSet="fas" fontIcon="fa-user-circle"></mat-icon>
</button>
<mat-menu #toolbarUserMenu="matMenu">
<button mat-menu-item (click)="onLogoutClick()">
<mat-icon fontSet="fas" fontIcon="fa-power-off"></mat-icon>
<span>{{ 'anms.menu.logout' | translate }}</span>
</button>
</mat-menu>

<a [matTooltip]="'anms.header.github' | translate"
matTooltipPosition="before"
mat-icon-button
class="link d-none d-sm-inline"
href="https://github.com/tomastrajan/angular-ngrx-material-starter"
target="_blank">
<mat-icon fontSet="fab" fontIcon="fa-github"></mat-icon>
</a>
<button mat-icon-button routerLink="settings" class="d-none d-sm-inline">
<mat-icon fontSet="fas" fontIcon="fa-cog"></mat-icon>
</button>

<span>
<mat-select [ngModel]="settings?.language"
(selectionChange)="onLanguageSelect($event)">
<mat-option *ngFor="let l of languages" [value]="l">
{{ l.toUpperCase() }}
</mat-option>
</mat-select>
</span>
</mat-toolbar>
</div>
<a [matTooltip]="'anms.header.github' | translate"
matTooltipPosition="before"
mat-icon-button
class="link d-none d-sm-inline"
href="https://github.com/tomastrajan/angular-ngrx-material-starter"
target="_blank">
<mat-icon fontSet="fab" fontIcon="fa-github"></mat-icon>
</a>

<div class="wrapper">
<span>
<mat-select [ngModel]="settings?.language"
(selectionChange)="onLanguageSelect($event)">
<mat-option *ngFor="let l of languages" [value]="l">
{{ l.toUpperCase() }}
</mat-option>
</mat-select>
</span>
</mat-toolbar>
</div>

<div class="content"
[@routeAnimations]="o.isActivated && o.activatedRoute.routeConfig.path">
Expand Down
13 changes: 7 additions & 6 deletions src/app/app.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ mat-sidenav-container {
position: fixed;
width: 100%;
display: flex;
z-index: 10;

.nav-button {
margin: 0 10px 0 0;
Expand Down Expand Up @@ -63,18 +64,18 @@ mat-sidenav-container {

.wrapper {
position: absolute;
top: 64px;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
flex-direction: column;
overflow-y: auto;
overflow-x: hidden;

.content {
position: relative;
flex: 1 0 auto;
margin-top: 64px;
overflow-y: auto;
overflow-x: hidden;
}

.footer {
Expand Down Expand Up @@ -150,8 +151,8 @@ mat-sidenav-container {
}

@media (max-width: $toolbar-breakpoint) {
.wrapper {
top: 56px;
.content {
margin-top: 56px !important;
}
}
}
Expand Down
6 changes: 6 additions & 0 deletions src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export class AppComponent implements OnInit, OnDestroy {

settings: SettingsState;
isAuthenticated: boolean;
isHeaderSticky: boolean;

constructor(
public overlayContainer: OverlayContainer,
Expand Down Expand Up @@ -119,6 +120,7 @@ export class AppComponent implements OnInit, OnDestroy {
.subscribe(settings => {
this.settings = settings;
this.setTheme(settings);
this.setStickyHeader(settings);
this.setLanguage(settings);
this.animationService.updateRouteAnimationType(
settings.pageAnimations,
Expand All @@ -145,6 +147,10 @@ export class AppComponent implements OnInit, OnDestroy {
classList.add(effectiveTheme);
}

private setStickyHeader(settings: SettingsState) {
this.isHeaderSticky = settings.stickyHeader;
}

private setLanguage(settings: SettingsState) {
const { language } = settings;
if (language) {
Expand Down
9 changes: 9 additions & 0 deletions src/app/settings/components/settings-container.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,15 @@ <h2>{{ 'anms.settings.general' | translate }}</h2>
</mat-select>
</mat-form-field>
</div>
<div class="icon-form-field">
<mat-icon fontSet="fas" fontIcon="fa-bars" color="accent"></mat-icon>
<mat-placeholder>{{ 'anms.settings.themes.sticky-header' | translate }}
</mat-placeholder>
<mat-slide-toggle
[checked]="settings?.stickyHeader"
(change)="onStickyHeaderToggle($event)">
</mat-slide-toggle>
</div>
</div>
<div class="col-md-6 group">
</div>
Expand Down
26 changes: 21 additions & 5 deletions src/app/settings/components/settings-container.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import {
ActionSettingsChangeAnimationsElements,
ActionSettingsChangeAnimationsPage,
ActionSettingsChangeAutoNightMode,
ActionSettingsChangeTheme
ActionSettingsChangeTheme,
ActionSettingsChangeStickyHeader
} from '../settings.actions';

describe('SettingsComponent', () => {
Expand All @@ -36,6 +37,7 @@ describe('SettingsComponent', () => {
settings: {
theme: 'DEFAULT-THEME',
autoNightMode: true,
stickyHeader: true,
pageAnimations: true,
pageAnimationsDisabled: false,
elementsAnimations: true,
Expand All @@ -55,6 +57,20 @@ describe('SettingsComponent', () => {
expect(component.settings.pageAnimations).toBeTruthy();
});

it('should dispatch change sticky header on sticky header toggle', () => {
dispatchSpy = spyOn(store, 'dispatch');
const componentDebug = fixture.debugElement;
const slider = componentDebug.queryAll(By.directive(MatSlideToggle))[0];

slider.triggerEventHandler('change', { checked: false });
fixture.detectChanges();

expect(dispatchSpy).toHaveBeenCalledTimes(2);
expect(dispatchSpy).toHaveBeenCalledWith(
new ActionSettingsChangeStickyHeader({ stickyHeader: false })
);
});

it('should dispatch change theme action on theme selection', () => {
dispatchSpy = spyOn(store, 'dispatch');
getThemeSelectArrow().triggerEventHandler('click', {});
Expand All @@ -74,7 +90,7 @@ describe('SettingsComponent', () => {
it('should dispatch change auto night mode on night mode toggle', () => {
dispatchSpy = spyOn(store, 'dispatch');
const componentDebug = fixture.debugElement;
const slider = componentDebug.queryAll(By.directive(MatSlideToggle))[0];
const slider = componentDebug.queryAll(By.directive(MatSlideToggle))[1];

slider.triggerEventHandler('change', { checked: false });
fixture.detectChanges();
Expand All @@ -88,7 +104,7 @@ describe('SettingsComponent', () => {
it('should dispatch change animations page', () => {
dispatchSpy = spyOn(store, 'dispatch');
const componentDebug = fixture.debugElement;
const slider = componentDebug.queryAll(By.directive(MatSlideToggle))[1];
const slider = componentDebug.queryAll(By.directive(MatSlideToggle))[2];

slider.triggerEventHandler('change', { checked: false });
fixture.detectChanges();
Expand All @@ -102,7 +118,7 @@ describe('SettingsComponent', () => {
it('should dispatch change animations elements', () => {
dispatchSpy = spyOn(store, 'dispatch');
const componentDebug = fixture.debugElement;
const slider = componentDebug.queryAll(By.directive(MatSlideToggle))[2];
const slider = componentDebug.queryAll(By.directive(MatSlideToggle))[3];

slider.triggerEventHandler('change', { checked: false });
fixture.detectChanges();
Expand All @@ -128,7 +144,7 @@ describe('SettingsComponent', () => {

dispatchSpy = spyOn(store, 'dispatch');
const componentDebug = fixture.debugElement;
const slider = componentDebug.queryAll(By.directive(MatSlideToggle))[1];
const slider = componentDebug.queryAll(By.directive(MatSlideToggle))[2];

slider.triggerEventHandler('change', { checked: false });
fixture.detectChanges();
Expand Down
8 changes: 7 additions & 1 deletion src/app/settings/components/settings-container.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import {
ActionSettingsChangeAutoNightMode,
ActionSettingsChangeLanguage,
ActionSettingsChangeTheme,
ActionSettingsPersist
ActionSettingsPersist,
ActionSettingsChangeStickyHeader
} from '../settings.actions';
import { SettingsState } from '../settings.model';
import { selectSettings } from '../settings.selectors';
Expand Down Expand Up @@ -69,6 +70,11 @@ export class SettingsContainerComponent implements OnInit, OnDestroy {
this.store.dispatch(new ActionSettingsPersist({ settings: this.settings }));
}

onStickyHeaderToggle({ checked: stickyHeader }) {
this.store.dispatch(new ActionSettingsChangeStickyHeader({ stickyHeader }));
this.store.dispatch(new ActionSettingsPersist({ settings: this.settings }));
}

onPageAnimationsToggle({ checked: pageAnimations }) {
this.store.dispatch(
new ActionSettingsChangeAnimationsPage({ pageAnimations })
Expand Down
10 changes: 9 additions & 1 deletion src/app/settings/settings.actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export enum SettingsActionTypes {
CHANGE_LANGUAGE = '[Settings] Change Language',
CHANGE_THEME = '[Settings] Change Theme',
CHANGE_AUTO_NIGHT_AUTO_MODE = '[Settings] Change Auto Night Mode',
CHANGE_STICKY_HEADER = '[Settings] Change Sticky Header',
CHANGE_ANIMATIONS_PAGE = '[Settings] Change Animations Page',
CHANGE_ANIMATIONS_PAGE_DISABLED = '[Settings] Change Animations Page Disabled',
CHANGE_ANIMATIONS_ELEMENTS = '[Settings] Change Animations Elements',
Expand All @@ -30,6 +31,12 @@ export class ActionSettingsChangeAutoNightMode implements Action {
constructor(readonly payload: { autoNightMode: boolean }) {}
}

export class ActionSettingsChangeStickyHeader implements Action {
readonly type = SettingsActionTypes.CHANGE_STICKY_HEADER;

constructor(readonly payload: { stickyHeader: boolean }) {}
}

export class ActionSettingsChangeAnimationsPage implements Action {
readonly type = SettingsActionTypes.CHANGE_ANIMATIONS_PAGE;

Expand Down Expand Up @@ -61,4 +68,5 @@ export type SettingsActions =
| ActionSettingsChangeAnimationsPage
| ActionSettingsChangeAnimationsPageDisabled
| ActionSettingsChangeAnimationsElements
| ActionSettingsChangeAutoNightMode;
| ActionSettingsChangeAutoNightMode
| ActionSettingsChangeStickyHeader;
1 change: 1 addition & 0 deletions src/app/settings/settings.effects.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ describe('SettingsEffects', () => {
elementsAnimations: true,
theme: 'default',
autoNightMode: false,
stickyHeader: false,
pageAnimationsDisabled: true
};

Expand Down
1 change: 1 addition & 0 deletions src/app/settings/settings.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export interface SettingsState {
language: string;
theme: string;
autoNightMode: boolean;
stickyHeader: boolean;
pageAnimations: boolean;
pageAnimationsDisabled: boolean;
elementsAnimations: boolean;
Expand Down
11 changes: 10 additions & 1 deletion src/app/settings/settings.reducer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import {
ActionSettingsChangeAnimationsPageDisabled,
ActionSettingsChangeAutoNightMode,
ActionSettingsChangeLanguage,
ActionSettingsChangeTheme
ActionSettingsChangeTheme,
ActionSettingsChangeStickyHeader
} from './settings.actions';

describe('SettingsReducer', () => {
Expand Down Expand Up @@ -60,4 +61,12 @@ describe('SettingsReducer', () => {
const state = settingsReducer(undefined, action);
expect(state.autoNightMode).toEqual(true);
});

it('should update stickyHeader', () => {
const action = new ActionSettingsChangeStickyHeader({
stickyHeader: false
});
const state = settingsReducer(undefined, action);
expect(state.stickyHeader).toEqual(false);
});
});
2 changes: 2 additions & 0 deletions src/app/settings/settings.reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export const initialState: SettingsState = {
language: 'en',
theme: 'DEFAULT-THEME',
autoNightMode: false,
stickyHeader: true,
pageAnimations: true,
pageAnimationsDisabled: false,
elementsAnimations: true
Expand All @@ -18,6 +19,7 @@ export function settingsReducer(
case SettingsActionTypes.CHANGE_LANGUAGE:
case SettingsActionTypes.CHANGE_THEME:
case SettingsActionTypes.CHANGE_AUTO_NIGHT_AUTO_MODE:
case SettingsActionTypes.CHANGE_STICKY_HEADER:
case SettingsActionTypes.CHANGE_ANIMATIONS_PAGE:
case SettingsActionTypes.CHANGE_ANIMATIONS_ELEMENTS:
return { ...state, ...action.payload };
Expand Down
1 change: 1 addition & 0 deletions src/assets/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
"anms.settings.themes.nature": "Nature",
"anms.settings.themes.dark": "Dark",
"anms.settings.themes.night-mode": "Auto night mode (from 21:00 to 7:00)",
"anms.settings.themes.sticky-header": "Sticky header",
"anms.settings.animations": "Animations",
"anms.settings.animations.page": "Navigation whole page transition",
"anms.settings.animations.elements": "Navigation page elements slide up"
Expand Down

0 comments on commit 006d749

Please sign in to comment.