From 1cfcabb4fdcf72f0e559996d185eb7e3f77df90d Mon Sep 17 00:00:00 2001 From: Ward Bell Date: Thu, 4 May 2017 20:06:53 -0700 Subject: [PATCH] feat(aio): each doc can add/remove class to aio-shell with MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit closes #16549 Supports doc-specific styling of the entire shell based on the context of the current document. Also adds the “marketing” class if the docId indicates the doc is neither a guide nor tutorial page. --- aio/src/app/app.component.spec.ts | 67 +++++++++++++++++++++++-------- aio/src/app/app.component.ts | 21 ++++++++-- 2 files changed, 67 insertions(+), 21 deletions(-) diff --git a/aio/src/app/app.component.spec.ts b/aio/src/app/app.component.spec.ts index 9aa28f33ae561..9d1d25acb9aa3 100644 --- a/aio/src/app/app.component.spec.ts +++ b/aio/src/app/app.component.spec.ts @@ -31,6 +31,7 @@ describe('AppComponent', () => { let docViewer: HTMLElement; let hamburger: HTMLButtonElement; + let http: TestHttp; let locationService: MockLocationService; let sidenav: HTMLElement; @@ -256,6 +257,10 @@ describe('AppComponent', () => { describe('currentDocument', () => { + beforeEach(() => { + http = TestBed.get(Http); + }); + it('should display a guide page (guide/pipes)', () => { locationService.go('guide/pipes'); fixture.detectChanges(); @@ -274,6 +279,22 @@ describe('AppComponent', () => { expect(docViewer.innerText).toMatch(/Features/i); }); + it('should update the document title', () => { + const titleService = TestBed.get(Title); + spyOn(titleService, 'setTitle'); + locationService.go('guide/pipes'); + fixture.detectChanges(); + expect(titleService.setTitle).toHaveBeenCalledWith('Angular - Pipes'); + }); + + it('should update the document title, with a default value if the document has no title', () => { + const titleService = TestBed.get(Title); + spyOn(titleService, 'setTitle'); + locationService.go('no-title'); + fixture.detectChanges(); + expect(titleService.setTitle).toHaveBeenCalledWith('Angular'); + }); + const marketingClassName = 'marketing'; it('should not have marketing CSS class on host element for a guide page (guide/pipes)', () => { @@ -290,21 +311,28 @@ describe('AppComponent', () => { expect(classes).toContain(marketingClassName); }); - it('should update the document title', () => { - const titleService = TestBed.get(Title); - spyOn(titleService, 'setTitle'); - locationService.go('guide/pipes'); - fixture.detectChanges(); - expect(titleService.setTitle).toHaveBeenCalledWith('Angular - Pipes'); - }); + describe('when has ', () => { - it('should update the document title, with a default value if the document has no title', () => { - const titleService = TestBed.get(Title); - spyOn(titleService, 'setTitle'); - locationService.go('no-title'); - fixture.detectChanges(); - expect(titleService.setTitle).toHaveBeenCalledWith('Angular'); + it('should have marketing context\'s CSS class on host element', () => { + http.contents = ''; + locationService.go('features'); + fixture.detectChanges(); + const classes: string[] = fixture.nativeElement.className; + // Has "marketing" class as well as "foo bar" + expect(classes).toBe(marketingClassName + ' foo bar'); + }); + + it('should have tutorial context\'s CSS on host element when is late in the doc', () => { + const fluff = 'abc'.repeat(100); + http.contents = fluff + 'ignored stuff' + fluff; + locationService.go('tutorial/foo'); + fixture.detectChanges(); + const classes: string[] = fixture.nativeElement.className; + // No "marketing" class this time because is tutorial page + expect(classes).toBe('foo bar'); + }); }); + }); describe('autoScrolling with AutoScrollService', () => { @@ -484,6 +512,8 @@ class TestHttp { { title: 'v2', url: 'https://v2.angular.io' } ]; + contents: string; + // tslint:disable:quotemark navJson = { "TopBar": [ @@ -544,12 +574,15 @@ class TestHttp { if (/navigation\.json/.test(url)) { data = this.navJson; } else { + let contents = this.contents; const match = /generated\/docs\/(.+)\.json/.exec(url); const id = match[1]; - // Make up a title for test purposes - const title = id.split('/').pop().replace(/^([a-z])/, (_, letter) => letter.toUpperCase()); - const h1 = (id === 'no-title') ? '' : `

${title}

`; - const contents = `${h1}

Some heading

`; + if (contents === undefined) { + // Make up a title for test purposes + const title = id.split('/').pop().replace(/^([a-z])/, (_, letter) => letter.toUpperCase()); + const h1 = (id === 'no-title') ? '' : `

${title}

`; + contents = `${h1}

Some heading

`; + } data = { id, contents }; } return of({ json: () => data }); diff --git a/aio/src/app/app.component.ts b/aio/src/app/app.component.ts index 1c70960e8dcbf..62d4f4f76af1f 100644 --- a/aio/src/app/app.component.ts +++ b/aio/src/app/app.component.ts @@ -26,9 +26,6 @@ export class AppComponent implements OnInit { currentDocument: DocumentContents; footerNodes: NavigationNode[]; - @HostBinding('class.marketing') - isMarketing = false; - isStarting = true; isSideBySide = false; private isSideNavDoc = false; @@ -60,6 +57,9 @@ export class AppComponent implements OnInit { @ViewChild(SearchResultsComponent) searchResults: SearchResultsComponent; + @HostBinding('class') + shellClasses = ''; + @ViewChild(MdSidenav) sidenav: MdSidenav; @@ -79,6 +79,7 @@ export class AppComponent implements OnInit { this.documentService.currentDocument.subscribe(doc => { this.currentDocument = doc; this.setPageId(doc.id); + this.shellClasses = this.getShellClasses(doc); }); this.locationService.currentPath.subscribe(path => { @@ -98,7 +99,6 @@ export class AppComponent implements OnInit { if (this.previousNavView === currentNode.view) { return; } this.previousNavView = currentNode.view; this.isSideNavDoc = currentNode.view === sideNavView; - this.isMarketing = !this.isSideNavDoc; this.sideNavToggle(this.isSideNavDoc && this.isSideBySide); }); @@ -174,4 +174,17 @@ export class AppComponent implements OnInit { // Special case the home page this.pageId = (id === 'index') ? 'home' : id.replace('/', '-'); } + + private getShellClasses(doc: DocumentContents) { + let shellClasses = /^(guide|tutorial)\//.test(doc.id) ? '' : 'marketing '; + + const contents = doc.contents || ''; + // extract the classes from `` + const contextMatch = contents.match(/