From e894f5c39951573e695b74456cc4d980bc49db02 Mon Sep 17 00:00:00 2001 From: Georgios Kalpakas Date: Wed, 7 Jun 2017 16:51:21 +0300 Subject: [PATCH] fix(aio): expand the main content width when there is no ToC Previously, the main content would always leave a 18% margin on the right to be occupied by the ToC (even if there was no ToC). This commit lets the main content expand to the right to occupy all the available space when there is no ToC. Fixes #17205 Fixes #17270 --- aio/src/app/app.component.html | 4 +- aio/src/app/app.component.spec.ts | 115 ++++++++++++++++++++----- aio/src/app/app.component.ts | 18 ++-- aio/src/styles/1-layouts/_sidenav.scss | 6 +- 4 files changed, 113 insertions(+), 30 deletions(-) diff --git a/aio/src/app/app.component.html b/aio/src/app/app.component.html index 58ebe94341..9958318de0 100644 --- a/aio/src/app/app.component.html +++ b/aio/src/app/app.component.html @@ -15,7 +15,7 @@ - + @@ -33,7 +33,7 @@ -
+
diff --git a/aio/src/app/app.component.spec.ts b/aio/src/app/app.component.spec.ts index 6f9f995a75..531d9851a0 100644 --- a/aio/src/app/app.component.spec.ts +++ b/aio/src/app/app.component.spec.ts @@ -3,7 +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 { MdProgressBar, MdSidenav } from '@angular/material'; import { By } from '@angular/platform-browser'; import { BehaviorSubject } from 'rxjs/BehaviorSubject'; @@ -27,7 +27,7 @@ import { SearchService } from 'app/search/search.service'; import { SelectComponent, Option } from 'app/shared/select/select.component'; import { SwUpdateNotificationsService } from 'app/sw-updates/sw-update-notifications.service'; import { TocComponent } from 'app/embedded/toc/toc.component'; -import { MdSidenav } from '@angular/material'; +import { TocItem, TocService } from 'app/shared/toc.service'; const sideBySideBreakPoint = 992; const hideToCBreakPoint = 800; @@ -40,6 +40,7 @@ describe('AppComponent', () => { let hamburger: HTMLButtonElement; let locationService: MockLocationService; let sidenav: HTMLElement; + let tocService: TocService; const initializeTest = () => { fixture = TestBed.createComponent(AppComponent); @@ -48,10 +49,12 @@ describe('AppComponent', () => { fixture.detectChanges(); component.onResize(sideBySideBreakPoint + 1); // 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; + const de = fixture.debugElement; + docViewer = de.query(By.css('aio-doc-viewer')).nativeElement; + hamburger = de.query(By.css('.hamburger')).nativeElement; + locationService = de.injector.get(LocationService) as any as MockLocationService; + sidenav = de.query(By.css('md-sidenav')).nativeElement; + tocService = de.injector.get(TocService); }; describe('with proper DocViewer', () => { @@ -72,19 +75,74 @@ describe('AppComponent', () => { }); }); - describe('onResize', () => { - it('should update `isSideBySide` accordingly', () => { - component.onResize(sideBySideBreakPoint + 1); - expect(component.isSideBySide).toBe(true); - component.onResize(sideBySideBreakPoint - 1); - expect(component.isSideBySide).toBe(false); + describe('hasFloatingToc', () => { + it('should initially be true', () => { + const fixture2 = TestBed.createComponent(AppComponent); + const component2 = fixture2.componentInstance; + + expect(component2.hasFloatingToc).toBe(true); }); - it('should update `showFloatingToc` accordingly', () => { - component.onResize(hideToCBreakPoint + 1); - expect(component.showFloatingToc).toBe(true); + it('should be false on narrow screens', () => { component.onResize(hideToCBreakPoint - 1); - expect(component.showFloatingToc).toBe(false); + + tocService.tocList.next([{}, {}, {}] as TocItem[]); + expect(component.hasFloatingToc).toBe(false); + + tocService.tocList.next([]); + expect(component.hasFloatingToc).toBe(false); + + tocService.tocList.next([{}, {}, {}] as TocItem[]); + expect(component.hasFloatingToc).toBe(false); + }); + + it('should be true on wide screens unless the toc is empty', () => { + component.onResize(hideToCBreakPoint + 1); + + tocService.tocList.next([{}, {}, {}] as TocItem[]); + expect(component.hasFloatingToc).toBe(true); + + tocService.tocList.next([]); + expect(component.hasFloatingToc).toBe(false); + + tocService.tocList.next([{}, {}, {}] as TocItem[]); + expect(component.hasFloatingToc).toBe(true); + }); + + it('should be false when toc is empty', () => { + tocService.tocList.next([]); + + component.onResize(hideToCBreakPoint + 1); + expect(component.hasFloatingToc).toBe(false); + + component.onResize(hideToCBreakPoint - 1); + expect(component.hasFloatingToc).toBe(false); + + component.onResize(hideToCBreakPoint + 1); + expect(component.hasFloatingToc).toBe(false); + }); + + it('should be true when toc is not empty unless the screen is narrow', () => { + tocService.tocList.next([{}, {}, {}] as TocItem[]); + + component.onResize(hideToCBreakPoint + 1); + expect(component.hasFloatingToc).toBe(true); + + component.onResize(hideToCBreakPoint - 1); + expect(component.hasFloatingToc).toBe(false); + + component.onResize(hideToCBreakPoint + 1); + expect(component.hasFloatingToc).toBe(true); + }); + }); + + describe('isSideBySide', () => { + it('should be updated on resize', () => { + component.onResize(sideBySideBreakPoint - 1); + expect(component.isSideBySide).toBe(false); + + component.onResize(sideBySideBreakPoint + 1); + expect(component.isSideBySide).toBe(true); }); }); @@ -523,14 +581,31 @@ describe('AppComponent', () => { let tocDebugElement: DebugElement; let tocContainer: DebugElement; - beforeEach(() => { + const setHasFloatingToc = hasFloatingToc => { + component.hasFloatingToc = hasFloatingToc; + fixture.detectChanges(); + tocDebugElement = fixture.debugElement.query(By.directive(TocComponent)); - tocContainer = tocDebugElement.parent; + tocContainer = tocDebugElement && tocDebugElement.parent; + }; + + beforeEach(() => setHasFloatingToc(true)); + + + it('should show/hide `` based on `hasFloatingToc`', () => { + expect(tocDebugElement).toBeTruthy(); + expect(tocContainer).toBeTruthy(); + + setHasFloatingToc(false); + expect(tocDebugElement).toBeFalsy(); + expect(tocContainer).toBeFalsy(); + + setHasFloatingToc(true); + expect(tocDebugElement).toBeTruthy(); + expect(tocContainer).toBeTruthy(); }); - it('should have a non-embedded `` element', () => { - expect(tocDebugElement).toBeDefined(); expect(tocDebugElement.classes['embedded']).toBeFalsy(); }); diff --git a/aio/src/app/app.component.ts b/aio/src/app/app.component.ts index 7f20212c82..1f44e91a8a 100644 --- a/aio/src/app/app.component.ts +++ b/aio/src/app/app.component.ts @@ -12,7 +12,9 @@ import { SearchResultsComponent } from 'app/search/search-results/search-results import { SearchBoxComponent } from 'app/search/search-box/search-box.component'; import { SearchService } from 'app/search/search.service'; import { SwUpdateNotificationsService } from 'app/sw-updates/sw-update-notifications.service'; +import { TocService } from 'app/shared/toc.service'; +import { BehaviorSubject } from 'rxjs/BehaviorSubject'; import { combineLatest } from 'rxjs/observable/combineLatest'; const sideNavView = 'SideNav'; @@ -65,8 +67,9 @@ export class AppComponent implements OnInit { topMenuNodes: NavigationNode[]; topMenuNarrowNodes: NavigationNode[]; - showFloatingToc = false; - showFloatingTocWidth = 800; + hasFloatingToc = true; + private showFloatingToc = new BehaviorSubject(false); + private showFloatingTocWidth = 800; tocMaxHeight: string; private tocMaxHeightOffset = 0; @@ -103,8 +106,9 @@ export class AppComponent implements OnInit { private navigationService: NavigationService, private scrollService: ScrollService, private searchService: SearchService, - private swUpdateNotifications: SwUpdateNotificationsService - ) { } + private swUpdateNotifications: SwUpdateNotificationsService, + private tocService: TocService + ) { } ngOnInit() { // Do not initialize the search on browsers that lack web worker support @@ -174,6 +178,10 @@ export class AppComponent implements OnInit { this.navigationService.versionInfo.subscribe( vi => this.versionInfo = vi ); this.swUpdateNotifications.enable(); + + const hasNonEmptyToc = this.tocService.tocList.map(tocList => tocList.length > 0); + combineLatest(hasNonEmptyToc, this.showFloatingToc) + .subscribe(([hasToc, showFloatingToc]) => this.hasFloatingToc = hasToc && showFloatingToc); } // Scroll to the anchor in the hash fragment or top of doc. @@ -207,7 +215,7 @@ export class AppComponent implements OnInit { @HostListener('window:resize', ['$event.target.innerWidth']) onResize(width) { this.isSideBySide = width > this.sideBySideWidth; - this.showFloatingToc = width > this.showFloatingTocWidth; + this.showFloatingToc.next(width > this.showFloatingTocWidth); } @HostListener('click', ['$event.target', '$event.button', '$event.ctrlKey', '$event.metaKey', '$event.altKey']) diff --git a/aio/src/styles/1-layouts/_sidenav.scss b/aio/src/styles/1-layouts/_sidenav.scss index 2d97514fcb..fad21e9ac0 100644 --- a/aio/src/styles/1-layouts/_sidenav.scss +++ b/aio/src/styles/1-layouts/_sidenav.scss @@ -42,12 +42,12 @@ md-sidenav.mat-sidenav.sidenav { md-sidenav-container.sidenav-container { min-height: 100%; height: auto !important; - max-width: 82%; + max-width: 100%; margin: 0; transform: none; - @media (max-width: 800px) { - max-width: 100%; + &.has-floating-toc { + max-width: 82%; } }