diff --git a/schematics/collection.json b/schematics/collection.json index 6cd12e815f24..19c7407b8916 100644 --- a/schematics/collection.json +++ b/schematics/collection.json @@ -2,6 +2,13 @@ { "$schema": "./node_modules/@angular-devkit/schematics/collection-schema.json", "schematics": { + // Create a dashboard component + "materialDashboard": { + "description": "Create a dashboard component", + "factory": "./dashboard/index", + "schema": "./dashboard/schema.json", + "aliases": [ "material-dashboard" ] + }, // Creates a table component "materialTable": { "description": "Create a table component", diff --git a/schematics/dashboard/files/__path__/__name@dasherize@if-flat__/__name@dasherize__.component.__styleext__ b/schematics/dashboard/files/__path__/__name@dasherize@if-flat__/__name@dasherize__.component.__styleext__ new file mode 100644 index 000000000000..11bc8f15db08 --- /dev/null +++ b/schematics/dashboard/files/__path__/__name@dasherize@if-flat__/__name@dasherize__.component.__styleext__ @@ -0,0 +1,21 @@ +.grid-container { + margin: 20px; +} + +.dashboard-card { + position: absolute; + top: 15px; + left: 15px; + right: 15px; + bottom: 15px; +} + +.more-button { + position: absolute; + top: 5px; + right: 10px; +} + +.dashboard-card-content { + text-align: center; +} \ No newline at end of file diff --git a/schematics/dashboard/files/__path__/__name@dasherize@if-flat__/__name@dasherize__.component.html b/schematics/dashboard/files/__path__/__name@dasherize@if-flat__/__name@dasherize__.component.html new file mode 100644 index 000000000000..159daf97b770 --- /dev/null +++ b/schematics/dashboard/files/__path__/__name@dasherize@if-flat__/__name@dasherize__.component.html @@ -0,0 +1,24 @@ +
+

Dashboard

+ + + + + + {{card.title}} + + + + + + + + +
Card Content Here
+
+
+
+
+
\ No newline at end of file diff --git a/schematics/dashboard/files/__path__/__name@dasherize@if-flat__/__name@dasherize__.component.spec.ts b/schematics/dashboard/files/__path__/__name@dasherize@if-flat__/__name@dasherize__.component.spec.ts new file mode 100644 index 000000000000..ad38814c940d --- /dev/null +++ b/schematics/dashboard/files/__path__/__name@dasherize@if-flat__/__name@dasherize__.component.spec.ts @@ -0,0 +1,24 @@ + +import { fakeAsync, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { <%= classify(name) %>Component } from './<%= dasherize(name) %>.component'; + +describe('<%= classify(name) %>Component', () => { + let component: <%= classify(name) %>Component; + let fixture: ComponentFixture<<%= classify(name) %>Component>; + + beforeEach(fakeAsync(() => { + TestBed.configureTestingModule({ + declarations: [ <%= classify(name) %>Component ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(<%= classify(name) %>Component); + component = fixture.componentInstance; + fixture.detectChanges(); + })); + + it('should compile', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/schematics/dashboard/files/__path__/__name@dasherize@if-flat__/__name@dasherize__.component.ts b/schematics/dashboard/files/__path__/__name@dasherize@if-flat__/__name@dasherize__.component.ts new file mode 100644 index 000000000000..eca0f8b33a8e --- /dev/null +++ b/schematics/dashboard/files/__path__/__name@dasherize@if-flat__/__name@dasherize__.component.ts @@ -0,0 +1,68 @@ +import { Component, <% if(!!viewEncapsulation) { %>, ViewEncapsulation<% }%><% if(changeDetection !== 'Default') { %>, ChangeDetectionStrategy<% }%> } from '@angular/core'; + +@Component({ + selector: '<%= selector %>',<% if(inlineTemplate) { %> + template: ` +
+

Dashboard

+ + + + + + {{card.title}} + + + + + + + + +
Card Content Here
+
+
+
+
+
+ `,<% } else { %> + templateUrl: './<%= dasherize(name) %>.component.html',<% } if(inlineStyle) { %> + styles: [ + ` + .grid-container { + margin: 20px; + } + + .dashboard-card { + position: absolute; + top: 15px; + left: 15px; + right: 15px; + bottom: 15px; + } + + .more-button { + position: absolute; + top: 5px; + right: 10px; + } + + .dashboard-card-content { + text-align: center; + } + ` + ]<% } else { %> + styleUrls: ['./<%= dasherize(name) %>.component.<%= styleext %>']<% } %><% if(!!viewEncapsulation) { %>, + encapsulation: ViewEncapsulation.<%= viewEncapsulation %><% } if (changeDetection !== 'Default') { %>, + changeDetection: ChangeDetectionStrategy.<%= changeDetection %><% } %> +}) +export class <%= classify(name) %>Component { + cards = [ + { title: 'Card 1', cols: 2, rows: 1 }, + { title: 'Card 2', cols: 1, rows: 1 }, + { title: 'Card 3', cols: 1, rows: 2 }, + { title: 'Card 4', cols: 1, rows: 1 } + ]; +} diff --git a/schematics/dashboard/index.ts b/schematics/dashboard/index.ts new file mode 100644 index 000000000000..07d4f0003087 --- /dev/null +++ b/schematics/dashboard/index.ts @@ -0,0 +1,31 @@ +import {chain, Rule, noop, Tree, SchematicContext} from '@angular-devkit/schematics'; +import {Schema} from './schema'; +import {addModuleImportToModule} from '../utils/ast'; +import {findModuleFromOptions} from '../utils/devkit-utils/find-module'; +import {buildComponent} from '../utils/devkit-utils/component'; + +/** + * Scaffolds a new navigation component. + * Internally it bootstraps the base component schematic + */ +export default function(options: Schema): Rule { + return chain([ + buildComponent({ ...options }), + options.skipImport ? noop() : addNavModulesToModule(options) + ]); +} + +/** + * Adds the required modules to the relative module. + */ +function addNavModulesToModule(options: Schema) { + return (host: Tree) => { + const modulePath = findModuleFromOptions(host, options); + addModuleImportToModule(host, modulePath, 'MatGridListModule', '@angular/material'); + addModuleImportToModule(host, modulePath, 'MatCardModule', '@angular/material'); + addModuleImportToModule(host, modulePath, 'MatMenuModule', '@angular/material'); + addModuleImportToModule(host, modulePath, 'MatIconModule', '@angular/material'); + addModuleImportToModule(host, modulePath, 'MatButtonModule', '@angular/material'); + return host; + }; +} diff --git a/schematics/dashboard/index_spec.ts b/schematics/dashboard/index_spec.ts new file mode 100644 index 000000000000..218d0ee9ed5c --- /dev/null +++ b/schematics/dashboard/index_spec.ts @@ -0,0 +1,59 @@ +import {SchematicTestRunner} from '@angular-devkit/schematics/testing'; +import {join} from 'path'; +import {Tree} from '@angular-devkit/schematics'; +import {createTestApp} from '../utils/testing'; +import {getFileContent} from '@schematics/angular/utility/test'; + +const collectionPath = join(__dirname, '../collection.json'); + +describe('material-dashboard-schematic', () => { + let runner: SchematicTestRunner; + const options = { + name: 'foo', + path: 'app', + sourceDir: 'src', + inlineStyle: false, + inlineTemplate: false, + changeDetection: 'Default', + styleext: 'css', + spec: true, + module: undefined, + export: false, + prefix: undefined, + viewEncapsulation: undefined, + }; + + beforeEach(() => { + runner = new SchematicTestRunner('schematics', collectionPath); + }); + + it('should create dashboard files and add them to module', () => { + const tree = runner.runSchematic('materialDashboard', { ...options }, createTestApp()); + const files = tree.files; + + expect(files).toContain('/src/app/foo/foo.component.css'); + expect(files).toContain('/src/app/foo/foo.component.html'); + expect(files).toContain('/src/app/foo/foo.component.spec.ts'); + expect(files).toContain('/src/app/foo/foo.component.ts'); + + const moduleContent = getFileContent(tree, '/src/app/app.module.ts'); + expect(moduleContent).toMatch(/import.*Foo.*from '.\/foo\/foo.component'/); + expect(moduleContent).toMatch(/declarations:\s*\[[^\]]+?,\r?\n\s+FooComponent\r?\n/m); + }); + + it('should add dashboard imports to module', () => { + const tree = runner.runSchematic('materialDashboard', { ...options }, createTestApp()); + const moduleContent = getFileContent(tree, '/src/app/app.module.ts'); + + expect(moduleContent).toContain('MatGridListModule'); + expect(moduleContent).toContain('MatCardModule'); + expect(moduleContent).toContain('MatMenuModule'); + expect(moduleContent).toContain('MatIconModule'); + expect(moduleContent).toContain('MatButtonModule'); + + expect(moduleContent).toContain( + // tslint:disable-next-line + `import { MatGridListModule, MatCardModule, MatMenuModule, MatIconModule, MatButtonModule } from '@angular/material';`); + }); + +}); diff --git a/schematics/dashboard/schema.d.ts b/schematics/dashboard/schema.d.ts new file mode 100644 index 000000000000..49a799dd1532 --- /dev/null +++ b/schematics/dashboard/schema.d.ts @@ -0,0 +1,3 @@ +import {Schema as ComponentSchema} from '@schematics/angular/component/schema'; + +export interface Schema extends ComponentSchema {} diff --git a/schematics/dashboard/schema.json b/schematics/dashboard/schema.json new file mode 100644 index 000000000000..1cc9d76a914b --- /dev/null +++ b/schematics/dashboard/schema.json @@ -0,0 +1,99 @@ +{ + "$schema": "http://json-schema.org/schema", + "id": "SchematicsMaterialDashboard", + "title": "Material Dashboard Options Schema", + "type": "object", + "properties": { + "path": { + "type": "string", + "description": "The path to create the component.", + "default": "app", + "visible": false + }, + "sourceDir": { + "type": "string", + "description": "The path of the source directory.", + "default": "src", + "alias": "sd", + "visible": false + }, + "appRoot": { + "type": "string", + "description": "The root of the application.", + "visible": false + }, + "name": { + "type": "string", + "description": "The name of the component." + }, + "inlineStyle": { + "description": "Specifies if the style will be in the ts file.", + "type": "boolean", + "default": false, + "alias": "is" + }, + "inlineTemplate": { + "description": "Specifies if the template will be in the ts file.", + "type": "boolean", + "default": false, + "alias": "it" + }, + "viewEncapsulation": { + "description": "Specifies the view encapsulation strategy.", + "enum": ["Emulated", "Native", "None"], + "type": "string", + "default": "Emulated", + "alias": "ve" + }, + "changeDetection": { + "description": "Specifies the change detection strategy.", + "enum": ["Default", "OnPush"], + "type": "string", + "default": "Default", + "alias": "cd" + }, + "prefix": { + "type": "string", + "description": "The prefix to apply to generated selectors.", + "default": "app", + "alias": "p" + }, + "styleext": { + "description": "The file extension to be used for style files.", + "type": "string", + "default": "css" + }, + "spec": { + "type": "boolean", + "description": "Specifies if a spec file is generated.", + "default": true + }, + "flat": { + "type": "boolean", + "description": "Flag to indicate if a dir is created.", + "default": false + }, + "skipImport": { + "type": "boolean", + "description": "Flag to skip the module import.", + "default": false + }, + "selector": { + "type": "string", + "description": "The selector to use for the component." + }, + "module": { + "type": "string", + "description": "Allows specification of the declaring module.", + "alias": "m" + }, + "export": { + "type": "boolean", + "default": false, + "description": "Specifies if declaring module exports the component." + } + }, + "required": [ + "name" + ] +} diff --git a/schematics/tsconfig.json b/schematics/tsconfig.json index 21bb42adccf6..9787dc54f70a 100644 --- a/schematics/tsconfig.json +++ b/schematics/tsconfig.json @@ -1,6 +1,5 @@ { "compilerOptions": { - "baseUrl": "tsconfig", "lib": [ "es2017", "dom" @@ -8,7 +7,6 @@ "module": "commonjs", "moduleResolution": "node", "noEmitOnError": false, - "rootDir": "src/", "skipDefaultLibCheck": true, "skipLibCheck": true, "sourceMap": true,