Merge branch 'migrate-to-angular-cli' into 'develop'
Migrate to angular cli See merge request manuel/dencode.org!20
This commit is contained in:
		
						commit
						ec16831e59
					
				
					 16 changed files with 2254 additions and 1303 deletions
				
			
		
							
								
								
									
										2
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							|  | @ -4,7 +4,6 @@ | ||||||
| /dist | /dist | ||||||
| /tmp | /tmp | ||||||
| /out-tsc | /out-tsc | ||||||
| /src/**/*.js |  | ||||||
| 
 | 
 | ||||||
| # dependencies | # dependencies | ||||||
| /node_modules | /node_modules | ||||||
|  | @ -17,6 +16,7 @@ | ||||||
| *.launch | *.launch | ||||||
| .settings/ | .settings/ | ||||||
| *.sublime-workspace | *.sublime-workspace | ||||||
|  | *.iml | ||||||
| 
 | 
 | ||||||
| # IDE - VSCode | # IDE - VSCode | ||||||
| .vscode/* | .vscode/* | ||||||
|  |  | ||||||
|  | @ -4,23 +4,23 @@ stages: | ||||||
|   - deploy |   - deploy | ||||||
|   - cleanup |   - cleanup | ||||||
| 
 | 
 | ||||||
| build_job: | #build_job: | ||||||
|   stage: build | #  stage: build | ||||||
|   script: | #  script: | ||||||
|     - yarn install | #    - yarn install | ||||||
|     - yarn run build | #    - yarn run build | ||||||
|     - yarn run lint | #    - yarn run lint | ||||||
| #    - yarn run test | #    - yarn run test | ||||||
| #    - yarn run e2e | #    - yarn run e2e | ||||||
|   tags: | #  tags: | ||||||
|     - javascript | #    - javascript | ||||||
|   except: | #  except: | ||||||
|     - tags | #    - tags | ||||||
|     - master | #    - master | ||||||
|   artifacts: | #  artifacts: | ||||||
|     paths: | #    paths: | ||||||
|       - dist | #      - dist | ||||||
|     expire_in: 30 min | #    expire_in: 30 min | ||||||
| 
 | 
 | ||||||
| build_job_production: | build_job_production: | ||||||
|   stage: build |   stage: build | ||||||
|  | @ -32,8 +32,8 @@ build_job_production: | ||||||
| #    - yarn run e2e | #    - yarn run e2e | ||||||
|   tags: |   tags: | ||||||
|     - javascript |     - javascript | ||||||
|   only: | #  only: | ||||||
|     - master | #    - master | ||||||
|   artifacts: |   artifacts: | ||||||
|     paths: |     paths: | ||||||
|       - dist |       - dist | ||||||
|  | @ -58,7 +58,7 @@ pages: | ||||||
|     paths: |     paths: | ||||||
|       - public |       - public | ||||||
|   dependencies: |   dependencies: | ||||||
|     - build_job |     - build_job_production | ||||||
| 
 | 
 | ||||||
| production: | production: | ||||||
|   stage: deploy |   stage: deploy | ||||||
|  | @ -75,4 +75,3 @@ cleanup_job: | ||||||
|   script: |   script: | ||||||
|     - rm -rf node_modules |     - rm -rf node_modules | ||||||
|   when: always |   when: always | ||||||
|   allow_failure: true |  | ||||||
|  |  | ||||||
|  | @ -7,8 +7,25 @@ describe('convertorizr App', () => { | ||||||
|     page = new ConvertorizrPage(); |     page = new ConvertorizrPage(); | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|   it('should display message saying app works', () => { |   it('should display a textarea that is initially empty', () => { | ||||||
|     page.navigateTo(); |     page.navigateTo() | ||||||
|     expect(page.getInputfieldContent(0)).toEqual(''); |       .then(() => page.getInputFieldContent(0)) | ||||||
|  |       .then((value: string) => { | ||||||
|  |         expect(value).toEqual(''); | ||||||
|  |       }); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should convert a string to its base64 representation', () => { | ||||||
|  |     page.navigateTo() | ||||||
|  |       .then(() => page.setInputFieldContent(0, 'Hello, World!')) | ||||||
|  |       .then(() => page.getSelectedConverterOption(0)) | ||||||
|  |       .then((option: string) => { | ||||||
|  |         expect(option).toEqual('Select conversion ...'); | ||||||
|  |       }) | ||||||
|  |       .then(() => page.selectConverterOption(0, 'Encode Base 64')) | ||||||
|  |       .then(() => page.getInputFieldContent(1)) | ||||||
|  |       .then((content: string) => { | ||||||
|  |         expect(content).toEqual('SGVsbG8sIFdvcmxkIQ=='); | ||||||
|  |       }); | ||||||
|   }); |   }); | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | @ -1,25 +1,44 @@ | ||||||
| import {browser, by, element} from 'protractor'; | import {browser, by, element, ElementFinder} from 'protractor'; | ||||||
|  | import {promise, WebElementPromise} from 'selenium-webdriver'; | ||||||
| 
 | 
 | ||||||
| export class ConvertorizrPage { | export class ConvertorizrPage { | ||||||
|   navigateTo() { |   navigateTo(): promise.Promise<any> { | ||||||
|     return browser.get('/'); |     return browser.get('/'); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   public foo() { |   private getInputField(index: number): WebElementPromise { | ||||||
|     return 'bar'; |     return element | ||||||
|  |       .all(by.css('app-root div.inputwrapper')) | ||||||
|  |       .get(index) | ||||||
|  |       .element(by.css('.textwrapper textarea')) | ||||||
|  |       .getWebElement(); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   public getInputfieldContent(index: number): Promise<any> { |   getInputFieldContent(index: number): promise.Promise<string> { | ||||||
|     const css1 = by.css('app-root div.inputwrapper'); |     return this.getInputField(index).getText(); | ||||||
|     console.log(css1); |   } | ||||||
|     const el1 = element.all(css1)[index]; | 
 | ||||||
|     console.log(el1); |   setInputFieldContent(index: number, content: string): promise.Promise<void> { | ||||||
|     const css2 = by.css('.textwrapper textarea'); |     return this.getInputField(index).sendKeys(content); | ||||||
|     console.log(css2); |   } | ||||||
|     const el2 = el1.findElement(css2); | 
 | ||||||
|     console.log(el2); |   private getConverterDropdown(index: number): ElementFinder { | ||||||
|     const t = el2.getText(); |     return element | ||||||
|     console.log(t); |       .all(by.css('app-root div.inputwrapper')) | ||||||
|     return t; |       .get(index) | ||||||
|  |       .element(by.css('.selectwrapper select')); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   getSelectedConverterOption(index: number): promise.Promise<string> { | ||||||
|  |     return this.getConverterDropdown(index) | ||||||
|  |       .$('option:checked') | ||||||
|  |       .getWebElement() | ||||||
|  |       .getText(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   selectConverterOption(index: number, optionName: string): promise.Promise<void> { | ||||||
|  |     return this.getConverterDropdown(index) | ||||||
|  |       .element(by.cssContainingText('option', optionName)) | ||||||
|  |       .click(); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -7,7 +7,7 @@ module.exports = function (config) { | ||||||
|     frameworks: ['jasmine', '@angular/cli'], |     frameworks: ['jasmine', '@angular/cli'], | ||||||
|     plugins: [ |     plugins: [ | ||||||
|       require('karma-jasmine'), |       require('karma-jasmine'), | ||||||
|       require('karma-phantomjs-launcher'), |       require('karma-nightmare'), | ||||||
|       require('karma-jasmine-html-reporter'), |       require('karma-jasmine-html-reporter'), | ||||||
|       require('karma-coverage-istanbul-reporter'), |       require('karma-coverage-istanbul-reporter'), | ||||||
|       require('@angular/cli/plugins/karma') |       require('@angular/cli/plugins/karma') | ||||||
|  | @ -42,7 +42,7 @@ module.exports = function (config) { | ||||||
|     colors: true, |     colors: true, | ||||||
|     logLevel: config.LOG_INFO, |     logLevel: config.LOG_INFO, | ||||||
|     autoWatch: true, |     autoWatch: true, | ||||||
|     browsers: ['PhantomJS'], |     browsers: ['Nightmare'], | ||||||
|     singleRun: false |     singleRun: false | ||||||
|   }); |   }); | ||||||
| }; | }; | ||||||
|  |  | ||||||
							
								
								
									
										45
									
								
								package.json
									
										
									
									
									
								
							
							
						
						
									
										45
									
								
								package.json
									
										
									
									
									
								
							|  | @ -22,36 +22,36 @@ | ||||||
|   "scripts": { |   "scripts": { | ||||||
|     "ng": "ng", |     "ng": "ng", | ||||||
|     "start": "ng serve", |     "start": "ng serve", | ||||||
|     "build": "ng build", |     "build": "ng build --delete-output-path", | ||||||
|     "build-prod": "ng build --env=prod", |     "build-prod": "ng build -prod -e prod --aot --delete-output-path --build-optimizer", | ||||||
|     "test": "ng test --single-run", |     "test": "ng test --single-run", | ||||||
|     "test-continuous": "ng test", |     "test-continuous": "ng test", | ||||||
|     "lint": "ng lint", |     "lint": "ng lint", | ||||||
|     "e2e": "ng e2e" |     "e2e": "ng e2e", | ||||||
|  |     "postinstall": "npm rebuild node-sass" | ||||||
|   }, |   }, | ||||||
|   "private": true, |   "private": true, | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|     "@angular/common": "^4.0.0", |     "@angular/common": "^5.0.0", | ||||||
|     "@angular/compiler": "^4.0.0", |     "@angular/compiler": "^5.0.0", | ||||||
|     "@angular/core": "^4.0.0", |     "@angular/core": "^5.0.0", | ||||||
|     "@angular/forms": "^4.0.0", |     "@angular/forms": "^5.0.0", | ||||||
|     "@angular/http": "^4.0.0", |     "@angular/platform-browser": "^5.0.0", | ||||||
|     "@angular/platform-browser": "^4.0.0", |     "@angular/platform-browser-dynamic": "^5.0.0", | ||||||
|     "@angular/platform-browser-dynamic": "^4.0.0", |     "@angular/router": "^5.0.0", | ||||||
|     "@angular/router": "^4.0.0", |     "core-js": "^2.5.1", | ||||||
|     "core-js": "^2.4.1", |  | ||||||
|     "punycode": "^2.1.0", |     "punycode": "^2.1.0", | ||||||
|     "quoted-printable": "^1.0.0", |     "quoted-printable": "^1.0.0", | ||||||
|     "rxjs": "^5.1.0", |     "rxjs": "^5.5.2", | ||||||
|     "utf8": "^2.1.0", |     "utf8": "^2.1.0", | ||||||
|     "zone.js": "^0.8.4" |     "zone.js": "^0.8.18" | ||||||
|   }, |   }, | ||||||
|   "devDependencies": { |   "devDependencies": { | ||||||
|     "@angular/cli": "^1.0.0", |     "@angular/cli": "^1.5.0", | ||||||
|     "@angular/compiler-cli": "^4.0.0", |     "@angular/compiler-cli": "^5.0.0", | ||||||
|     "@types/jasmine": "^2.5.38", |     "@types/jasmine": "^2.5.38", | ||||||
|     "@types/node": "^7.0.0", |     "@types/node": "^8.0.50", | ||||||
|     "codelyzer": "^2.0.0", |     "codelyzer": "^4.0.1", | ||||||
|     "jasmine-core": "^2.5.2", |     "jasmine-core": "^2.5.2", | ||||||
|     "jasmine-spec-reporter": "^4.0.0", |     "jasmine-spec-reporter": "^4.0.0", | ||||||
|     "karma": "^1.4.1", |     "karma": "^1.4.1", | ||||||
|  | @ -60,11 +60,12 @@ | ||||||
|     "karma-coverage-istanbul-reporter": "^1.2.0", |     "karma-coverage-istanbul-reporter": "^1.2.0", | ||||||
|     "karma-jasmine": "^1.1.0", |     "karma-jasmine": "^1.1.0", | ||||||
|     "karma-jasmine-html-reporter": "^0.2.2", |     "karma-jasmine-html-reporter": "^0.2.2", | ||||||
|     "karma-phantomjs-launcher": "^1.0.4", |     "karma-nightmare": "^0.4.9", | ||||||
|  |     "nightmare": "^2.10.0", | ||||||
|     "protractor": "^5.1.0", |     "protractor": "^5.1.0", | ||||||
|     "protractor-console": "^2.0.1", |     "protractor-console": "^3.0.0", | ||||||
|     "ts-node": "^3.0.0", |     "ts-node": "^3.0.0", | ||||||
|     "tslint": "^4.0.0", |     "tslint": "^5.0.0", | ||||||
|     "typescript": "~2.2.0" |     "typescript": "^2.2.0" | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -17,8 +17,7 @@ exports.config = { | ||||||
|   jasmineNodeOpts: { |   jasmineNodeOpts: { | ||||||
|     showColors: true, |     showColors: true, | ||||||
|     defaultTimeoutInterval: 30000, |     defaultTimeoutInterval: 30000, | ||||||
|     print: function () { |     print: function() {} | ||||||
|     } |  | ||||||
|   }, |   }, | ||||||
|   beforeLaunch: function() { |   beforeLaunch: function() { | ||||||
|     require('ts-node').register({ |     require('ts-node').register({ | ||||||
|  |  | ||||||
|  | @ -1,7 +1,10 @@ | ||||||
| import {AppComponent} from './app.component'; | import {AppComponent} from './app.component'; | ||||||
| import {async, ComponentFixture, TestBed} from '@angular/core/testing'; | import {async, ComponentFixture, TestBed} from '@angular/core/testing'; | ||||||
|  | import {FormsModule} from '@angular/forms'; | ||||||
| import {InputComponentManagerService} from './inputcomponentmanager.service'; | import {InputComponentManagerService} from './inputcomponentmanager.service'; | ||||||
| import {Step} from './step'; | import {Step} from './step'; | ||||||
|  | import {ConverterRegistryService} from './converterregistry.service'; | ||||||
|  | import {Converter} from './converter/converter'; | ||||||
| 
 | 
 | ||||||
| describe('AppComponent', () => { | describe('AppComponent', () => { | ||||||
|   let sut: AppComponent; |   let sut: AppComponent; | ||||||
|  | @ -14,13 +17,25 @@ describe('AppComponent', () => { | ||||||
|     } |     } | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|  |   const converterRegistryServiceStub = { | ||||||
|  |     getAllConverters: (): Converter[] => { | ||||||
|  |       return []; | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     getConverter: (id: string): Converter => { | ||||||
|  |       return undefined; | ||||||
|  |     } | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|   beforeEach(async(() => { |   beforeEach(async(() => { | ||||||
|     /*return */ |     /*return */ | ||||||
|     TestBed.configureTestingModule({ |     TestBed.configureTestingModule({ | ||||||
|       declarations: [AppComponent], |       declarations: [AppComponent], | ||||||
|       providers: [{ |       imports: [FormsModule], | ||||||
|         provide: InputComponentManagerService, useValue: inputComponentManagerServiceStub |       providers: [ | ||||||
|       }] |         {provide: InputComponentManagerService, useValue: inputComponentManagerServiceStub}, | ||||||
|  |         {provide: ConverterRegistryService, useValue: converterRegistryServiceStub} | ||||||
|  |       ] | ||||||
|     }) |     }) | ||||||
|       .compileComponents(); |       .compileComponents(); | ||||||
|   })); |   })); | ||||||
|  | @ -29,26 +44,10 @@ describe('AppComponent', () => { | ||||||
|     fixture = TestBed.createComponent(AppComponent); |     fixture = TestBed.createComponent(AppComponent); | ||||||
|     sut = fixture.componentInstance; |     sut = fixture.componentInstance; | ||||||
|   }); |   }); | ||||||
|   // beforeEach(async(() => {
 |  | ||||||
