Skip to content

Commit

Permalink
feat(aio): migrate embedded comp to elements (#22413)
Browse files Browse the repository at this point in the history
PR Close #22413
  • Loading branch information
andrewseguin authored and mhevery committed Mar 16, 2018
1 parent 22b96b9 commit 7c9b411
Show file tree
Hide file tree
Showing 69 changed files with 1,021 additions and 1,753 deletions.
2 changes: 2 additions & 0 deletions aio/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
"@angular/common": "^5.2.0",
"@angular/compiler": "^5.2.0",
"@angular/core": "^5.2.0",
"@angular/elements": "file:../dist/packages-dist/elements",
"@angular/forms": "^5.2.0",
"@angular/http": "^5.2.0",
"@angular/material": "^5.0.0-rc.1",
Expand All @@ -83,6 +84,7 @@
"@angular/platform-server": "^5.2.0",
"@angular/router": "^5.2.0",
"@angular/service-worker": "^1.0.0-beta.16",
"@webcomponents/custom-elements": "^1.0.8",
"classlist.js": "^1.1.20150312",
"core-js": "^2.4.1",
"jasmine": "^2.6.0",
Expand Down
7 changes: 0 additions & 7 deletions aio/src/app/app.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { MatProgressBar, MatSidenav } from '@angular/material';
import { By } from '@angular/platform-browser';

import { Observable } from 'rxjs/Observable';
import { of } from 'rxjs/observable/of';
import { timer } from 'rxjs/observable/timer';
import 'rxjs/add/operator/mapTo';

Expand All @@ -16,7 +15,6 @@ import { AppModule } from './app.module';
import { DocumentService } from 'app/documents/document.service';
import { DocViewerComponent } from 'app/layout/doc-viewer/doc-viewer.component';
import { Deployment } from 'app/shared/deployment.service';
import { EmbedComponentsService } from 'app/embed-components/embed-components.service';
import { GaService } from 'app/shared/ga.service';
import { LocationService } from 'app/shared/location.service';
import { Logger } from 'app/shared/logger.service';
Expand Down Expand Up @@ -1280,7 +1278,6 @@ function createTestingModule(initialUrl: string, mode: string = 'stable') {
imports: [ AppModule ],
providers: [
{ provide: APP_BASE_HREF, useValue: '/' },
{ provide: EmbedComponentsService, useClass: TestEmbedComponentsService },
{ provide: GaService, useClass: TestGaService },
{ provide: HttpClient, useClass: TestHttpClient },
{ provide: LocationService, useFactory: () => mockLocationService },
Expand All @@ -1295,10 +1292,6 @@ function createTestingModule(initialUrl: string, mode: string = 'stable') {
});
}

class TestEmbedComponentsService {
embedInto = jasmine.createSpy('embedInto').and.returnValue(of([]));
}

class TestGaService {
locationChanged = jasmine.createSpy('locationChanged');
}
Expand Down
50 changes: 0 additions & 50 deletions aio/src/app/app.module.spec.ts
Original file line number Diff line number Diff line change
@@ -1,50 +0,0 @@
import { TestBed } from '@angular/core/testing';
import { AppModule } from 'app/app.module';
import { ComponentsOrModulePath, EMBEDDED_COMPONENTS } from 'app/embed-components/embed-components.service';
import { embeddedComponents } from 'app/embedded/embedded.module';

describe('AppModule', () => {
let componentsMap: {[multiSelectorstring: string]: ComponentsOrModulePath};

beforeEach(() => {
TestBed.configureTestingModule({imports: [AppModule]});
componentsMap = TestBed.get(EMBEDDED_COMPONENTS);
});

it('should provide a map of selectors to embedded components (or module)', () => {
const allSelectors = Object.keys(componentsMap);

expect(allSelectors.length).toBeGreaterThan(1);
allSelectors.forEach(selector => {
const value = componentsMap[selector];
const isArrayOrString = Array.isArray(value) || (typeof value === 'string');
expect(isArrayOrString).toBe(true);
});
});

it('should provide a list of eagerly-loaded embedded components', () => {

const eagerConfig = Object.keys(componentsMap).filter(selector => Array.isArray(componentsMap[selector]));
expect(eagerConfig.length).toBeGreaterThan(0);

const eagerSelectors = eagerConfig.reduce<string[]>((selectors, config) => selectors.concat(config.split(',')), []);
expect(eagerSelectors.length).toBeGreaterThan(0);

// For example...
expect(eagerSelectors).toContain('aio-toc');
expect(eagerSelectors).toContain('aio-announcement-bar');
});

it('should provide a list of lazy-loaded embedded components', () => {
const lazySelector = Object.keys(componentsMap).find(selector => selector.includes('code-example'))!;
const selectorCount = lazySelector.split(',').length;

expect(lazySelector).not.toBeNull();
expect(selectorCount).toBe(embeddedComponents.length);

// For example...
expect(lazySelector).toContain('code-example');
expect(lazySelector).toContain('code-tabs');
expect(lazySelector).toContain('live-example');
});
});
39 changes: 5 additions & 34 deletions aio/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,7 @@ import { MatProgressBarModule } from '@angular/material/progress-bar';
import { MatSidenavModule } from '@angular/material/sidenav';
import { MatToolbarModule } from '@angular/material/toolbar';

import { ROUTES } from '@angular/router';


import { AnnouncementBarComponent } from 'app/embedded/announcement-bar/announcement-bar.component';
import { AppComponent } from 'app/app.component';
import { EMBEDDED_COMPONENTS, EmbeddedComponentsMap } from 'app/embed-components/embed-components.service';
import { CustomIconRegistry, SVG_ICONS } from 'app/shared/custom-icon-registry';
import { Deployment } from 'app/shared/deployment.service';
import { DocViewerComponent } from 'app/layout/doc-viewer/doc-viewer.component';
Expand All @@ -42,14 +37,10 @@ import { TocService } from 'app/shared/toc.service';
import { CurrentDateToken, currentDateProvider } from 'app/shared/current-date';
import { WindowToken, windowProvider } from 'app/shared/window';

import { EmbedComponentsModule } from 'app/embed-components/embed-components.module';
import { CustomElementsModule } from 'app/custom-elements/custom-elements.module';
import { SharedModule } from 'app/shared/shared.module';
import { SwUpdatesModule } from 'app/sw-updates/sw-updates.module';


// The path to the `EmbeddedModule`.
const embeddedModulePath = 'app/embedded/embedded.module#EmbeddedModule';

// These are the hardcoded inline svg sources to be used by the `<mat-icon>` component
export const svgIconProviders = [
{
Expand Down Expand Up @@ -100,18 +91,17 @@ export const svgIconProviders = [
imports: [
BrowserModule,
BrowserAnimationsModule,
EmbedComponentsModule,
CustomElementsModule,
HttpClientModule,
MatButtonModule,
MatIconModule,
MatProgressBarModule,
MatSidenavModule,
MatToolbarModule,
SwUpdatesModule,
SharedModule
SharedModule,
],
declarations: [
AnnouncementBarComponent,
AppComponent,
DocViewerComponent,
DtComponent,
Expand Down Expand Up @@ -142,27 +132,8 @@ export const svgIconProviders = [
TocService,
{ provide: CurrentDateToken, useFactory: currentDateProvider },
{ provide: WindowToken, useFactory: windowProvider },

{
provide: EMBEDDED_COMPONENTS,
useValue: {
/* tslint:disable: max-line-length */
'aio-announcement-bar': [AnnouncementBarComponent],
'aio-toc': [TocComponent],
'aio-api-list, aio-contributor-list, aio-file-not-found-search, aio-resource-list, code-example, code-tabs, current-location, live-example': embeddedModulePath,
/* tslint:enable: max-line-length */
} as EmbeddedComponentsMap,
},
{
// This is currently the only way to get `@angular/cli`
// to split `EmbeddedModule` into a separate chunk :(
provide: ROUTES,
useValue: [{ path: '/embedded', loadChildren: embeddedModulePath }],
multi: true,
},
],
entryComponents: [ AnnouncementBarComponent, TocComponent ],
entryComponents: [ TocComponent ],
bootstrap: [ AppComponent ]
})
export class AppModule {
}
export class AppModule { }
Original file line number Diff line number Diff line change
Expand Up @@ -66,21 +66,15 @@ describe('AnnouncementBarComponent', () => {
const request = httpMock.expectOne('generated/announcements.json');
request.flush('some random response');
expect(component.announcement).toBeUndefined();
expect(mockLogger.output.error).toEqual([
[jasmine.any(Error)]
]);
expect(mockLogger.output.error[0][0].message).toMatch(/^generated\/announcements\.json contains invalid data:/);
expect(mockLogger.output.error[0][0]).toContain('generated/announcements.json contains invalid data:');
});

it('should handle a failed request for `announcements.json`', () => {
component.ngOnInit();
const request = httpMock.expectOne('generated/announcements.json');
request.error(new ErrorEvent('404'));
expect(component.announcement).toBeUndefined();
expect(mockLogger.output.error).toEqual([
[jasmine.any(Error)]
]);
expect(mockLogger.output.error[0][0].message).toMatch(/^generated\/announcements\.json request failed:/);
expect(mockLogger.output.error[0][0]).toContain('generated/announcements.json request failed:');
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,12 @@ export class AnnouncementBarComponent implements OnInit {
ngOnInit() {
this.http.get<Announcement[]>(announcementsPath)
.catch(error => {
this.logger.error(new Error(`${announcementsPath} request failed: ${error.message}`));
this.logger.error(`${announcementsPath} request failed: ${error.message}`);
return [];
})
.map(announcements => this.findCurrentAnnouncement(announcements))
.catch(error => {
this.logger.error(new Error(`${announcementsPath} contains invalid data: ${error.message}`));
this.logger.error(`${announcementsPath} contains invalid data: ${error.message}`);
return [];
})
.subscribe(announcement => this.announcement = announcement);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { NgModule, Type } from '@angular/core';
import { CommonModule } from '@angular/common';
import { HttpClientModule } from '@angular/common/http';
import { SharedModule } from '../../shared/shared.module';
import { AnnouncementBarComponent } from './announcement-bar.component';
import { WithCustomElementComponent } from '../element-registry';

@NgModule({
imports: [ CommonModule, SharedModule, HttpClientModule ],
declarations: [ AnnouncementBarComponent ],
entryComponents: [ AnnouncementBarComponent ],
})
export class AnnouncementBarModule implements WithCustomElementComponent {
customElementComponent: Type<any> = AnnouncementBarComponent;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { ApiListComponent } from './api-list.component';
import { ApiItem, ApiSection, ApiService } from './api.service';
import { LocationService } from 'app/shared/location.service';
import { SharedModule } from 'app/shared/shared.module';
import { Logger } from 'app/shared/logger.service';
import { MockLogger } from 'testing/logger.service';
import { ApiListModule } from './api-list.module';

describe('ApiListComponent', () => {
let component: ApiListComponent;
Expand All @@ -13,10 +15,10 @@ describe('ApiListComponent', () => {

beforeEach(() => {
TestBed.configureTestingModule({
imports: [ SharedModule ],
declarations: [ ApiListComponent ],
imports: [ ApiListModule ],
providers: [
{ provide: ApiService, useClass: TestApiService },
{ provide: Logger, useClass: MockLogger },
{ provide: LocationService, useClass: TestLocationService }
]
});
Expand All @@ -37,11 +39,11 @@ describe('ApiListComponent', () => {
let badItem: ApiItem|undefined;
expect(filtered.length).toBeGreaterThan(0, 'expected something');
expect(filtered.every(section => section.items.every(
item => {
const ok = item.show === itemTest(item);
if (!ok) { badItem = item; }
return ok;
}
item => {
const ok = item.show === itemTest(item);
if (!ok) { badItem = item; }
return ok;
}
))).toBe(true, `${label} fail: ${JSON.stringify(badItem, null, 2)}`);
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class SearchCriteria {

@Component({
selector: 'aio-api-list',
templateUrl: './api-list.component.html'
templateUrl: './api-list.component.html',
})
export class ApiListComponent implements OnInit {

Expand Down Expand Up @@ -69,7 +69,6 @@ export class ApiListComponent implements OnInit {
private locationService: LocationService) { }

ngOnInit() {

this.filteredSections = combineLatest(
this.apiService.sections,
this.criteriaSubject,
Expand Down
17 changes: 17 additions & 0 deletions aio/src/app/custom-elements/api/api-list.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { NgModule, Type } from '@angular/core';
import { CommonModule } from '@angular/common';
import { HttpClientModule } from '@angular/common/http';
import { SharedModule } from '../../shared/shared.module';
import { ApiListComponent } from './api-list.component';
import { ApiService } from './api.service';
import { WithCustomElementComponent } from '../element-registry';

@NgModule({
imports: [ CommonModule, SharedModule, HttpClientModule ],
declarations: [ ApiListComponent ],
entryComponents: [ ApiListComponent ],
providers: [ ApiService ]
})
export class ApiListModule implements WithCustomElementComponent {
customElementComponent: Type<any> = ApiListComponent;
}
File renamed without changes.
Loading

0 comments on commit 7c9b411

Please sign in to comment.