-
Notifications
You must be signed in to change notification settings - Fork 6.8k
feat(schematics): dashboard schematic #10011
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
bac514c
b3b3af2
b7da5c4
54e067b
a46d848
98c1fe5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
<div class="grid-container"> | ||
<h1 class="mat-h1">Dashboard</h1> | ||
<mat-grid-list cols="2" rowHeight="350px"> | ||
<mat-grid-tile *ngFor="let card of cards" [colspan]="card.cols" [rowspan]="card.rows"> | ||
<mat-card class="dashboard-card"> | ||
<mat-card-header> | ||
<mat-card-title> | ||
{{card.title}} | ||
<button mat-icon-button class="more-button" [matMenuTriggerFor]="menu" aria-label="Toggle menu"> | ||
<mat-icon>more_vert</mat-icon> | ||
</button> | ||
<mat-menu #menu="matMenu" xPosition="before"> | ||
<button mat-menu-item>Expand</button> | ||
<button mat-menu-item>Remove</button> | ||
</mat-menu> | ||
</mat-card-title> | ||
</mat-card-header> | ||
<mat-card-content class="dashboard-card-content"> | ||
<div>Card Content Here</div> | ||
</mat-card-content> | ||
</mat-card> | ||
</mat-grid-tile> | ||
</mat-grid-list> | ||
</div> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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(); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
import { Component, <% if(!!viewEncapsulation) { %>, ViewEncapsulation<% }%><% if(changeDetection !== 'Default') { %>, ChangeDetectionStrategy<% }%> } from '@angular/core'; | ||
|
||
@Component({ | ||
selector: '<%= selector %>',<% if(inlineTemplate) { %> | ||
template: ` | ||
<div class="grid-container"> | ||
<h1 class="mat-h1">Dashboard</h1> | ||
<mat-grid-list cols="2" rowHeight="350px"> | ||
<mat-grid-tile *ngFor="let card of cards" [colspan]="card.cols" [rowspan]="card.rows"> | ||
<mat-card class="dashboard-card"> | ||
<mat-card-header> | ||
<mat-card-title> | ||
{{card.title}} | ||
<button mat-icon-button class="more-button" [matMenuTriggerFor]="menu" aria-label="Toggle menu"> | ||
<mat-icon>more_vert</mat-icon> | ||
</button> | ||
<mat-menu #menu="matMenu" xPosition="before"> | ||
<button mat-menu-item>Expand</button> | ||
<button mat-menu-item>Remove</button> | ||
</mat-menu> | ||
</mat-card-title> | ||
</mat-card-header> | ||
<mat-card-content class="dashboard-card-content"> | ||
<div>Card Content Here</div> | ||
</mat-card-content> | ||
</mat-card> | ||
</mat-grid-tile> | ||
</mat-grid-list> | ||
</div> | ||
`,<% } 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 = [ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Condense this? 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}
]; |
||
{ 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 } | ||
]; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
}; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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';`); | ||
}); | ||
|
||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import {Schema as ComponentSchema} from '@schematics/angular/component/schema'; | ||
|
||
export interface Schema extends ComponentSchema {} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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.", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unclear what There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This code was copied from the base component schematic. |
||
"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" | ||
] | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Grid-list is only useful when you want items with heterogenous rowspans. We could either mix that in, or just use cards laid out with flex styles.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll update it to have spans :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are the rowspans meaningful here? A screenshot would be helpful
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ya - https://www.screencast.com/t/op5NHbKC