Skip to content
This repository was archived by the owner on Dec 18, 2024. It is now read-only.

Commit 4e993c5

Browse files
authored
feat(doc-viewer): add doc-viewer component (#17)
* component skeleton * Load document and instantiate examples * addressed comments * integrated w/ tina's stuff * fixed indent * move under src/app/shared * started tests * finish tests
1 parent 692aa4e commit 4e993c5

File tree

13 files changed

+324
-5
lines changed

13 files changed

+324
-5
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
"@angular/core": "~2.2.3",
2222
"@angular/forms": "~2.2.3",
2323
"@angular/http": "~2.2.3",
24-
"@angular/material": "^2.0.0-alpha.11-3",
24+
"@angular/material": "2.0.0-alpha.11-3",
2525
"@angular/platform-browser": "~2.2.3",
2626
"@angular/platform-browser-dynamic": "~2.2.3",
2727
"@angular/router": "~3.2.3",

src/app/app-module.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import {Homepage} from './pages/homepage/homepage';
99
import {NavBar} from './shared/navbar/navbar';
1010
import {routing} from './routes';
1111
import {ComponentsList} from './pages/components/components';
12+
import {DocViewerModule} from './shared/doc-viewer/index';
13+
import {ExampleViewerModule} from './shared/example-viewer/index';
1214

1315

1416
@NgModule({
@@ -18,8 +20,16 @@ import {ComponentsList} from './pages/components/components';
1820
Homepage,
1921
NavBar,
2022
],
23+
exports: [
24+
MaterialDocsApp,
25+
ComponentsList,
26+
Homepage,
27+
NavBar,
28+
],
2129
imports: [
2230
BrowserModule,
31+
DocViewerModule,
32+
ExampleViewerModule,
2333
FormsModule,
2434
HttpModule,
2535
MaterialModule.forRoot(),

src/app/material-docs-app.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import {Component} from '@angular/core';
22

33

44
@Component({
5-
moduleId: module.id,
65
selector: 'material-docs-app',
76
templateUrl: './material-docs-app.html',
87
styleUrls: ['./material-docs-app.scss'],

src/app/pages/components/components.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
<img src="../../../assets/img/components/{{component.src}}.svg" [alt]="component.name" />
33
{{component.name}}
44
</div>
5+
<doc-viewer documentUrl="/assets/documents/README.html"></doc-viewer>

src/app/pages/components/components.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { Component} from '@angular/core';
22

33
@Component({
4-
moduleId: module.id,
54
selector: 'app-components',
65
templateUrl: './components.html',
76
styleUrls: ['./components.scss']

src/app/pages/homepage/homepage.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { Component} from '@angular/core';
22

33
@Component({
4-
moduleId: module.id,
54
selector: 'app-homepage',
65
templateUrl: './homepage.html',
76
styleUrls: ['./homepage.scss']
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import {Component} from '@angular/core';
2+
import {TestBed, inject, async} from '@angular/core/testing';
3+
import {MockBackend} from '@angular/http/testing';
4+
import {Response, ResponseOptions, XHRBackend} from '@angular/http';
5+
import {DocViewerModule} from './index';
6+
import {By} from '@angular/platform-browser';
7+
import {DocViewer} from './doc-viewer';
8+
9+
10+
describe('DocViewer', () => {
11+
beforeEach(async(() => {
12+
TestBed.configureTestingModule({
13+
imports: [DocViewerModule],
14+
declarations: [
15+
DocViewerTestComponent,
16+
],
17+
providers: [
18+
MockBackend,
19+
{provide: XHRBackend, useExisting: MockBackend},
20+
]
21+
});
22+
23+
TestBed.compileComponents();
24+
}));
25+
26+
beforeEach(inject([MockBackend], (mockBackend: MockBackend) => {
27+
mockBackend.connections.subscribe((connection: any) => {
28+
const url = connection.request.url;
29+
connection.mockRespond(getFakeDocResponse(url));
30+
});
31+
}));
32+
33+
it('should load doc into innerHTML', () => {
34+
let fixture = TestBed.createComponent(DocViewerTestComponent);
35+
fixture.detectChanges();
36+
37+
let docViewer = fixture.debugElement.query(By.directive(DocViewer));
38+
expect(docViewer).not.toBeNull();
39+
expect(docViewer.nativeElement.innerHTML).toBe('<div>my docs page</div>');
40+
});
41+
42+
it('should show error message when doc not found', () => {
43+
let fixture = TestBed.createComponent(DocViewerTestComponent);
44+
fixture.detectChanges();
45+
46+
fixture.componentInstance.documentUrl = 'http://material.angular.io/error-doc.html';
47+
fixture.detectChanges();
48+
49+
let docViewer = fixture.debugElement.query(By.directive(DocViewer));
50+
expect(docViewer).not.toBeNull();
51+
expect(docViewer.nativeElement.innerHTML).toContain(
52+
'Failed to load document: http://material.angular.io/error-doc.html');
53+
});
54+
55+
// TODO(mmalerba): Add test that example-viewer is instantiated.
56+
});
57+
58+
@Component({
59+
selector: 'test',
60+
template: `<doc-viewer [documentUrl]="documentUrl"></doc-viewer>`,
61+
})
62+
class DocViewerTestComponent {
63+
documentUrl = 'http://material.angular.io/simple-doc.html';
64+
}
65+
66+
const FAKE_DOCS = {
67+
'http://material.angular.io/simple-doc.html': '<div>my docs page</div>',
68+
'http://material.angular.io/doc-with-example.html': `
69+
<div>Check out this example:</div>
70+
<div material-docs-example="some-example"></div>`,
71+
};
72+
73+
function getFakeDocResponse(url: string) {
74+
if (url in FAKE_DOCS) {
75+
return new Response(new ResponseOptions({
76+
status: 200,
77+
body: FAKE_DOCS[url],
78+
}));
79+
} else {
80+
return new Response(new ResponseOptions({status: 404}));
81+
}
82+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import {
2+
Component,
3+
Input,
4+
ElementRef,
5+
ComponentFactoryResolver,
6+
ViewContainerRef,
7+
ApplicationRef,
8+
Injector
9+
} from '@angular/core';
10+
import {Http} from '@angular/http';
11+
import {DomPortalHost, ComponentPortal} from '@angular/material';
12+
import {ExampleViewer} from '../example-viewer/example-viewer';
13+
14+
15+
@Component({
16+
selector: 'doc-viewer',
17+
template: 'Loading document...',
18+
})
19+
export class DocViewer {
20+
/** The URL of the document to display. */
21+
@Input()
22+
set documentUrl(url: string) {
23+
this._fetchDocument(url);
24+
}
25+
26+
constructor(private _appRef: ApplicationRef,
27+
private _componentFactoryResolver: ComponentFactoryResolver,
28+
private _elementRef: ElementRef,
29+
private _http: Http,
30+
private _injector: Injector,
31+
private _viewContainerRef: ViewContainerRef) {}
32+
33+
/** Fetch a document by URL. */
34+
private _fetchDocument(url: string) {
35+
this._http.get(url).subscribe(
36+
response => {
37+
// TODO(mmalerba): Trust HTML.
38+
if (response.ok) {
39+
let docHtml = response.text();
40+
this._elementRef.nativeElement.innerHTML = docHtml;
41+
this._loadLiveExamples();
42+
} else {
43+
this._elementRef.nativeElement.innerText =
44+
`Failed to load document: ${url}. Error: ${response.status}`;
45+
}
46+
},
47+
error => {
48+
this._elementRef.nativeElement.innerText =
49+
`Failed to load document: ${url}. Error: ${error}`;
50+
});
51+
}
52+
53+
/** Instantiate a ExampleViewer for each example. */
54+
private _loadLiveExamples() {
55+
let exampleElements =
56+
this._elementRef.nativeElement.querySelectorAll('[material-docs-example]');
57+
Array.prototype.slice.call(exampleElements).forEach((element: Element) => {
58+
console.log('create portal');
59+
let example = element.getAttribute('material-docs-example');
60+
let portalHost =
61+
new DomPortalHost(element, this._componentFactoryResolver, this._appRef, this._injector);
62+
let examplePortal = new ComponentPortal(ExampleViewer, this._viewContainerRef);
63+
let exampleViewer = portalHost.attach(examplePortal);
64+
exampleViewer.instance.example = example;
65+
});
66+
}
67+
}

src/app/shared/doc-viewer/index.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import {NgModule} from '@angular/core';
2+
import {HttpModule} from '@angular/http';
3+
import {DocViewer} from './doc-viewer';
4+
import {ExampleViewer} from '../example-viewer/example-viewer';
5+
import {ExampleViewerModule} from '../example-viewer/index';
6+
7+
export * from './doc-viewer';
8+
9+
@NgModule({
10+
imports: [
11+
ExampleViewerModule,
12+
HttpModule,
13+
],
14+
declarations: [DocViewer],
15+
exports: [DocViewer],
16+
entryComponents: [
17+
ExampleViewer,
18+
],
19+
})
20+
export class DocViewerModule {}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import {Component, Input} from '@angular/core';
2+
3+
4+
@Component({
5+
selector: 'example-viewer',
6+
template: 'EXAMPLE: {{example}}',
7+
})
8+
export class ExampleViewer {
9+
@Input()
10+
example: string;
11+
}

0 commit comments

Comments
 (0)