Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(aio): add sidenav driven by navigation data #14429

Merged
merged 1 commit into from Feb 15, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion aio/e2e/app.po.ts
Expand Up @@ -3,7 +3,7 @@ import { browser, element, by } from 'protractor';
export class SitePage {

links = element.all(by.css('md-toolbar a'));
datePipeLink = element(by.css('md-toolbar a[aioNavLink="docs/api/common/DatePipe"]'));
datePipeLink = element(by.css('md-toolbar a[aioNavLink="api/common/date-pipe"]'));
docViewer = element(by.css('aio-doc-viewer'));
codeExample = element.all(by.css('aio-doc-viewer pre > code'));
featureLink = element(by.css('md-toolbar a[aioNavLink="features"]'));
Expand Down
16 changes: 8 additions & 8 deletions aio/src/app/app.component.html
@@ -1,11 +1,11 @@
<md-toolbar color="primary" class="app-toolbar">
<span>Angular</span>
<span><a class="nav-link" aioNavLink="home"> Home </a></span>
<span><a class="nav-link" aioNavLink="news"> News</a></span>
<span><a class="nav-link" aioNavLink="features"> Features</a></span>
<span><a class="nav-link" aioNavLink="docs/api/common/DatePipe"> DatePipe</a></span>
<button *ngIf="isHamburgerVisible" class="hamburger" md-button (click)="toggleSideNav()"><md-icon>menu</md-icon></button>
<aio-menu></aio-menu>
<md-input-container >
<input #search md-input placeholder="Search">
</md-input-container>
<span class="fill-remaining-space"></span>
</md-toolbar>
<section class="app-content">
<aio-doc-viewer [doc]="navEngine.currentDoc"></aio-doc-viewer>
</section>

<aio-sidenav></aio-sidenav>

22 changes: 18 additions & 4 deletions aio/src/app/app.component.scss
@@ -1,8 +1,22 @@
.fill-remaining-space {
flex: 1 1 auto;
}
.nav-link {
margin-right: 10px;
margin-left: 20px;
cursor: pointer;

md-input-container {
margin-left: 10px;
input {
min-width:200px;
}
}


.md-input-element {
font-size: 70%;
font-style: italic;
}

@media (max-width: 600px) {
aio-menu {
display: none;
}
}
10 changes: 7 additions & 3 deletions aio/src/app/app.component.ts
@@ -1,12 +1,16 @@
import { Component } from '@angular/core';
import { Component, ViewChild } from '@angular/core';

import { NavEngine } from './nav-engine/nav-engine.service';
import { SidenavComponent } from './sidenav/sidenav.component';

@Component({
selector: 'aio-shell',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
constructor(public navEngine: NavEngine) {}
isHamburgerVisible = true; // always ... for now

@ViewChild(SidenavComponent) sidenav: SidenavComponent;

toggleSideNav() { this.sidenav.toggle(); }
}
23 changes: 21 additions & 2 deletions aio/src/app/app.module.ts
@@ -1,32 +1,51 @@
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpModule } from '@angular/http';

import { MdToolbarModule } from '@angular/material/toolbar';
import { MdButtonModule} from '@angular/material/button';
import { MdIconModule} from '@angular/material/icon';
import { MdInputModule } from '@angular/material/input';
import { MdSidenavModule } from '@angular/material/sidenav';
import { Platform } from '@angular/material/core';

// Temporary fix for MdSidenavModule issue:
// crashes with "missing first" operator when SideNav.mode is "over"
import 'rxjs/add/operator/first';

import { AppComponent } from './app.component';
import { DocViewerComponent } from './doc-viewer/doc-viewer.component';
import { embeddedComponents, EmbeddedComponents } from './embedded';
import { Logger } from './logger.service';
import { navDirectives, navProviders } from './nav-engine';
import { SidenavComponent } from './sidenav/sidenav.component';
import { NavItemComponent } from './sidenav/nav-item.component';
import { MenuComponent } from './sidenav/menu.component';

@NgModule({
imports: [
BrowserModule,
HttpModule,
MdButtonModule.forRoot(),
MdIconModule.forRoot(),
MdInputModule.forRoot(),
MdToolbarModule.forRoot(),
MdButtonModule.forRoot()
MdSidenavModule.forRoot()
],
declarations: [
AppComponent,
embeddedComponents,
DocViewerComponent,
MenuComponent,
navDirectives,
NavItemComponent,
SidenavComponent,
],
providers: [
EmbeddedComponents,
Logger,
navProviders
navProviders,
Platform
],
entryComponents: [ embeddedComponents ],
bootstrap: [AppComponent]
Expand Down
30 changes: 14 additions & 16 deletions aio/src/app/doc-viewer/doc-viewer.component.spec.ts
Expand Up @@ -18,9 +18,7 @@ import { embeddedComponents, EmbeddedComponents } from '../embedded';
selector: 'aio-foo',
template: `Foo Component`
})
class FooComponent {

}
class FooComponent { }

///// BarComponent /////

Expand Down Expand Up @@ -101,7 +99,7 @@ class TestComponent {
//////// Tests //////////////

describe('DocViewerComponent', () => {
const mockDocMetadata: DocMetadata = { id: 'mock', title: 'Mock Doc', url: '' };
const fakeDocMetadata: DocMetadata = { docId: 'fake', title: 'fake Doc' };
let component: TestComponent;
let docViewerDE: DebugElement;
let docViewerEl: HTMLElement;
Expand Down Expand Up @@ -135,21 +133,21 @@ describe('DocViewerComponent', () => {
});

it(('should display nothing when set DocViewer.doc to doc w/o content'), () => {
component.docViewer.doc = { metadata: mockDocMetadata, content: '' };
component.docViewer.doc = { metadata: fakeDocMetadata, content: '' };
expect(docViewerEl.innerHTML).toBe('');
});

it(('should display simple static content doc'), () => {
const content = '<p>Howdy, doc viewer</p>';
component.docViewer.doc = { metadata: mockDocMetadata, content };
component.docViewer.doc = { metadata: fakeDocMetadata, content };
expect(docViewerEl.innerHTML).toEqual(content);
});

it(('should display nothing after reset static content doc'), () => {
const content = '<p>Howdy, doc viewer</p>';
component.docViewer.doc = { metadata: mockDocMetadata, content };
component.docViewer.doc = { metadata: fakeDocMetadata, content };
fixture.detectChanges();
component.docViewer.doc = { metadata: mockDocMetadata, content: '' };
component.docViewer.doc = { metadata: fakeDocMetadata, content: '' };
expect(docViewerEl.innerHTML).toEqual('');
});

Expand All @@ -159,7 +157,7 @@ describe('DocViewerComponent', () => {
<p><aio-foo></aio-foo></p>
<p>Below Foo</p>
`;
component.docViewer.doc = { metadata: mockDocMetadata, content };
component.docViewer.doc = { metadata: fakeDocMetadata, content };
const fooHtml = docViewerEl.querySelector('aio-foo').innerHTML;
expect(fooHtml).toContain('Foo Component');
});
Expand All @@ -174,7 +172,7 @@ describe('DocViewerComponent', () => {
</div>
<p>Below Foo</p>
`;
component.docViewer.doc = { metadata: mockDocMetadata, content };
component.docViewer.doc = { metadata: fakeDocMetadata, content };
const foos = docViewerEl.querySelectorAll('aio-foo');
expect(foos.length).toBe(2);
});
Expand All @@ -185,7 +183,7 @@ describe('DocViewerComponent', () => {
<aio-bar></aio-bar>
<p>Below Bar</p>
`;
component.docViewer.doc = { metadata: mockDocMetadata, content };
component.docViewer.doc = { metadata: fakeDocMetadata, content };
const barHtml = docViewerEl.querySelector('aio-bar').innerHTML;
expect(barHtml).toContain('Bar Component');
});
Expand All @@ -196,7 +194,7 @@ describe('DocViewerComponent', () => {
<aio-bar>###bar content###</aio-bar>
<p>Below Bar</p>
`;
component.docViewer.doc = { metadata: mockDocMetadata, content };
component.docViewer.doc = { metadata: fakeDocMetadata, content };

// necessary to trigger projection within ngOnInit
fixture.detectChanges();
Expand All @@ -214,7 +212,7 @@ describe('DocViewerComponent', () => {
<p><aio-foo></aio-foo></p>
<p>Bottom</p>
`;
component.docViewer.doc = { metadata: mockDocMetadata, content };
component.docViewer.doc = { metadata: fakeDocMetadata, content };

// necessary to trigger Bar's projection within ngOnInit
fixture.detectChanges();
Expand All @@ -237,7 +235,7 @@ describe('DocViewerComponent', () => {
<p><aio-foo></aio-foo><p>
<p>Bottom</p>
`;
component.docViewer.doc = { metadata: mockDocMetadata, content };
component.docViewer.doc = { metadata: fakeDocMetadata, content };

// necessary to trigger Bar's projection within ngOnInit
fixture.detectChanges();
Expand All @@ -261,7 +259,7 @@ describe('DocViewerComponent', () => {
<p><aio-foo></aio-foo></p>
<p>Bottom</p>
`;
component.docViewer.doc = { metadata: mockDocMetadata, content };
component.docViewer.doc = { metadata: fakeDocMetadata, content };

// necessary to trigger Bar's projection within ngOnInit
fixture.detectChanges();
Expand Down Expand Up @@ -289,7 +287,7 @@ describe('DocViewerComponent', () => {
<p><aio-baz>---More baz--</aio-baz></p>
<p>Bottom</p>
`;
component.docViewer.doc = { metadata: mockDocMetadata, content };
component.docViewer.doc = { metadata: fakeDocMetadata, content };

// necessary to trigger Bar's projection within ngOnInit
fixture.detectChanges();
Expand Down
15 changes: 12 additions & 3 deletions aio/src/app/doc-viewer/doc-viewer.component.ts
Expand Up @@ -3,7 +3,7 @@ import {
DoCheck, ElementRef, Injector, Input, OnDestroy, ViewEncapsulation
} from '@angular/core';

import { Doc, DocMetadata } from '../nav-engine';
import { Doc, DocMetadata, DocMetadataService, NavNode } from '../nav-engine';
import { EmbeddedComponents } from '../embedded';

interface EmbeddedComponentFactory {
Expand All @@ -17,7 +17,14 @@ const initialDocViewerContent = initialDocViewerElement ? initialDocViewerElemen

@Component({
selector: 'aio-doc-viewer',
template: ''
template: '',
providers: [ DocMetadataService ],
styles: [ `
:host >>> doc-title.not-found h1 {
color: white;
background-color: red;
}
`]
// TODO(robwormald): shadow DOM and emulated don't work here (?!)
// encapsulation: ViewEncapsulation.Native
})
Expand All @@ -31,7 +38,8 @@ export class DocViewerComponent implements DoCheck, OnDestroy {
componentFactoryResolver: ComponentFactoryResolver,
elementRef: ElementRef,
embeddedComponents: EmbeddedComponents,
private injector: Injector,
private docMetadataService: DocMetadataService,
private injector: Injector
) {
this.hostElement = elementRef.nativeElement;
// Security: the initialDocViewerContent comes from the prerendered DOM and is considered to be secure
Expand All @@ -49,6 +57,7 @@ export class DocViewerComponent implements DoCheck, OnDestroy {
set doc(newDoc: Doc) {
this.ngOnDestroy();
if (newDoc) {
this.docMetadataService.metadata = newDoc.metadata;
window.scrollTo(0, 0);
this.build(newDoc);
}
Expand Down
14 changes: 14 additions & 0 deletions aio/src/app/embedded/doc-title.component.ts
@@ -0,0 +1,14 @@
/* tslint:disable component-selector */
import { Component } from '@angular/core';
import { DocMetadataService } from '../nav-engine';

@Component({
selector: 'doc-title',
template: '<h1 class="docs-primary-header">{{title}}</h1>'
})
export class DocTitleComponent {
title: string;
constructor(metadataService: DocMetadataService) {
this.title = metadataService.metadata.title;
}
}
5 changes: 3 additions & 2 deletions aio/src/app/embedded/index.ts
@@ -1,8 +1,9 @@
import { CodeExampleComponent } from './code-example.component';
import { DocTitleComponent } from './doc-title.component';

/** Components that can be embedded in docs such as CodeExampleComponent, LiveExampleComponent,... */
export const embeddedComponents = [
CodeExampleComponent
export const embeddedComponents: any[] = [
CodeExampleComponent, DocTitleComponent
];

/** Injectable class w/ property returning components that can be embedded in docs */
Expand Down