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">
|
||||
<button class="hamburger" md-button
|
||||
(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 { APP_BASE_HREF } from '@angular/common';
|
||||
import { Http } from '@angular/http';
|
||||
import { MdProgressBar } from '@angular/material';
|
||||
import { By } from '@angular/platform-browser';
|
||||
|
||||
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
|
||||
|
@ -36,18 +37,24 @@ describe('AppComponent', () => {
|
|||
let locationService: MockLocationService;
|
||||
let sidenav: HTMLElement;
|
||||
|
||||
beforeEach(() => {
|
||||
createTestingModule('a/b');
|
||||
|
||||
const initializeTest = () => {
|
||||
fixture = TestBed.createComponent(AppComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.detectChanges();
|
||||
component.onResize(1033); // wide by default
|
||||
|
||||
docViewer = fixture.debugElement.query(By.css('aio-doc-viewer')).nativeElement;
|
||||
hamburger = fixture.debugElement.query(By.css('.hamburger')).nativeElement;
|
||||
locationService = fixture.debugElement.injector.get(LocationService) as any;
|
||||
sidenav = fixture.debugElement.query(By.css('md-sidenav')).nativeElement;
|
||||
};
|
||||
|
||||
describe('with proper DocViewer', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
createTestingModule('a/b');
|
||||
initializeTest();
|
||||
});
|
||||
|
||||
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', () => {
|
||||
it('should intercept clicks on anchors and call `location.handleAnchorClick()`',
|
||||
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 ////
|
||||
|
||||
function createTestingModule(initialUrl: string) {
|
||||
|
|
|
@ -54,8 +54,10 @@ export class AppComponent implements OnInit {
|
|||
@HostBinding('class')
|
||||
hostClasses = '';
|
||||
|
||||
isFetching = false;
|
||||
isStarting = true;
|
||||
isSideBySide = false;
|
||||
private isFetchingTimeout: any;
|
||||
private isSideNavDoc = false;
|
||||
private previousNavView: string;
|
||||
|
||||
|
@ -122,6 +124,10 @@ export class AppComponent implements OnInit {
|
|||
} else {
|
||||
// don't scroll; leave that to `onDocRendered`
|
||||
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() {
|
||||
// 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
|
||||
// The delay is to allow time for async layout to complete
|
||||
setTimeout(() => this.autoScroll(), 500);
|
||||
setTimeout(() => {
|
||||
this.autoScroll();
|
||||
this.isStarting = false;
|
||||
this.isFetching = false;
|
||||
}, 500);
|
||||
}
|
||||
|
||||
onDocVersionChange(versionIndex: number) {
|
||||
|
|
|
@ -5,8 +5,17 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
|||
|
||||
import { Location, LocationStrategy, PathLocationStrategy } from '@angular/common';
|
||||
|
||||
import { MdToolbarModule, MdButtonModule, MdIconModule, MdInputModule, MdSidenavModule, MdTabsModule, Platform,
|
||||
MdIconRegistry } from '@angular/material';
|
||||
import {
|
||||
MdButtonModule,
|
||||
MdIconModule,
|
||||
MdIconRegistry,
|
||||
MdInputModule,
|
||||
MdProgressBarModule,
|
||||
MdSidenavModule,
|
||||
MdTabsModule,
|
||||
MdToolbarModule,
|
||||
Platform
|
||||
} from '@angular/material';
|
||||
|
||||
// Temporary fix for MdSidenavModule issue:
|
||||
// crashes with "missing first" operator when SideNav.mode is "over"
|
||||
|
@ -67,9 +76,10 @@ export const svgIconProviders = [
|
|||
MdButtonModule,
|
||||
MdIconModule,
|
||||
MdInputModule,
|
||||
MdToolbarModule,
|
||||
MdProgressBarModule,
|
||||
MdSidenavModule,
|
||||
MdTabsModule,
|
||||
MdToolbarModule,
|
||||
SwUpdatesModule
|
||||
],
|
||||
declarations: [
|
||||
|
|
|
@ -1,3 +1,12 @@
|
|||
.progress-bar-container {
|
||||
height: 2px;
|
||||
overflow: hidden;
|
||||
position: fixed;
|
||||
top: 64px;
|
||||
width: 100vw;
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
.sidenav-content {
|
||||
padding: 1rem 3rem 3rem;
|
||||
margin: auto;
|
||||
|
@ -12,9 +21,15 @@
|
|||
aio-menu {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.progress-bar-container {
|
||||
top: 56px;
|
||||
}
|
||||
|
||||
.sidenav-content {
|
||||
min-height: 450px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.sidenav-container {
|
||||
|
|
Loading…
Reference in New Issue