
committed by
Pete Bacon Darwin

parent
f5b2ce0206
commit
368169dc15
@ -1,3 +1,7 @@
|
|||||||
|
<div *ngIf="isFetching" class="progress-bar-container">
|
||||||
|
<md-progress-bar mode="indeterminate" color="warn"></md-progress-bar>
|
||||||
|
</div>
|
||||||
|
|
||||||
<md-toolbar color="primary" class="app-toolbar">
|
<md-toolbar color="primary" class="app-toolbar">
|
||||||
<button class="hamburger" md-button
|
<button class="hamburger" md-button
|
||||||
(click)="sidenav.toggle()" title="Docs menu">
|
(click)="sidenav.toggle()" title="Docs menu">
|
||||||
|
@ -3,6 +3,7 @@ import { async, inject, ComponentFixture, TestBed, fakeAsync, tick } from '@angu
|
|||||||
import { Title } from '@angular/platform-browser';
|
import { Title } from '@angular/platform-browser';
|
||||||
import { APP_BASE_HREF } from '@angular/common';
|
import { APP_BASE_HREF } from '@angular/common';
|
||||||
import { Http } from '@angular/http';
|
import { Http } from '@angular/http';
|
||||||
|
import { MdProgressBar } from '@angular/material';
|
||||||
import { By } from '@angular/platform-browser';
|
import { By } from '@angular/platform-browser';
|
||||||
|
|
||||||
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
|
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
|
||||||
@ -36,18 +37,24 @@ describe('AppComponent', () => {
|
|||||||
let locationService: MockLocationService;
|
let locationService: MockLocationService;
|
||||||
let sidenav: HTMLElement;
|
let sidenav: HTMLElement;
|
||||||
|
|
||||||
beforeEach(() => {
|
const initializeTest = () => {
|
||||||
createTestingModule('a/b');
|
|
||||||
|
|
||||||
fixture = TestBed.createComponent(AppComponent);
|
fixture = TestBed.createComponent(AppComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
component.onResize(1033); // wide by default
|
component.onResize(1033); // wide by default
|
||||||
|
|
||||||
docViewer = fixture.debugElement.query(By.css('aio-doc-viewer')).nativeElement;
|
docViewer = fixture.debugElement.query(By.css('aio-doc-viewer')).nativeElement;
|
||||||
hamburger = fixture.debugElement.query(By.css('.hamburger')).nativeElement;
|
hamburger = fixture.debugElement.query(By.css('.hamburger')).nativeElement;
|
||||||
locationService = fixture.debugElement.injector.get(LocationService) as any;
|
locationService = fixture.debugElement.injector.get(LocationService) as any;
|
||||||
sidenav = fixture.debugElement.query(By.css('md-sidenav')).nativeElement;
|
sidenav = fixture.debugElement.query(By.css('md-sidenav')).nativeElement;
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('with proper DocViewer', () => {
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
createTestingModule('a/b');
|
||||||
|
initializeTest();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it('should create', () => {
|
||||||
@ -403,30 +410,6 @@ describe('AppComponent', () => {
|
|||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('initial rendering', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
createTestingModule('a/b');
|
|
||||||
// Remove the DocViewer for this test and hide the missing component message
|
|
||||||
TestBed.overrideModule(AppModule, {
|
|
||||||
remove: { declarations: [DocViewerComponent] },
|
|
||||||
add: { schemas: [NO_ERRORS_SCHEMA] }
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should initially add the starting class until the first document is rendered', () => {
|
|
||||||
fixture = TestBed.createComponent(AppComponent);
|
|
||||||
fixture.detectChanges();
|
|
||||||
|
|
||||||
expect(fixture.componentInstance.isStarting).toBe(true);
|
|
||||||
expect(fixture.debugElement.query(By.css('md-sidenav-container')).classes['starting']).toBe(true);
|
|
||||||
|
|
||||||
fixture.debugElement.query(By.css('aio-doc-viewer')).triggerEventHandler('docRendered', {});
|
|
||||||
fixture.detectChanges();
|
|
||||||
expect(fixture.componentInstance.isStarting).toBe(false);
|
|
||||||
expect(fixture.debugElement.query(By.css('md-sidenav-container')).classes['starting']).toBe(false);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('click intercepting', () => {
|
describe('click intercepting', () => {
|
||||||
it('should intercept clicks on anchors and call `location.handleAnchorClick()`',
|
it('should intercept clicks on anchors and call `location.handleAnchorClick()`',
|
||||||
inject([LocationService], (location: LocationService) => {
|
inject([LocationService], (location: LocationService) => {
|
||||||
@ -581,6 +564,151 @@ describe('AppComponent', () => {
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('with mocked DocViewer', () => {
|
||||||
|
const getDocViewer = () => fixture.debugElement.query(By.css('aio-doc-viewer'));
|
||||||
|
const triggerDocRendered = () => getDocViewer().triggerEventHandler('docRendered', {});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
createTestingModule('a/b');
|
||||||
|
// Remove the DocViewer for this test and hide the missing component message
|
||||||
|
TestBed.overrideModule(AppModule, {
|
||||||
|
remove: { declarations: [DocViewerComponent] },
|
||||||
|
add: { schemas: [NO_ERRORS_SCHEMA] }
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('initial rendering', () => {
|
||||||
|
it('should initially add the starting class until the first document is rendered', fakeAsync(() => {
|
||||||
|
const getSidenavContainer = () => fixture.debugElement.query(By.css('md-sidenav-container'));
|
||||||
|
|
||||||
|
initializeTest();
|
||||||
|
|
||||||
|
expect(component.isStarting).toBe(true);
|
||||||
|
expect(getSidenavContainer().classes['starting']).toBe(true);
|
||||||
|
|
||||||
|
triggerDocRendered();
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(component.isStarting).toBe(true);
|
||||||
|
expect(getSidenavContainer().classes['starting']).toBe(true);
|
||||||
|
|
||||||
|
tick(499);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(component.isStarting).toBe(true);
|
||||||
|
expect(getSidenavContainer().classes['starting']).toBe(true);
|
||||||
|
|
||||||
|
tick(2);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(component.isStarting).toBe(false);
|
||||||
|
expect(getSidenavContainer().classes['starting']).toBe(false);
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('progress bar', () => {
|
||||||
|
const SHOW_DELAY = 200;
|
||||||
|
const HIDE_DELAY = 500;
|
||||||
|
const getProgressBar = () => fixture.debugElement.query(By.directive(MdProgressBar));
|
||||||
|
const initializeAndCompleteNavigation = () => {
|
||||||
|
initializeTest();
|
||||||
|
triggerDocRendered();
|
||||||
|
tick(HIDE_DELAY);
|
||||||
|
};
|
||||||
|
|
||||||
|
it('should initially be hidden', () => {
|
||||||
|
initializeTest();
|
||||||
|
expect(getProgressBar()).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be shown (after a delay) when the path changes', fakeAsync(() => {
|
||||||
|
initializeAndCompleteNavigation();
|
||||||
|
locationService.urlSubject.next('c/d');
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(getProgressBar()).toBeFalsy();
|
||||||
|
|
||||||
|
tick(SHOW_DELAY - 1);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(getProgressBar()).toBeFalsy();
|
||||||
|
|
||||||
|
tick(1);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(getProgressBar()).toBeTruthy();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should not be shown when the URL changes but the path remains the same', fakeAsync(() => {
|
||||||
|
initializeAndCompleteNavigation();
|
||||||
|
locationService.urlSubject.next('a/b');
|
||||||
|
|
||||||
|
tick(SHOW_DELAY);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(getProgressBar()).toBeFalsy();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should not be shown if the doc is rendered quickly', fakeAsync(() => {
|
||||||
|
initializeAndCompleteNavigation();
|
||||||
|
locationService.urlSubject.next('c/d');
|
||||||
|
|
||||||
|
tick(SHOW_DELAY - 1);
|
||||||
|
triggerDocRendered();
|
||||||
|
|
||||||
|
tick(1);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(getProgressBar()).toBeFalsy();
|
||||||
|
|
||||||
|
tick(HIDE_DELAY); // Fire the remaining timer or `fakeAsync()` complains.
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should be shown if rendering the doc takes too long', fakeAsync(() => {
|
||||||
|
initializeAndCompleteNavigation();
|
||||||
|
locationService.urlSubject.next('c/d');
|
||||||
|
|
||||||
|
tick(SHOW_DELAY);
|
||||||
|
triggerDocRendered();
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(getProgressBar()).toBeTruthy();
|
||||||
|
|
||||||
|
tick(HIDE_DELAY); // Fire the remaining timer or `fakeAsync()` complains.
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should be hidden (after a delay) once the doc is rendered', fakeAsync(() => {
|
||||||
|
initializeAndCompleteNavigation();
|
||||||
|
locationService.urlSubject.next('c/d');
|
||||||
|
|
||||||
|
tick(SHOW_DELAY);
|
||||||
|
triggerDocRendered();
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(getProgressBar()).toBeTruthy();
|
||||||
|
|
||||||
|
tick(HIDE_DELAY - 1);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(getProgressBar()).toBeTruthy();
|
||||||
|
|
||||||
|
tick(1);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(getProgressBar()).toBeFalsy();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should only take the latest request into account', fakeAsync(() => {
|
||||||
|
initializeAndCompleteNavigation();
|
||||||
|
locationService.urlSubject.next('c/d'); // The URL changes.
|
||||||
|
locationService.urlSubject.next('e/f'); // The URL changes again before `onDocRendered()`.
|
||||||
|
|
||||||
|
tick(SHOW_DELAY - 1); // `onDocRendered()` is triggered (for the last doc),
|
||||||
|
triggerDocRendered(); // before the progress bar is shown.
|
||||||
|
|
||||||
|
tick(1);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(getProgressBar()).toBeFalsy();
|
||||||
|
|
||||||
|
tick(HIDE_DELAY); // Fire the remaining timer or `fakeAsync()` complains.
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
//// test helpers ////
|
//// test helpers ////
|
||||||
|
|
||||||
function createTestingModule(initialUrl: string) {
|
function createTestingModule(initialUrl: string) {
|
||||||
|
@ -54,8 +54,10 @@ export class AppComponent implements OnInit {
|
|||||||
@HostBinding('class')
|
@HostBinding('class')
|
||||||
hostClasses = '';
|
hostClasses = '';
|
||||||
|
|
||||||
|
isFetching = false;
|
||||||
isStarting = true;
|
isStarting = true;
|
||||||
isSideBySide = false;
|
isSideBySide = false;
|
||||||
|
private isFetchingTimeout: any;
|
||||||
private isSideNavDoc = false;
|
private isSideNavDoc = false;
|
||||||
private previousNavView: string;
|
private previousNavView: string;
|
||||||
|
|
||||||
@ -122,6 +124,10 @@ export class AppComponent implements OnInit {
|
|||||||
} else {
|
} else {
|
||||||
// don't scroll; leave that to `onDocRendered`
|
// don't scroll; leave that to `onDocRendered`
|
||||||
this.currentPath = path;
|
this.currentPath = path;
|
||||||
|
|
||||||
|
// Start progress bar if doc not rendered within brief time
|
||||||
|
clearTimeout(this.isFetchingTimeout);
|
||||||
|
this.isFetchingTimeout = setTimeout(() => this.isFetching = true, 200);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -168,10 +174,16 @@ export class AppComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onDocRendered() {
|
onDocRendered() {
|
||||||
|
// Stop fetching timeout (which, when render is fast, means progress bar never shown)
|
||||||
|
clearTimeout(this.isFetchingTimeout);
|
||||||
|
|
||||||
// Scroll 500ms after the doc-viewer has finished rendering the new doc
|
// Scroll 500ms after the doc-viewer has finished rendering the new doc
|
||||||
// The delay is to allow time for async layout to complete
|
// The delay is to allow time for async layout to complete
|
||||||
setTimeout(() => this.autoScroll(), 500);
|
setTimeout(() => {
|
||||||
|
this.autoScroll();
|
||||||
this.isStarting = false;
|
this.isStarting = false;
|
||||||
|
this.isFetching = false;
|
||||||
|
}, 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
onDocVersionChange(versionIndex: number) {
|
onDocVersionChange(versionIndex: number) {
|
||||||
|
@ -5,8 +5,17 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
|||||||
|
|
||||||
import { Location, LocationStrategy, PathLocationStrategy } from '@angular/common';
|
import { Location, LocationStrategy, PathLocationStrategy } from '@angular/common';
|
||||||
|
|
||||||
import { MdToolbarModule, MdButtonModule, MdIconModule, MdInputModule, MdSidenavModule, MdTabsModule, Platform,
|
import {
|
||||||
MdIconRegistry } from '@angular/material';
|
MdButtonModule,
|
||||||
|
MdIconModule,
|
||||||
|
MdIconRegistry,
|
||||||
|
MdInputModule,
|
||||||
|
MdProgressBarModule,
|
||||||
|
MdSidenavModule,
|
||||||
|
MdTabsModule,
|
||||||
|
MdToolbarModule,
|
||||||
|
Platform
|
||||||
|
} from '@angular/material';
|
||||||
|
|
||||||
// Temporary fix for MdSidenavModule issue:
|
// Temporary fix for MdSidenavModule issue:
|
||||||
// crashes with "missing first" operator when SideNav.mode is "over"
|
// crashes with "missing first" operator when SideNav.mode is "over"
|
||||||
@ -67,9 +76,10 @@ export const svgIconProviders = [
|
|||||||
MdButtonModule,
|
MdButtonModule,
|
||||||
MdIconModule,
|
MdIconModule,
|
||||||
MdInputModule,
|
MdInputModule,
|
||||||
MdToolbarModule,
|
MdProgressBarModule,
|
||||||
MdSidenavModule,
|
MdSidenavModule,
|
||||||
MdTabsModule,
|
MdTabsModule,
|
||||||
|
MdToolbarModule,
|
||||||
SwUpdatesModule
|
SwUpdatesModule
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
|
@ -1,3 +1,12 @@
|
|||||||
|
.progress-bar-container {
|
||||||
|
height: 2px;
|
||||||
|
overflow: hidden;
|
||||||
|
position: fixed;
|
||||||
|
top: 64px;
|
||||||
|
width: 100vw;
|
||||||
|
z-index: 5;
|
||||||
|
}
|
||||||
|
|
||||||
.sidenav-content {
|
.sidenav-content {
|
||||||
padding: 1rem 3rem 3rem;
|
padding: 1rem 3rem 3rem;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
@ -12,9 +21,15 @@
|
|||||||
aio-menu {
|
aio-menu {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.progress-bar-container {
|
||||||
|
top: 56px;
|
||||||
|
}
|
||||||
|
|
||||||
.sidenav-content {
|
.sidenav-content {
|
||||||
min-height: 450px;
|
min-height: 450px;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidenav-container {
|
.sidenav-container {
|
||||||
|
Reference in New Issue
Block a user