Skip to content

Commit c3405aa

Browse files
devversionandrewseguin
authored andcommitted
fix(toolbar): no longer auto-generate toolbar rows (#6661)
Currently the toolbar always generates the first `<md-toolbar-row>`. This means that developers have no opportunity to set directives/attributes/classes on the first toolbar row (e.g with flex-layout). With this change, the toolbar won't auto-generate any `<md-toolbar-row>` element. The toolbar will have two different row modes: _Single row toolbar_ ```html <md-toolbar> First Tow </md-toolbar> ``` _Multiple rows toolbar_ ```html <md-toolbar> <md-toolbar-row>First Row</md-toolbar-row> <md-toolbar-row>Second Row</md-toolbar-row> </md-toolbar> ``` This means that mixing those two row modes is no longer possible and allowed ```html <md-toolbar> <span>First Row</span> <md-toolbar-row>Second Row</md-toolbar-row> </md-toolbar> ``` BREAKING CHANGE: `<md-toolbar-row>` elements will be no longer auto-generated for the first row. Meaning that custom styling for the first `<md-toolbar-row>` is no longer working as before. Since no toolbar-row is auto-generated anymore, there are two different modes for the toolbar now. Either place content directly inside of the `<md-toolbar>` or place multiple `<md-toolbar-row>` elements. Fixes #6004. Fixes #1718.
1 parent b39202f commit c3405aa

File tree

8 files changed

+198
-83
lines changed

8 files changed

+198
-83
lines changed

src/demo-app/toolbar/toolbar-demo.html

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,27 +35,27 @@
3535

3636
<p>
3737
<mat-toolbar color="accent">
38-
<span>Custom Toolbar</span>
39-
<mat-toolbar-row>
40-
<span>Second Line</span>
41-
</mat-toolbar-row>
38+
<mat-toolbar-row>First Row</mat-toolbar-row>
39+
<mat-toolbar-row>Second Row</mat-toolbar-row>
4240
</mat-toolbar>
4341
</p>
4442

4543
<p>
4644
<mat-toolbar color="primary">
47-
<span>Custom Toolbar</span>
45+
<mat-toolbar-row>
46+
<span>First Row</span>
47+
</mat-toolbar-row>
4848

4949
<mat-toolbar-row>
50-
<span>Second Line</span>
50+
<span>Second Row</span>
5151

5252
<span class="demo-fill-remaining"></span>
5353

5454
<mat-icon class="demo-toolbar-icon">verified_user</mat-icon>
5555
</mat-toolbar-row>
5656

5757
<mat-toolbar-row>
58-
<span>Third Line</span>
58+
<span>Third Row</span>
5959

6060
<span class="demo-fill-remaining"></span>
6161

@@ -64,5 +64,4 @@
6464
</mat-toolbar-row>
6565
</mat-toolbar>
6666
</p>
67-
68-
</div>
67+
</div>

src/lib/toolbar/toolbar-module.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@
88

99
import {NgModule} from '@angular/core';
1010
import {MatCommonModule} from '@angular/material/core';
11+
import {PlatformModule} from '@angular/cdk/platform';
1112
import {MatToolbar, MatToolbarRow} from './toolbar';
1213

13-
1414
@NgModule({
15-
imports: [MatCommonModule],
15+
imports: [MatCommonModule, PlatformModule],
1616
exports: [MatToolbar, MatToolbarRow, MatCommonModule],
1717
declarations: [MatToolbar, MatToolbarRow],
1818
})

src/lib/toolbar/toolbar.html

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,2 @@
1-
<div class="mat-toolbar-layout">
2-
<mat-toolbar-row>
3-
<ng-content></ng-content>
4-
</mat-toolbar-row>
5-
<ng-content select="mat-toolbar-row"></ng-content>
6-
</div>
1+
<ng-content></ng-content>
2+
<ng-content select="mat-toolbar-row"></ng-content>

src/lib/toolbar/toolbar.md

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,38 @@
22

33
<!-- example(toolbar-overview) -->
44

5-
### Multiple rows
6-
Toolbars can have multiple rows using `<mat-toolbar-row>` elements. Any content outside of an
7-
`<mat-toolbar-row>` element are automatically placed inside of one at the beginning of the toolbar.
8-
Each toolbar row is a `display: flex` container.
5+
### Single row
6+
7+
In the most situations, a toolbar will be placed at the top of your application and will only
8+
have a single row that includes the title of your application.
99

1010
```html
1111
<mat-toolbar>
12-
<span>First Row</span>
13-
12+
<span>My Application</span>
13+
</mat-toolbar>
14+
```
15+
16+
### Multiple rows
17+
18+
The Material Design specifications describe that toolbars can also have multiple rows. Creating
19+
toolbars with multiple rows in Angular Material can be done by placing `<mat-toolbar-row>` elements
20+
inside of a `<mat-toolbar>`.
21+
22+
```html
23+
<mat-toolbar>
1424
<mat-toolbar-row>
15-
<span>Second Row</span>
25+
<span>First Row</span>
1626
</mat-toolbar-row>
1727

1828
<mat-toolbar-row>
19-
<span>Third Row</span>
29+
<span>Second Row</span>
2030
</mat-toolbar-row>
2131
</mat-toolbar>
2232
```
2333

34+
**Note**: Placing content outside of a `<mat-toolbar-row>` when multiple rows are specified is not
35+
supported.
36+
2437
### Positioning toolbar content
2538
The toolbar does not perform any positioning of its content. This gives the user full power to
2639
position the content as it suits their application.

src/lib/toolbar/toolbar.scss

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,39 +4,39 @@ $mat-toolbar-height-desktop: 64px !default;
44
$mat-toolbar-height-mobile-portrait: 56px !default;
55
$mat-toolbar-height-mobile-landscape: 48px !default;
66

7-
$mat-toolbar-padding: 16px !default;
7+
$mat-toolbar-row-padding: 16px !default;
88

99

1010
@mixin mat-toolbar-height($height) {
11-
.mat-toolbar {
11+
.mat-toolbar-multiple-rows {
1212
min-height: $height;
1313
}
14-
.mat-toolbar-row {
14+
.mat-toolbar-row, .mat-toolbar-single-row {
1515
height: $height;
1616
}
1717
}
1818

19-
.mat-toolbar {
19+
.mat-toolbar-row, .mat-toolbar-single-row {
2020
display: flex;
2121
box-sizing: border-box;
22-
width: 100%;
23-
padding: 0 $mat-toolbar-padding;
24-
flex-direction: column;
2522

26-
.mat-toolbar-row {
27-
display: flex;
28-
box-sizing: border-box;
23+
padding: 0 $mat-toolbar-row-padding;
24+
width: 100%;
2925

30-
width: 100%;
26+
// Flexbox Vertical Alignment
27+
flex-direction: row;
28+
align-items: center;
3129

32-
// Flexbox Vertical Alignment
33-
flex-direction: row;
34-
align-items: center;
30+
// Per Material specs a toolbar cannot have multiple lines inside of a single row.
31+
// Disable text wrapping inside of the toolbar. Developers are still able to overwrite it.
32+
white-space: nowrap;
33+
}
3534

36-
// Per Material specs a toolbar cannot have multiple lines inside of a single row.
37-
// Disable text wrapping inside of the toolbar. Developers are still able to overwrite it.
38-
white-space: nowrap;
39-
}
35+
.mat-toolbar-multiple-rows {
36+
display: flex;
37+
box-sizing: border-box;
38+
flex-direction: column;
39+
width: 100%;
4040
}
4141

4242
// Set the default height for the toolbar.

src/lib/toolbar/toolbar.spec.ts

Lines changed: 87 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,55 +3,115 @@ import {TestBed, async, ComponentFixture} from '@angular/core/testing';
33
import {By} from '@angular/platform-browser';
44
import {MatToolbarModule} from './index';
55

6-
76
describe('MatToolbar', () => {
87

9-
let fixture: ComponentFixture<TestApp>;
10-
let testComponent: TestApp;
11-
let toolbarElement: HTMLElement;
12-
138
beforeEach(async(() => {
149
TestBed.configureTestingModule({
1510
imports: [MatToolbarModule],
16-
declarations: [TestApp],
11+
declarations: [ToolbarSingleRow, ToolbarMultipleRows, ToolbarMixedRowModes],
1712
});
1813

1914
TestBed.compileComponents();
2015
}));
2116

22-
beforeEach(() => {
23-
fixture = TestBed.createComponent(TestApp);
24-
testComponent = fixture.debugElement.componentInstance;
25-
toolbarElement = fixture.debugElement.query(By.css('mat-toolbar')).nativeElement;
26-
});
17+
describe('with single row', () => {
18+
let fixture: ComponentFixture<ToolbarSingleRow>;
19+
let testComponent: ToolbarSingleRow;
20+
let toolbarElement: HTMLElement;
21+
22+
beforeEach(() => {
23+
fixture = TestBed.createComponent(ToolbarSingleRow);
24+
testComponent = fixture.debugElement.componentInstance;
25+
toolbarElement = fixture.debugElement.query(By.css('.mat-toolbar')).nativeElement;
26+
});
2727

28-
it('should apply class based on color attribute', () => {
29-
testComponent.toolbarColor = 'primary';
30-
fixture.detectChanges();
28+
it('should apply class based on color attribute', () => {
29+
testComponent.toolbarColor = 'primary';
30+
fixture.detectChanges();
3131

32-
expect(toolbarElement.classList.contains('mat-primary')).toBe(true);
32+
expect(toolbarElement.classList.contains('mat-primary')).toBe(true);
3333

34-
testComponent.toolbarColor = 'accent';
35-
fixture.detectChanges();
34+
testComponent.toolbarColor = 'accent';
35+
fixture.detectChanges();
3636

37-
expect(toolbarElement.classList.contains('mat-primary')).toBe(false);
38-
expect(toolbarElement.classList.contains('mat-accent')).toBe(true);
37+
expect(toolbarElement.classList.contains('mat-primary')).toBe(false);
38+
expect(toolbarElement.classList.contains('mat-accent')).toBe(true);
3939

40-
testComponent.toolbarColor = 'warn';
41-
fixture.detectChanges();
40+
testComponent.toolbarColor = 'warn';
41+
fixture.detectChanges();
4242

43-
expect(toolbarElement.classList.contains('mat-accent')).toBe(false);
44-
expect(toolbarElement.classList.contains('mat-warn')).toBe(true);
43+
expect(toolbarElement.classList.contains('mat-accent')).toBe(false);
44+
expect(toolbarElement.classList.contains('mat-warn')).toBe(true);
45+
});
46+
47+
it('should not wrap the first row contents inside of a generated element', () => {
48+
expect(toolbarElement.firstElementChild!.tagName).toBe('SPAN',
49+
'Expected the <span> element of the first row to be a direct child of the toolbar');
50+
});
4551
});
4652

47-
it('should set the toolbar role on the host', () => {
48-
expect(toolbarElement.getAttribute('role')).toBe('toolbar');
53+
describe('with multiple rows', () => {
54+
55+
it('should project each toolbar-row element inside of the toolbar', () => {
56+
const fixture = TestBed.createComponent(ToolbarMultipleRows);
57+
fixture.detectChanges();
58+
59+
expect(fixture.debugElement.queryAll(By.css('.mat-toolbar > .mat-toolbar-row')).length)
60+
.toBe(2, 'Expected one toolbar row to be present while no content is projected.');
61+
});
62+
63+
it('should throw an error if different toolbar modes are mixed', () => {
64+
expect(() => {
65+
const fixture = TestBed.createComponent(ToolbarMixedRowModes);
66+
fixture.detectChanges();
67+
}).toThrowError(/attempting to combine different/i);
68+
});
69+
70+
it('should throw an error if a toolbar-row is added later', () => {
71+
const fixture = TestBed.createComponent(ToolbarMixedRowModes);
72+
73+
fixture.componentInstance.showToolbarRow = false;
74+
fixture.detectChanges();
75+
76+
expect(() => {
77+
fixture.componentInstance.showToolbarRow = true;
78+
fixture.detectChanges();
79+
}).toThrowError(/attempting to combine different/i);
80+
});
4981
});
5082

5183
});
5284

5385

54-
@Component({template: `<mat-toolbar [color]="toolbarColor">Test Toolbar</mat-toolbar>`})
55-
class TestApp {
86+
@Component({
87+
template: `
88+
<mat-toolbar [color]="toolbarColor">
89+
<span>First Row</span>
90+
</mat-toolbar>
91+
`
92+
})
93+
class ToolbarSingleRow {
5694
toolbarColor: string;
5795
}
96+
97+
@Component({
98+
template: `
99+
<mat-toolbar>
100+
<mat-toolbar-row>First Row</mat-toolbar-row>
101+
<mat-toolbar-row>Second Row</mat-toolbar-row>
102+
</mat-toolbar>
103+
`
104+
})
105+
class ToolbarMultipleRows {}
106+
107+
@Component({
108+
template: `
109+
<mat-toolbar>
110+
First Row
111+
<mat-toolbar-row *ngIf="showToolbarRow">Second Row</mat-toolbar-row>
112+
</mat-toolbar>
113+
`
114+
})
115+
class ToolbarMixedRowModes {
116+
showToolbarRow: boolean = true;
117+
}

0 commit comments

Comments
 (0)