|   //   TestBed.configureTestingModule({
 |  | ||||||
|   //     imports: [
 |  | ||||||
|   //       RouterTestingModule
 |  | ||||||
|   //     ],
 |  | ||||||
|   //     declarations: [
 |  | ||||||
|   //       AppComponent
 |  | ||||||
|   //     ],
 |  | ||||||
|   //   }).compileComponents();
 |  | ||||||
|   // }));
 |  | ||||||
| 
 | 
 | ||||||
|   it('should be true that true is true', () => { |   it('should create the app', async(() => { | ||||||
|     expect(true).toBe(true); |  | ||||||
|   }); |  | ||||||
| 
 |  | ||||||
|   // it('should create the app', async(() => {
 |  | ||||||
|     // const fixture = TestBed.createComponent(AppComponent);
 |     // const fixture = TestBed.createComponent(AppComponent);
 | ||||||
|   //   const app = fixture.debugElement.componentInstance;
 |     const app = fixture.debugElement.componentInstance; | ||||||
|   //   expect(app).toBeTruthy();
 |     expect(app).toBeTruthy(); | ||||||
|   // }));
 |   })); | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | @ -1,15 +1,13 @@ | ||||||
| import {Component, OnInit} from '@angular/core'; | import {Component, OnInit} from '@angular/core'; | ||||||
| import {ConverterRegistryService} from './converterregistry.service'; | import {ConverterRegistryService} from './converterregistry.service'; | ||||||
| import {InputComponentManagerService} from './inputcomponentmanager.service'; | import {InputComponentManagerService} from './inputcomponentmanager.service'; | ||||||
| import {NativeLibraryWrapperService} from './nativelibrarywrapper.service'; |  | ||||||
| import {Step} from './step'; | import {Step} from './step'; | ||||||
| import {Converter} from './converter/converter'; | import {Converter} from './converter/converter'; | ||||||
| 
 | 
 | ||||||
