diff --git a/projects/ng-aquila/src/schematics/ng-add/files/app.component.html b/projects/ng-aquila/src/schematics/ng-add/files/app.component.html new file mode 100644 index 000000000..bf1a928e8 --- /dev/null +++ b/projects/ng-aquila/src/schematics/ng-add/files/app.component.html @@ -0,0 +1,112 @@ +

Aquila Insurance App

+ + + Please fill out your application details. + + +
+
+
+
+ + + + Please note: this field is required! + + +
+ +
+ + + 1 + 2 + 3 + + + Please select an option. + + +
+
+
+
+ + + + + + + Please provide a valid email address. + + +
+
+
+
+ I have read and understood + the disclaimer and conditions. + +
+
+
+
+ +
+
+
+
+ + + + +
+ Please enter your email address. +
+
+ + +
+

+ Modal with Disclaimer +

+ +

+ Conditions and Disclaimer +

+

+ Some copy text that describes what this overlay is all about. Tdiam nonumy + eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam + voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Lorem + ipsum dolor sit amet, consetetur sadipscing et justo duo dolores et ea + rebum. Lorem ipsum dolor sit amet, consetetur sadipscing Some copy text + that describes what this overlay is all about. Tdiam nonumy eirmod tempor + invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At + vero eos et accusam et justo duo dolores et ea rebum. Lorem ipsum dolor + sit amet, consetetur sadipscing et justo duo dolores et ea rebum. +

+
+
+ + +
+

+ Your data has been submitted to the backend +

+ +

+ Congratulations, this ends the Starter App user journey. We hope you will use the Starter App as a + foundation to + build + something awesome! +

