-
Notifications
You must be signed in to change notification settings - Fork 6.8k
feat(schematics): navigation schematic #10009
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
Conversation
|
||
mat-drawer { | ||
width: 200px; | ||
box-shadow: 3px 0 6px rgba(0,0,0,.24); |
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.
Should this be done with one of the elevation classes instead?
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 thought about using the elevation helper but that would only work if you are using SCSS. We have to assume CSS/inline css/etc.
@@ -0,0 +1,18 @@ | |||
<mat-drawer-container> | |||
<mat-drawer mode="side" #drawer [opened]="!isHandset"> |
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 believe this should be a mat-sidenav
since it's at the top level. @mmalerba will know best
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.
yep, if you make it a mat-sidenav
you can also use the fixedInViewport
option to make the sidenav fixed position on mobile and allow the address bar to scroll away. This is something I think we should include by default for users, or at least give an option to easily enable it, because it can be a little tricky to set up on your own.
@@ -0,0 +1,18 @@ | |||
<mat-drawer-container> | |||
<mat-drawer mode="side" #drawer [opened]="!isHandset"> |
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.
Should the mode
change based on the screen size? I.e., when it gets large enough it goes to over
.
<mat-drawer-content> | ||
<mat-toolbar color="primary"> | ||
<button type="button" mat-icon-button (click)="drawer.toggle()" *ngIf="isHandset"> | ||
<mat-icon aria-label="Side nav toggle icon">menu</mat-icon> |
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.
The aria-label
should go on the button instead of the icon and should describe the button's action rather than describe the icon.
@@ -0,0 +1,18 @@ | |||
<mat-drawer-container> | |||
<mat-drawer mode="side" #drawer [opened]="!isHandset"> | |||
<mat-toolbar color="primary">Menu</mat-toolbar> |
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.
Does the sidenav need a toolbar / header? Not sure if I typically see this.
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 have seen this on Google products before, they will often put the product logo in a toolbar in the sidenav so it lines ups with the main toolbar when the sidenav opens. Google photos and play music are currently doing this, but it seems like most products are switching to the inbox style where the sidenav just opens below the toolbar
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.
@jelbourn - Let me know what you'd like me to do here.
|
||
it('should create', () => { | ||
expect(component).toBeTruthy(); | ||
}); |
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.
It would be good to flesh out a couple of basic tests here (e.g. sidenav toggling)
schematics/nav/index_spec.ts
Outdated
const tree = runner.runSchematic('materialNav', { ...options }, createTestApp()); | ||
const files = tree.files; | ||
|
||
expect(files.indexOf('/src/app/foo/foo.component.css')).toBeGreaterThanOrEqual(0); |
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.
Should be able to use toContain
here
export class <%= classify(name) %>Component { | ||
isHandset: boolean; | ||
constructor(breakpointObserver: BreakpointObserver) { | ||
this.isHandset = breakpointObserver.isMatched(Breakpoints.Handset); |
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.
Instead of just setting this once we should subscribe to the observer to keep it up-to-date.
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.
This is a good case for the async
pipe. See how it was done for the tooltip: https://github.com/angular/material2/blob/master/src/lib/tooltip/tooltip.html#L3
.compileComponents(); | ||
})); | ||
|
||
beforeEach(() => { |
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.
The two beforeEach
calls can be combined.
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.
This code was copied from the base create component schematic.
it('should create', () => { | ||
expect(component).toBeTruthy(); | ||
}); | ||
}); |
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.
Missing a newline at the end of the file.
fixture.detectChanges(); | ||
}); | ||
|
||
it('should create', () => { |
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.
should create -> should compile
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.
This code was copied from the base create component schematic.
let component: <%= classify(name) %>Component; | ||
let fixture: ComponentFixture<<%= classify(name) %>Component>; | ||
|
||
beforeEach(async(() => { |
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.
Use fakeAsync
instead of async
.
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.
This code was copied from the base create component schematic.
<mat-drawer-content> | ||
<mat-toolbar color="primary"> | ||
<button type="button" mat-icon-button (click)="drawer.toggle()" *ngIf="isHandset"> | ||
<mat-icon aria-label="Side nav toggle icon">menu</mat-icon> |
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.
aria-label
should be on the button.
export class <%= classify(name) %>Component { | ||
isHandset: boolean; | ||
constructor(breakpointObserver: BreakpointObserver) { | ||
this.isHandset = breakpointObserver.isMatched(Breakpoints.Handset); |
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.
This is a good case for the async
pipe. See how it was done for the tooltip: https://github.com/angular/material2/blob/master/src/lib/tooltip/tooltip.html#L3
}, | ||
"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 comment
The reason will be displayed to describe this comment to others. Learn more.
Unclear what "dir" it's referring to.
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.
This code was copied from the base create component schematic.
# Conflicts: # schematics/collection.json # schematics/tsconfig.json
@jelbourn - Addressed feedback. |
import { Observable } from 'rxjs/Observable'; | ||
|
||
@Component({ | ||
selector: '<%= selector %>',<% if(inlineTemplate) { %> |
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.
So I guess schematics doesn't have a way to insert contents of another file here? Do they have a way to add comments that will be stripped out of the generated file? If so it would be good to add some comments for people maintaining this file that they need to update in 2 places
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, its very painful. I'm thinking about making a util to handle this automatically.
} | ||
` | ||
]<% } else { %> | ||
styleUrls: ['./<%= dasherize(name) %>.component.<%= styleext %>']<% } %><% if(!!viewEncapsulation) { %>, |
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.
should this be if(viewEncapsulation !== 'Emulated')
?
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.
This code was copied from the base create component schematic.
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.
Can we update the base schematic?
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.
@hansl ^^
"viewEncapsulation": { | ||
"description": "Specifies the view encapsulation strategy.", | ||
"enum": ["Emulated", "Native", "None"], | ||
"type": "string", |
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.
"default": "Emulated"
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.
This code was copied from the base create component schematic.
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.
Not really good enough reason on its own; Hans has mentioned that those base schematics are not meant to be normative.
mat-sidenav { | ||
width: 200px; | ||
box-shadow: 3px 0 6px rgba(0,0,0,.24); | ||
} |
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.
Same comment about the styles as in #10011
</mat-sidenav> | ||
<mat-sidenav-content> | ||
<mat-toolbar color="primary"> | ||
<button type="button" mat-icon-button (click)="drawer.toggle()" *ngIf="(_isHandset | async)!.matches"> |
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.
Button needs an aria-label
</mat-sidenav> | ||
<mat-sidenav-content> | ||
<mat-toolbar color="primary"> | ||
<button type="button" mat-icon-button (click)="drawer.toggle()" *ngIf="(_isHandset | async)!.matches"> |
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.
We need to make sure the a11y here is sensible. We may need to add a focus-trap to the sidenav (which in turn might need role="dialog"
added to the sidenav element along with everything that goes along with that role)
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.
Would you expect this role to only be present in mobile mode where its sliding out?
}) | ||
export class <%= classify(name) %>Component { | ||
_isHandset: Observable<BreakpointState> = this._breakpointObserver.observe(Breakpoints.Handset); | ||
constructor(private _breakpointObserver: BreakpointObserver) {} |
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.
Avoid prefixing with an underscore (which is a special thing we do in Angular Material that we don't recommend for most folks).
changeDetection: ChangeDetectionStrategy.<%= changeDetection %><% } %> | ||
}) | ||
export class <%= classify(name) %>Component { | ||
_isHandset: Observable<BreakpointState> = this._breakpointObserver.observe(Breakpoints.Handset); |
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.
Update this as the viewport changes?
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.
Its handled here: [opened]="!(isHandset | async)!.matches"
@jelbourn - Ready for another review. |
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.
LGTM
This issue has been automatically locked due to inactivity. Read more about our automatic conversation locking policy. This action has been performed automatically by a bot. |
This PR creates a navigation/toolbar schematic.
Large Screen

Small Screen