| @Component({ | @Component({ | ||||||
|   selector: 'app-root', |   selector: 'app-root', | ||||||
|   templateUrl: './app.component.html', |   templateUrl: './app.component.html', | ||||||
|   styleUrls: ['./app.component.scss'], |   styleUrls: ['./app.component.scss'] | ||||||
|   providers: [ConverterRegistryService, InputComponentManagerService, NativeLibraryWrapperService] |  | ||||||
| }) | }) | ||||||
| export class AppComponent implements OnInit { | export class AppComponent implements OnInit { | ||||||
|   public steps: Step[] = []; |   public steps: Step[] = []; | ||||||
|  |  | ||||||
|  | @ -1,10 +1,10 @@ | ||||||
| import {BrowserModule} from '@angular/platform-browser'; | import {BrowserModule} from '@angular/platform-browser'; | ||||||
| import {NgModule} from '@angular/core'; | import {NgModule} from '@angular/core'; | ||||||
| import {FormsModule} from '@angular/forms'; |  | ||||||
| import {HttpModule} from '@angular/http'; |  | ||||||
| 
 |  | ||||||
| import {AppRoutingModule} from './app-routing.module'; |  | ||||||
| import {AppComponent} from './app.component'; | import {AppComponent} from './app.component'; | ||||||
|  | import {ConverterRegistryService} from './converterregistry.service'; | ||||||
|  | import {InputComponentManagerService} from './inputcomponentmanager.service'; | ||||||
|  | import {NativeLibraryWrapperService} from './nativelibrarywrapper.service'; | ||||||
|  | import {FormsModule} from '@angular/forms'; | ||||||
| 
 | 
 | ||||||
| @NgModule({ | @NgModule({ | ||||||
|   declarations: [ |   declarations: [ | ||||||
|  | @ -12,11 +12,9 @@ import {AppComponent} from './app.component'; | ||||||
|   ], |   ], | ||||||
|   imports: [ |   imports: [ | ||||||
|     BrowserModule, |     BrowserModule, | ||||||
|     FormsModule, |     FormsModule | ||||||
|     HttpModule, |  | ||||||
|     AppRoutingModule |  | ||||||
|   ], |   ], | ||||||
|   providers: [], |   providers: [ConverterRegistryService, InputComponentManagerService, NativeLibraryWrapperService], | ||||||
|   bootstrap: [AppComponent] |   bootstrap: [AppComponent] | ||||||
| }) | }) | ||||||
| export class AppModule { | export class AppModule { | ||||||
|  |  | ||||||
|  | @ -11,7 +11,7 @@ export class HTMLEntitiesDecoder implements Converter { | ||||||
| 
 | 
 | ||||||
|   convert(input: string): string { |   convert(input: string): string { | ||||||
|     return input |     return input | ||||||
|       .replace(/\"\;/g, '\'') |       .replace(/\"\;/g, '"') | ||||||
|       .replace(/\>\;/g, '>') |       .replace(/\>\;/g, '>') | ||||||
|       .replace(/\<\;/g, '<') |       .replace(/\<\;/g, '<') | ||||||
|       .replace(/\&\;/g, '&'); |       .replace(/\&\;/g, '&'); | ||||||
|  |  | ||||||
|  | @ -14,6 +14,6 @@ export class HTMLEntitiesEncoder implements Converter { | ||||||
|       .replace(/\&/g, '&') |       .replace(/\&/g, '&') | ||||||
|       .replace(/\</g, '<') |       .replace(/\</g, '<') | ||||||
|       .replace(/\>/g, '>') |       .replace(/\>/g, '>') | ||||||
|       .replace(/\'/g, '"'); |       .replace(/\"/g, '"'); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
							
								
								
									
										
											BIN
										
									
								
								src/favicon.ico
									
										
									
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/favicon.ico
									
										
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 2.4 KiB | 
|  | @ -17,19 +17,19 @@ | ||||||
|  * BROWSER POLYFILLS |  * BROWSER POLYFILLS | ||||||
|  */ |  */ | ||||||
| /** IE9, IE10 and IE11 requires all of the following polyfills. **/ | /** IE9, IE10 and IE11 requires all of the following polyfills. **/ | ||||||
| import 'core-js/es6/symbol'; | // import 'core-js/es6/symbol';
 | ||||||
| import 'core-js/es6/object'; | // import 'core-js/es6/object';
 | ||||||
| import 'core-js/es6/function'; | // import 'core-js/es6/function';
 | ||||||
| import 'core-js/es6/parse-int'; | // import 'core-js/es6/parse-int';
 | ||||||
| import 'core-js/es6/parse-float'; | // import 'core-js/es6/parse-float';
 | ||||||
| import 'core-js/es6/number'; | // import 'core-js/es6/number';
 | ||||||
| import 'core-js/es6/math'; | // import 'core-js/es6/math';
 | ||||||
| import 'core-js/es6/string'; | // import 'core-js/es6/string';
 | ||||||
| import 'core-js/es6/date'; | // import 'core-js/es6/date';
 | ||||||
| import 'core-js/es6/array'; | // import 'core-js/es6/array';
 | ||||||
| import 'core-js/es6/regexp'; | // import 'core-js/es6/regexp';
 | ||||||
| import 'core-js/es6/map'; | // import 'core-js/es6/map';
 | ||||||
| import 'core-js/es6/set'; | // import 'core-js/es6/set';
 | ||||||
| /** IE10 and IE11 requires the following for NgClass support on SVG elements */ | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ | ||||||
| // import 'classlist.js';  // Run `npm install --save classlist.js`.
 | // import 'classlist.js';  // Run `npm install --save classlist.js`.
 | ||||||
| /** IE10 and IE11 requires the following to support `@angular/animation`. */ | /** IE10 and IE11 requires the following to support `@angular/animation`. */ | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue