diff --git a/src/app/pages/component-viewer/component-api.html b/src/app/pages/component-viewer/component-api.html index 841d16d96..3de0bac87 100644 --- a/src/app/pages/component-viewer/component-api.html +++ b/src/app/pages/component-viewer/component-api.html @@ -2,9 +2,9 @@ API for {{componentViewer.componentDocItem.id}} - diff --git a/src/app/pages/component-viewer/component-overview.html b/src/app/pages/component-viewer/component-overview.html index b40b81a66..e56dcb1b5 100644 --- a/src/app/pages/component-viewer/component-overview.html +++ b/src/app/pages/component-viewer/component-overview.html @@ -2,7 +2,7 @@ Overview for {{componentViewer.componentDocItem.id}} diff --git a/src/app/pages/component-viewer/component-viewer.spec.ts b/src/app/pages/component-viewer/component-viewer.spec.ts index aee52d75e..ccf280bc2 100644 --- a/src/app/pages/component-viewer/component-viewer.spec.ts +++ b/src/app/pages/component-viewer/component-viewer.spec.ts @@ -42,7 +42,7 @@ describe('ComponentViewer', () => { it('should set page title correctly', () => { const component = fixture.componentInstance; fixture.detectChanges(); - const expected = `${component.docItems.getItemById(docItemsId).name}`; + const expected = `${component.docItems.getItemById(docItemsId, 'material').name}`; expect(component._componentPageTitle.title).toEqual(expected); }); }); diff --git a/src/app/pages/component-viewer/component-viewer.ts b/src/app/pages/component-viewer/component-viewer.ts index 24be9285c..54f37b2f4 100644 --- a/src/app/pages/component-viewer/component-viewer.ts +++ b/src/app/pages/component-viewer/component-viewer.ts @@ -1,11 +1,13 @@ import {Component, OnInit, NgModule, ElementRef, ViewEncapsulation, ViewChild} from '@angular/core'; -import {ActivatedRoute, Router, RouterModule} from '@angular/router'; +import {ActivatedRoute, Params, Router, RouterModule} from '@angular/router'; import {DocumentationItems, DocItem} from '../../shared/documentation-items/documentation-items'; import {ComponentPageTitle} from '../page-title/page-title'; import {MatTabsModule} from '@angular/material'; import {DocViewerModule} from '../../shared/doc-viewer/doc-viewer-module'; import {CommonModule} from '@angular/common'; import {TableOfContentsModule} from '../../shared/table-of-contents/table-of-contents.module'; +import {Observable} from 'rxjs/Observable'; + @Component({ selector: 'app-component-viewer', @@ -22,19 +24,23 @@ export class ComponentViewer { private router: Router, public _componentPageTitle: ComponentPageTitle, public docItems: DocumentationItems) { - this._route.params.subscribe(params => { - this.componentDocItem = docItems.getItemById(params['id']); - - if (this.componentDocItem) { - this._componentPageTitle.title = `${this.componentDocItem.name}`; - this.componentDocItem.examples.length ? - this.sections.add('examples') : - this.sections.delete('examples'); + // Listen to changes on the current route for the doc id (e.g. button/checkbox) and the + // parent route for the section (material/cdk). + Observable.combineLatest(_route.params, _route.parent.params) + .map((p: [Params, Params]) => ({id: p[0]['id'], section: p[1]['section']})) + .map(p => docItems.getItemById(p.id, p.section)) + .subscribe(d => { + this.componentDocItem = d; + if (this.componentDocItem) { + this._componentPageTitle.title = `${this.componentDocItem.name}`; + this.componentDocItem.examples.length ? + this.sections.add('examples') : + this.sections.delete('examples'); - } else { - this.router.navigate(['/components']); - } - }); + } else { + this.router.navigate(['/components']); + } + }); } } diff --git a/src/app/shared/documentation-items/documentation-items.spec.ts b/src/app/shared/documentation-items/documentation-items.spec.ts index 9522ac7e1..ee3338fac 100644 --- a/src/app/shared/documentation-items/documentation-items.spec.ts +++ b/src/app/shared/documentation-items/documentation-items.spec.ts @@ -37,6 +37,6 @@ describe('DocViewer', () => { }); it('should get a doc item by id', () => { - expect(docsItems.getItemById('button')).toBeDefined(); + expect(docsItems.getItemById('button', 'material')).toBeDefined(); }); }); diff --git a/src/app/shared/documentation-items/documentation-items.ts b/src/app/shared/documentation-items/documentation-items.ts index 78b2a2c18..97d8fd331 100644 --- a/src/app/shared/documentation-items/documentation-items.ts +++ b/src/app/shared/documentation-items/documentation-items.ts @@ -3,6 +3,7 @@ import {Injectable} from '@angular/core'; export interface DocItem { id: string; name: string; + packageName?: string; examples?: string[]; } @@ -107,51 +108,47 @@ const DOCS: {[key: string]: DocCategory[]} = { } ], [CDK] : [ - { - id: 'components', - name: 'Components', - items: [ - {id: 'table', name: 'Table', examples: []}, - {id: 'stepper', name: 'Stepper', examples: []}, - - ] - }, { id: 'component-composition', - name: 'Component Composition', + name: 'Common Behaviors', items: [ + {id: 'a11y', name: 'Accessibility', examples: []}, {id: 'observers', name: 'Observers', examples: []}, {id: 'layout', name: 'Layout', examples: []}, {id: 'overlay', name: 'Overlay', examples: []}, {id: 'portal', name: 'Portal', examples: []}, {id: 'bidi', name: 'Bidirectionality', examples: []}, {id: 'scrolling', name: 'Scrolling', examples: []}, - {id: 'viewport', name: 'Viewport', examples: []}, - ] - }, - { - id: 'utilities', - name: 'Utilities', - items: [ - {id: 'coercion', name: 'Coercion', examples: []}, - {id: 'collections', name: 'Collections', examples: []}, - {id: 'keycodes', name: 'Keycodes', examples: []}, - {id: 'platform', name: 'Platform', examples: []}, ] }, { - id: 'accessibility', - name: 'Accessibility', + id: 'components', + name: 'Components', items: [ - {id: 'focus-key-manager', name: 'Focus Key Manager', examples: []}, - {id: 'focus-trap', name: 'Focus Trap', examples: []}, - {id: 'interactivity-checker', name: 'Interactivity Checker', examples: []}, - {id: 'list-key-manager', name: 'List Key Manager', examples: []}, - {id: 'live-announcer', name: 'Live Announcer', examples: []}, + {id: 'table', name: 'Table', examples: []}, + {id: 'stepper', name: 'Stepper', examples: []}, + ] }, + // TODO(jelbourn): re-add utilities and a11y as top-level categories once we can generate + // their API docs with dgeni. Currently our setup doesn't generate API docs for constants + // and standalone functions (much of the utilities) and we have no way of generating API + // docs more granularly than directory-level (within a11y) (same for viewport). ] }; + +for (let category of DOCS[COMPONENTS]) { + for (let doc of category.items) { + doc.packageName = 'material'; + } +} + +for (let category of DOCS[CDK]) { + for (let doc of category.items) { + doc.packageName = 'cdk'; + } +} + const ALL_COMPONENTS = DOCS[COMPONENTS].reduce( (result, category) => result.concat(category.items), []); const ALL_CDK = DOCS[CDK].reduce((result, cdk) => result.concat(cdk.items), []); @@ -174,8 +171,9 @@ export class DocumentationItems { return []; } - getItemById(id: string): DocItem { - return ALL_DOCS.find(i => i.id === id); + getItemById(id: string, section: string): DocItem { + const sectionLookup = section == 'cdk' ? 'cdk' : 'material'; + return ALL_DOCS.find(doc => doc.id === id && doc.packageName == sectionLookup); } getCategoryById(id: string): DocCategory { diff --git a/src/app/shared/table-of-contents/table-of-contents.ts b/src/app/shared/table-of-contents/table-of-contents.ts index b358154b9..9735aeed2 100644 --- a/src/app/shared/table-of-contents/table-of-contents.ts +++ b/src/app/shared/table-of-contents/table-of-contents.ts @@ -1,10 +1,11 @@ -import {Component, Input, Inject, ElementRef, OnInit} from '@angular/core'; +import {Component, ElementRef, Inject, Input, OnInit} from '@angular/core'; import {DOCUMENT} from '@angular/platform-browser'; -import {Router, ActivatedRoute, NavigationEnd} from '@angular/router'; -import {Observable} from 'rxjs/Observable'; -import {Subscription} from 'rxjs/Subscription'; +import {ActivatedRoute, NavigationEnd, Router} from '@angular/router'; import 'rxjs/add/observable/fromEvent'; import 'rxjs/add/operator/debounceTime'; +import 'rxjs/add/operator/takeUntil'; +import {Observable} from 'rxjs/Observable'; +import {Subject} from 'rxjs/Subject'; interface Link { /* id of the section*/ @@ -36,9 +37,7 @@ export class TableOfContents implements OnInit { _rootUrl: string; private _scrollContainer: any; - private _scrollSubscription: Subscription; - private _routeSubscription: Subscription; - private _fragmentSubscription: Subscription; + private _destroyed = new Subject(); private _urlFragment = ''; constructor(private _router: Router, @@ -46,7 +45,7 @@ export class TableOfContents implements OnInit { private _element: ElementRef, @Inject(DOCUMENT) private _document: Document) { - this._routeSubscription = this._router.events.subscribe((event) => { + this._router.events.takeUntil(this._destroyed).subscribe((event) => { if (event instanceof NavigationEnd) { const rootUrl = _router.url.split('#')[0]; if (rootUrl !== this._rootUrl) { @@ -56,7 +55,7 @@ export class TableOfContents implements OnInit { } }); - this._fragmentSubscription = this._route.fragment.subscribe(fragment => { + this._route.fragment.takeUntil(this._destroyed).subscribe(fragment => { this._urlFragment = fragment; const target = document.getElementById(this._urlFragment); @@ -73,17 +72,15 @@ export class TableOfContents implements OnInit { this._scrollContainer = this.container ? this._document.querySelectorAll(this.container)[0] : window; - this._scrollSubscription = Observable - .fromEvent(this._scrollContainer, 'scroll') + Observable.fromEvent(this._scrollContainer, 'scroll') + .takeUntil(this._destroyed) .debounceTime(10) .subscribe(() => this.onScroll()); }); } ngOnDestroy(): void { - this._routeSubscription.unsubscribe(); - this._scrollSubscription.unsubscribe(); - this._fragmentSubscription.unsubscribe(); + this._destroyed.next(); } updateScrollPosition(): void {