+
+
+ + \ No newline at end of file diff --git a/projects/ng-aquila/src/schematics/ng-add/files/app.component.spec.ts b/projects/ng-aquila/src/schematics/ng-add/files/app.component.spec.ts new file mode 100644 index 000000000..ae7f23bf3 --- /dev/null +++ b/projects/ng-aquila/src/schematics/ng-add/files/app.component.spec.ts @@ -0,0 +1,73 @@ +import { TestBed } from '@angular/core/testing'; +import { AppComponent } from './app.component'; +import { NxModalModule } from '@aposin/ng-aquila/modal'; +import { HttpClientJsonpModule, HttpClientModule } from '@angular/common/http'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { BrowserModule } from '@angular/platform-browser'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { NxButtonModule } from '@aposin/ng-aquila/button'; +import { NxCheckboxModule } from '@aposin/ng-aquila/checkbox'; +import { NxDocumentationIconModule } from '@aposin/ng-aquila/documentation-icons'; +import { NxDropdownModule } from '@aposin/ng-aquila/dropdown'; +import { NxFooterModule } from '@aposin/ng-aquila/footer'; +import { NxFormfieldModule } from '@aposin/ng-aquila/formfield'; +import { NxGridModule } from '@aposin/ng-aquila/grid'; +import { NxHeadlineModule } from '@aposin/ng-aquila/headline'; +import { NxIconModule } from '@aposin/ng-aquila/icon'; +import { NxInputModule } from '@aposin/ng-aquila/input'; +import { NxLinkModule } from '@aposin/ng-aquila/link'; +import { NxMessageModule } from '@aposin/ng-aquila/message'; +import { NxOverlayModule } from '@aposin/ng-aquila/overlay'; +import { NxPopoverModule } from '@aposin/ng-aquila/popover'; + +describe('AppComponent', () => { + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ + AppComponent + ], + imports: [ + BrowserModule, + BrowserAnimationsModule, + FormsModule, + HttpClientJsonpModule, + HttpClientModule, + ReactiveFormsModule, + NxButtonModule, + NxCheckboxModule, + NxDocumentationIconModule, + NxDropdownModule, + NxFooterModule, + NxFormfieldModule, + NxGridModule, + NxHeadlineModule, + NxIconModule, + NxInputModule, + NxLinkModule, + NxMessageModule, + NxModalModule, + NxOverlayModule, + NxPopoverModule + ] + }).compileComponents(); + }); + + it('should create the app', () => { + const fixture = TestBed.createComponent(AppComponent); + const app = fixture.componentInstance; + expect(app).toBeTruthy(); + }); + + it(`should create the form`, () => { + const fixture = TestBed.createComponent(AppComponent); + const app = fixture.componentInstance; + expect(app.formGroup).toBeTruthy(); + }); + + it('should render title', () => { + const fixture = TestBed.createComponent(AppComponent); + fixture.detectChanges(); + const compiled = fixture.nativeElement; + expect(compiled.querySelector('h1.nx-heading--section').textContent).toContain('Aquila Insurance App'); + }); +}); diff --git a/projects/ng-aquila/src/schematics/ng-add/files/app.component.ts b/projects/ng-aquila/src/schematics/ng-add/files/app.component.ts new file mode 100644 index 000000000..1af76d6ae --- /dev/null +++ b/projects/ng-aquila/src/schematics/ng-add/files/app.component.ts @@ -0,0 +1,44 @@ +import { FormGroup, FormBuilder, Validators } from '@angular/forms'; +import { Component, ViewChild, TemplateRef } from '@angular/core'; +import { NxDialogService, NxModalRef } from '@aposin/ng-aquila/modal'; + +@Component({ + // tslint:disable-next-line + selector: 'app-root', + templateUrl: './app.component.html' +}) +export class AppComponent { + @ViewChild('consentTemplate') consentTemplateRef: TemplateRef; + @ViewChild('submitTemplate') submitTemplateRef: TemplateRef; + dialogRef: NxModalRef; + formGroup: FormGroup; + + constructor(public dialogService: NxDialogService) { + this.formGroup = new FormBuilder().group({ + name: ['', Validators.required], + items: ['', Validators.required], + email: ['', [Validators.email, Validators.required]], + consent: [false, Validators.requiredTrue] + }); + } + + openConsentDialog(): void { + this.dialogRef = this.dialogService.open(this.consentTemplateRef, { + ariaLabel: 'A modal with content', + showCloseIcon: true + }); + } + + openSubmitDialog(): void { + this.dialogRef = this.dialogService.open(this.submitTemplateRef, { + ariaLabel: 'The final modal of the Starter App', + showCloseIcon: false + }); + } + + closeDialog() { + this.dialogRef.close(); + } +} + +/** Copyright APOSIN 2021 */ diff --git a/projects/ng-aquila/src/schematics/ng-add/files/app.module.ts b/projects/ng-aquila/src/schematics/ng-add/files/app.module.ts new file mode 100644 index 000000000..5d6b180fd --- /dev/null +++ b/projects/ng-aquila/src/schematics/ng-add/files/app.module.ts @@ -0,0 +1,58 @@ +import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { RouterModule } from '@angular/router'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { HttpClientJsonpModule, HttpClientModule } from '@angular/common/http'; + +import { NxButtonModule } from '@aposin/ng-aquila/button'; +import { NxCheckboxModule } from '@aposin/ng-aquila/checkbox'; +import { NxDocumentationIconModule } from '@aposin/ng-aquila/documentation-icons'; +import { NxDropdownModule } from '@aposin/ng-aquila/dropdown'; +import { NxFooterModule } from '@aposin/ng-aquila/footer'; +import { NxFormfieldModule } from '@aposin/ng-aquila/formfield'; +import { NxGridModule } from '@aposin/ng-aquila/grid'; +import { NxHeadlineModule } from '@aposin/ng-aquila/headline'; +import { NxIconModule } from '@aposin/ng-aquila/icon'; +import { NxInputModule } from '@aposin/ng-aquila/input'; +import { NxLinkModule } from '@aposin/ng-aquila/link'; +import { NxMessageModule } from '@aposin/ng-aquila/message'; +import { NxModalModule } from '@aposin/ng-aquila/modal'; +import { NxOverlayModule } from '@aposin/ng-aquila/overlay'; +import { NxPopoverModule } from '@aposin/ng-aquila/popover'; + +import { AppComponent } from './app.component'; + +@NgModule({ + declarations: [ + AppComponent + ], + imports: [ + BrowserModule, + BrowserAnimationsModule, + FormsModule, + HttpClientJsonpModule, + HttpClientModule, + ReactiveFormsModule, + RouterModule.forRoot([]), + NxButtonModule, + NxCheckboxModule, + NxDocumentationIconModule, + NxDropdownModule, + NxFooterModule, + NxFormfieldModule, + NxGridModule, + NxHeadlineModule, + NxIconModule, + NxInputModule, + NxLinkModule, + NxMessageModule, + NxModalModule, + NxOverlayModule, + NxPopoverModule + ], + bootstrap: [AppComponent], +}) +export class AppModule {} + +/** Copyright APOSIN 2021 */ diff --git a/projects/ng-aquila/src/schematics/ng-add/index.spec.ts b/projects/ng-aquila/src/schematics/ng-add/index.spec.ts index 797304426..89d2f261f 100644 --- a/projects/ng-aquila/src/schematics/ng-add/index.spec.ts +++ b/projects/ng-aquila/src/schematics/ng-add/index.spec.ts @@ -38,6 +38,13 @@ describe('ng-aquila ng add', () => { it('should add css var ponyfill', async () => { expect(testSetup.appTree.readContent('projects/aquila-testing/src/polyfills.ts')).toContain('cssVars('); }); + + it('should not write Starter App files by default', () => { + expect(testSetup.appTree.readContent('projects/aquila-testing/src/app/app.component.ts')) + .not.toContain('openConsentDialog()'); + expect(testSetup.appTree.readContent('projects/aquila-testing/src/app/app.component.html')) + .not.toContain('Aquila Insurance App'); + }); }); describe('expert', () => { @@ -66,4 +73,18 @@ describe('ng-aquila ng add', () => { expect(testProjectConfig.targets.get('build')?.options?.styles).not.toContain('node_modules/@aposin/ng-aquila/themes/aposin.css'); }); }); + + describe('starter app', () => { + beforeEach(async () => { + await testSetup.runMigration({ starter: true }); + testProjectConfig = await getTestProjectConfig(); + }); + + it('should update Starter App files', () => { + expect(testSetup.appTree.readContent('projects/aquila-testing/src/app/app.component.ts')) + .toContain('openConsentDialog()'); + expect(testSetup.appTree.readContent('projects/aquila-testing/src/app/app.component.html')) + .toContain('Aquila Insurance App'); + }); + }); }); diff --git a/projects/ng-aquila/src/schematics/ng-add/schema.json b/projects/ng-aquila/src/schematics/ng-add/schema.json index 4d642b69e..6172b5b88 100644 --- a/projects/ng-aquila/src/schematics/ng-add/schema.json +++ b/projects/ng-aquila/src/schematics/ng-add/schema.json @@ -34,6 +34,12 @@ "description": "Option that no theme is added", "type": "boolean", "default": false + }, + "starter": { + "description": "Option that creates the Starter App", + "type": "boolean", + "default": false, + "x-prompt": "Do you want to update your project with the Aquila Starter App? (WARNING: this will replace existing project files!)" } }, "required": [] diff --git a/projects/ng-aquila/src/schematics/ng-add/schema.ts b/projects/ng-aquila/src/schematics/ng-add/schema.ts index 472cbaff0..f43608cd6 100644 --- a/projects/ng-aquila/src/schematics/ng-add/schema.ts +++ b/projects/ng-aquila/src/schematics/ng-add/schema.ts @@ -15,4 +15,7 @@ export interface Schema { /** Whether no theme should be added. */ noTheme: boolean; + + /** Whether the Starter App should be added. */ + starter: boolean; } diff --git a/projects/ng-aquila/src/schematics/ng-add/setup-project.ts b/projects/ng-aquila/src/schematics/ng-add/setup-project.ts index 34bd1aef6..ab837ac76 100644 --- a/projects/ng-aquila/src/schematics/ng-add/setup-project.ts +++ b/projects/ng-aquila/src/schematics/ng-add/setup-project.ts @@ -1,4 +1,4 @@ -import { chain, noop, Rule, Tree, SchematicContext } from '@angular-devkit/schematics'; +import { chain, noop, Rule, Tree, SchematicContext, apply, mergeWith, url, MergeStrategy, SchematicsException, move, applyTemplates } from '@angular-devkit/schematics'; import { addModuleImportToRootModule, getProjectFromWorkspace, @@ -7,15 +7,17 @@ import { } from '@angular/cdk/schematics'; import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks'; import * as chalk from 'chalk'; -import { getWorkspace, updateWorkspace } from '@schematics/angular/utility/workspace'; +import { buildDefaultPath, getWorkspace, updateWorkspace } from '@schematics/angular/utility/workspace'; import { Schema } from './schema'; -import { JsonArray } from '@angular-devkit/core'; +import { JsonArray, template } from '@angular-devkit/core'; +import { isAngularApplicationProject } from '../utils/utils'; export default function (options: Schema): Rule { return async (host: Tree, context: SchematicContext) => { const installTaskId = context.addTask(new NodePackageInstallTask()); return chain([ options && options.type && options.type === 'b2b' ? addExpertModule(options) : noop(), + options && options.starter ? addStarterApp(options) : noop(), addAposinTheme(options), addCdkStyles(options), addCdkA11yStyles(options), @@ -33,6 +35,50 @@ function addExpertModule(options: Schema) { }; } +function addStarterApp(options: Schema) { + return async (host: Tree, context: SchematicContext) => { + const workspace = await getWorkspace(host); + const project = getProjectFromWorkspace(workspace, options.project); + const projectRoot = project.sourceRoot || '.'; + const projectAppPath = buildDefaultPath(project); + + if (!isAngularApplicationProject(project)) { + throw new SchematicsException('Project is not an application or is using an unsupported builder'); + } + + const mainBuffer = host.read(`${projectRoot}/main.ts`); + if (!mainBuffer) { + throw new SchematicsException('Incompatible Starter project: cannot find main.ts'); + } + if (!mainBuffer.toString().includes('AppModule')) { + throw new SchematicsException('Incompatible Starter project: main.ts is not bootstrapping AppModule'); + } + + const indexBuffer = host.read(`${projectRoot}/index.html`); + if (!indexBuffer) { + throw new SchematicsException('Incompatible Starter project: cannot find index.html'); + } + if (!indexBuffer.toString().includes(' element'); + } + + const moduleBuffer = host.read(`${projectAppPath}/app.module.ts`); + if (!moduleBuffer) { + throw new SchematicsException(`Incompatible Starter project: ${projectAppPath}/app.module.ts does not exist`); + } + if (!moduleBuffer.toString().includes('AppComponent')) { + throw new SchematicsException(`Incompatible Starter project: ${projectAppPath}/app.module.ts does not import AppComponent`); + } + + return mergeWith( + apply(url('./files'), [ + move(projectAppPath) + ]), + MergeStrategy.Overwrite + ); + }; +} + function addAposinTheme(options: Schema) { return async (host: Tree) => { if (options.noTheme) { diff --git a/projects/ng-aquila/src/schematics/utils/utils.ts b/projects/ng-aquila/src/schematics/utils/utils.ts index dd10cebef..9643a4e3b 100644 --- a/projects/ng-aquila/src/schematics/utils/utils.ts +++ b/projects/ng-aquila/src/schematics/utils/utils.ts @@ -11,6 +11,7 @@ import * as ts from 'typescript'; import * as html from '@angular/compiler/src/ml_parser/ast'; import { getHtmlTagDefinition } from '@angular/compiler/src/ml_parser/html_tags'; import { JsonAstNode, JsonAstObject } from '@angular-devkit/core'; +import { ProjectDefinition } from '@angular-devkit/core/src/workspace'; class SerializerVisitor implements html.Visitor { visitElement(element: html.Element, _context: any): any { @@ -87,3 +88,16 @@ export function modifyProperty(node: ts.Node) { export function isJsonAstObject(node: JsonAstNode | null): node is JsonAstObject { return !!node && node.kind === 'object'; } + +export function isAngularApplicationProject(project: ProjectDefinition): boolean { + if (project.extensions.projectType !== 'application') { + return false; + } + + const builder = project.targets?.get('build')?.builder.toString(); + if (builder && (builder.includes('@angular-devkit/build-angular:browser') + || builder.includes('@angular-builders/custom-webpack:browser'))) { + return true; + } + return false; +} diff --git a/projects/ng-aquila/tsconfig.schematics.json b/projects/ng-aquila/tsconfig.schematics.json index e272438d1..60889fe2d 100644 --- a/projects/ng-aquila/tsconfig.schematics.json +++ b/projects/ng-aquila/tsconfig.schematics.json @@ -30,5 +30,7 @@ "include": [ "src/schematics/**/*" ], - "exclude": [] + "exclude": [ + "src/schematics/*/files/**/*" + ] } diff --git a/scripts/build-package.js b/scripts/build-package.js index c7347d462..6d277c0be 100644 --- a/scripts/build-package.js +++ b/scripts/build-package.js @@ -51,3 +51,4 @@ console.log("========================"); console.log(" Copying other assets"); cpx.copy('README.md', 'dist/ng-aquila'); cpx.copy('LICENSE', 'dist/ng-aquila'); +cpx.copy('./projects/ng-aquila/src/schematics/*/files/**', './dist/ng-aquila/schematics');