- - - + + +
diff --git a/aio/src/app/app.component.spec.ts b/aio/src/app/app.component.spec.ts index ce1c4d75c3..a872868931 100644 --- a/aio/src/app/app.component.spec.ts +++ b/aio/src/app/app.component.spec.ts @@ -25,11 +25,9 @@ import { first, mapTo } from 'rxjs/operators'; import { MockLocationService } from 'testing/location.service'; import { MockLogger } from 'testing/logger.service'; import { MockSearchService } from 'testing/search.service'; -import { AppComponent } from './app.component'; +import { AppComponent, dockSideNavWidth, showFloatingTocWidth, showTopMenuWidth } from './app.component'; import { AppModule } from './app.module'; -const sideBySideBreakPoint = 992; -const hideToCBreakPoint = 800; const startedDelay = 100; describe('AppComponent', () => { @@ -58,7 +56,7 @@ describe('AppComponent', () => { component = fixture.componentInstance; fixture.detectChanges(); - component.onResize(sideBySideBreakPoint + 1); // wide by default + component.onResize(showTopMenuWidth + 1); // wide by default const de = fixture.debugElement; const docViewerDe = de.query(By.css('aio-doc-viewer')); @@ -99,7 +97,7 @@ describe('AppComponent', () => { }); it('should be false on narrow screens', () => { - component.onResize(hideToCBreakPoint - 1); + component.onResize(showFloatingTocWidth - 1); tocService.tocList.next([{}, {}, {}] as TocItem[]); expect(component.hasFloatingToc).toBe(false); @@ -112,7 +110,7 @@ describe('AppComponent', () => { }); it('should be true on wide screens unless the toc is empty', () => { - component.onResize(hideToCBreakPoint + 1); + component.onResize(showFloatingTocWidth + 1); tocService.tocList.next([{}, {}, {}] as TocItem[]); expect(component.hasFloatingToc).toBe(true); @@ -127,37 +125,47 @@ describe('AppComponent', () => { it('should be false when toc is empty', () => { tocService.tocList.next([]); - component.onResize(hideToCBreakPoint + 1); + component.onResize(showFloatingTocWidth + 1); expect(component.hasFloatingToc).toBe(false); - component.onResize(hideToCBreakPoint - 1); + component.onResize(showFloatingTocWidth - 1); expect(component.hasFloatingToc).toBe(false); - component.onResize(hideToCBreakPoint + 1); + component.onResize(showFloatingTocWidth + 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); + component.onResize(showFloatingTocWidth + 1); expect(component.hasFloatingToc).toBe(true); - component.onResize(hideToCBreakPoint - 1); + component.onResize(showFloatingTocWidth - 1); expect(component.hasFloatingToc).toBe(false); - component.onResize(hideToCBreakPoint + 1); + component.onResize(showFloatingTocWidth + 1); expect(component.hasFloatingToc).toBe(true); }); }); - describe('isSideBySide', () => { + describe('showTopMenu', () => { it('should be updated on resize', () => { - component.onResize(sideBySideBreakPoint - 1); - expect(component.isSideBySide).toBe(false); + component.onResize(showTopMenuWidth - 1); + expect(component.showTopMenu).toBe(false); - component.onResize(sideBySideBreakPoint + 1); - expect(component.isSideBySide).toBe(true); + component.onResize(showTopMenuWidth + 1); + expect(component.showTopMenu).toBe(true); + }); + }); + + describe('dockSideNav', () => { + it('should be updated on resize', () => { + component.onResize(dockSideNavWidth - 1); + expect(component.dockSideNav).toBe(false); + + component.onResize(dockSideNavWidth + 1); + expect(component.dockSideNav).toBe(true); }); }); @@ -185,8 +193,8 @@ describe('AppComponent', () => { fixture.detectChanges(); }; - describe('when side-by-side (wide)', () => { - beforeEach(() => resizeTo(sideBySideBreakPoint + 1)); // side-by-side + describe('when view is wide', () => { + beforeEach(() => resizeTo(dockSideNavWidth + 1)); // wide view it('should open when navigating to a guide page (guide/pipes)', () => { navigateTo('guide/pipes'); @@ -232,8 +240,8 @@ describe('AppComponent', () => { }); }); - describe('when NOT side-by-side (narrow)', () => { - beforeEach(() => resizeTo(sideBySideBreakPoint - 1)); // NOT side-by-side + describe('when view is narrow', () => { + beforeEach(() => resizeTo(dockSideNavWidth - 1)); // narrow view it('should be closed when navigating to a guide page (guide/pipes)', () => { navigateTo('guide/pipes'); @@ -286,30 +294,30 @@ describe('AppComponent', () => { }); }); - describe('when changing side-by-side (narrow --> wide)', () => { + describe('when changing from narrow to wide view', () => { const sidenavDocs = ['api/a/b/c/d', 'guide/pipes']; const nonSidenavDocs = ['features', 'about']; sidenavDocs.forEach(doc => { it(`should open when on a sidenav doc (${doc})`, () => { - resizeTo(sideBySideBreakPoint - 1); + resizeTo(dockSideNavWidth - 1); navigateTo(doc); expect(sidenav.opened).toBe(false); - resizeTo(sideBySideBreakPoint + 1); + resizeTo(dockSideNavWidth + 1); expect(sidenav.opened).toBe(true); }); }); nonSidenavDocs.forEach(doc => { it(`should remain closed when on a non-sidenav doc (${doc})`, () => { - resizeTo(sideBySideBreakPoint - 1); + resizeTo(dockSideNavWidth - 1); navigateTo(doc); expect(sidenav.opened).toBe(false); - resizeTo(sideBySideBreakPoint + 1); + resizeTo(dockSideNavWidth + 1); expect(sidenav.opened).toBe(false); }); }); @@ -317,33 +325,33 @@ describe('AppComponent', () => { describe('when manually opened', () => { sidenavDocs.forEach(doc => { it(`should remain opened when on a sidenav doc (${doc})`, () => { - resizeTo(sideBySideBreakPoint - 1); + resizeTo(dockSideNavWidth - 1); navigateTo(doc); toggleSidenav(); expect(sidenav.opened).toBe(true); - resizeTo(sideBySideBreakPoint + 1); + resizeTo(dockSideNavWidth + 1); expect(sidenav.opened).toBe(true); }); }); nonSidenavDocs.forEach(doc => { it(`should close when on a non-sidenav doc (${doc})`, () => { - resizeTo(sideBySideBreakPoint - 1); + resizeTo(dockSideNavWidth - 1); navigateTo(doc); toggleSidenav(); expect(sidenav.opened).toBe(true); - resizeTo(sideBySideBreakPoint + 1); + resizeTo(showTopMenuWidth + 1); expect(sidenav.opened).toBe(false); }); }); }); }); - describe('when changing side-by-side (wide --> narrow)', () => { + describe('when changing from wide to narrow view', () => { const sidenavDocs = ['api/a/b/c/d', 'guide/pipes']; const nonSidenavDocs = ['features', 'about']; @@ -352,7 +360,7 @@ describe('AppComponent', () => { navigateTo(doc); expect(sidenav.opened).toBe(true); - resizeTo(sideBySideBreakPoint - 1); + resizeTo(dockSideNavWidth - 1); expect(sidenav.opened).toBe(false); }); }); @@ -362,7 +370,7 @@ describe('AppComponent', () => { navigateTo(doc); expect(sidenav.opened).toBe(false); - resizeTo(sideBySideBreakPoint - 1); + resizeTo(dockSideNavWidth - 1); expect(sidenav.opened).toBe(false); }); }); @@ -376,7 +384,7 @@ describe('AppComponent', () => { async function setupSelectorForTesting(mode?: string) { createTestingModule('a/b', mode); await initializeTest(); - component.onResize(sideBySideBreakPoint + 1); // side-by-side + component.onResize(dockSideNavWidth + 1); // wide view selectElement = fixture.debugElement.query(By.directive(SelectComponent)); selectComponent = selectElement.componentInstance; } diff --git a/aio/src/app/app.component.ts b/aio/src/app/app.component.ts index bedd804613..822f4b2e5e 100644 --- a/aio/src/app/app.component.ts +++ b/aio/src/app/app.component.ts @@ -14,6 +14,9 @@ import { BehaviorSubject, combineLatest, Observable } from 'rxjs'; import { first, map } from 'rxjs/operators'; const sideNavView = 'SideNav'; +export const showTopMenuWidth = 992; +export const dockSideNavWidth = 992; +export const showFloatingTocWidth = 800; @Component({ selector: 'aio-shell', @@ -57,18 +60,17 @@ export class AppComponent implements OnInit { isStarting = true; isTransitioning = true; isFetching = false; - isSideBySide = false; + showTopMenu = false; + dockSideNav = false; private isFetchingTimeout: any; private isSideNavDoc = false; - private sideBySideWidth = 992; sideNavNodes: NavigationNode[]; topMenuNodes: NavigationNode[]; topMenuNarrowNodes: NavigationNode[]; hasFloatingToc = false; private showFloatingToc = new BehaviorSubject(false); - private showFloatingTocWidth = 800; tocMaxHeight: string; private tocMaxHeightOffset = 0; @@ -76,8 +78,8 @@ export class AppComponent implements OnInit { private currentUrl: string; - get isOpened() { return this.isSideBySide && this.isSideNavDoc; } - get mode() { return this.isSideBySide ? 'side' : 'over'; } + get isOpened() { return this.dockSideNav && this.isSideNavDoc; } + get mode() { return this.dockSideNav && (this.isSideNavDoc || this.showTopMenu) ? 'side' : 'over'; } // Search related properties showSearchResults = false; @@ -239,13 +241,14 @@ export class AppComponent implements OnInit { @HostListener('window:resize', ['$event.target.innerWidth']) onResize(width: number) { - this.isSideBySide = width >= this.sideBySideWidth; - this.showFloatingToc.next(width > this.showFloatingTocWidth); + this.showTopMenu = width >= showTopMenuWidth; + this.dockSideNav = width >= dockSideNavWidth; + this.showFloatingToc.next(width > showFloatingTocWidth); - if (this.isSideBySide && !this.isSideNavDoc) { + if (this.showTopMenu && !this.isSideNavDoc) { // If this is a non-sidenav doc and the screen is wide enough so that we can display menu // items in the top-bar, ensure the sidenav is closed. - // (This condition can only be met when the resize event changes the value of `isSideBySide` + // (This condition can only be met when the resize event changes the value of `showTopMenu` // from `false` to `true` while on a non-sidenav doc.) this.sidenav.toggle(false); } @@ -338,7 +341,7 @@ export class AppComponent implements OnInit { } // May be open or closed when wide; always closed when narrow. - this.sidenav.toggle(this.isSideBySide && openSideNav); + this.sidenav.toggle(this.dockSideNav && openSideNav); } // Dynamically change height of table of contents container diff --git a/aio/src/styles/1-layouts/_top-menu.scss b/aio/src/styles/1-layouts/_top-menu.scss index 5894ca066c..da915c8b62 100644 --- a/aio/src/styles/1-layouts/_top-menu.scss +++ b/aio/src/styles/1-layouts/_top-menu.scss @@ -1,6 +1,6 @@ // VARIABLES -$hamburgerShownMaxWidth: 991px; -$hamburgerHiddenMinWidth: $hamburgerShownMaxWidth + 1; +$showTopMenuWidth: 992px; +$hideTopMenuWidth: $showTopMenuWidth - 1; $hamburgerShownMargin: 0 8px 0 0; $hamburgerHiddenMargin: 0 16px 0 -64px; @@ -54,7 +54,7 @@ aio-shell.folder-docs mat-toolbar.mat-toolbar, aio-shell.folder-guide mat-toolbar.mat-toolbar, aio-shell.folder-start mat-toolbar.mat-toolbar, aio-shell.folder-tutorial mat-toolbar.mat-toolbar { - @media (min-width: $hamburgerHiddenMinWidth) { + @media (min-width: $showTopMenuWidth) { .hamburger.mat-button { // Hamburger shown on non-marketing pages even on large screens. margin: $hamburgerShownMargin; @@ -68,7 +68,7 @@ aio-shell.folder-tutorial mat-toolbar.mat-toolbar { margin: $hamburgerShownMargin; padding: 0; - @media (min-width: $hamburgerHiddenMinWidth) { + @media (min-width: $showTopMenuWidth) { // Hamburger hidden by default on large screens. // (Will be shown per doc.) margin: $hamburgerHiddenMargin; @@ -106,7 +106,7 @@ aio-shell.folder-tutorial mat-toolbar.mat-toolbar { outline-offset: 4px; } - @media screen and (max-width: $hamburgerShownMaxWidth) { + @media screen and (max-width: $hideTopMenuWidth) { padding: 4px 0; } @@ -120,7 +120,7 @@ aio-shell.folder-tutorial mat-toolbar.mat-toolbar { top: 12px; height: 40px; - @media (max-width: $hamburgerShownMaxWidth) { + @media (max-width: $hideTopMenuWidth) { &:hover { transform: scale(1.1); }