Skip to content

Commit 0ef3eff

Browse files
crisbetojosephperrott
authored andcommitted
fix(dialog): improved handling of scrollable content
* Improves the handling of scrollable content inside the `mat-dialog-content` by using flexbox, rather than a hardcoded `max-height`, to define the content height. This resolves various issues where the dialog would go out of the screen at certain screen sizes or have multiple scrollbars. * Uses flexbox to ensure that the dialog content elements are always at the appropriate size. Fixes #2481. Fixes #3239. Fixes #6584. Fixes #8493.
1 parent 9062640 commit 0ef3eff

File tree

7 files changed

+131
-26
lines changed

7 files changed

+131
-26
lines changed

src/demo-app/dialog/dialog-demo.html

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,13 @@ <h1>Dialog demo</h1>
66
<button mat-raised-button color="accent" (click)="openContentElement()">
77
Open dialog with content elements
88
</button>
9-
<button mat-raised-button color="accent" (click)="openTemplate()">
9+
<button mat-raised-button color="accent" (click)="openTemplate(templateDialog)">
1010
Open dialog with template content
1111
</button>
12+
<button mat-raised-button color="accent" (click)="openTemplate(templateDialogWithContent)">
13+
Open template dialog with content elements
14+
</button>
15+
1216

1317
<mat-card class="demo-dialog-card">
1418
<mat-card-content>
@@ -111,7 +115,7 @@ <h2>Other options</h2>
111115
<p>Last afterClosed result: {{lastAfterClosedResult}}</p>
112116
<p>Last beforeClose result: {{lastBeforeCloseResult}}</p>
113117

114-
<ng-template let-data let-dialogRef="dialogRef">
118+
<ng-template #templateDialog let-data let-dialogRef="dialogRef">
115119
I'm a template dialog. I've been opened {{numTemplateOpens}} times!
116120

117121
<p>It's Jazz!</p>
@@ -123,5 +127,33 @@ <h2>Other options</h2>
123127

124128
<p> {{ data.message }} </p>
125129
<button type="button" (click)="dialogRef.close(lastCloseResult = howMuch.value)">Close dialog</button>
126-
<button (click)="dialogRef.updateSize('500px', '500px').updatePosition({ top: '25px', left: '25px' });">Change dimensions</button>`
130+
<button (click)="dialogRef.updateSize('500px', '500px').updatePosition({ top: '25px', left: '25px' });">Change dimensions</button>
131+
</ng-template>
132+
133+
<ng-template #templateDialogWithContent let-data let-dialogRef="dialogRef">
134+
<h2 mat-dialog-title>Saturn</h2>
135+
136+
<mat-dialog-content>
137+
<img style="max-width: 100%;" src="https://upload.wikimedia.org/wikipedia/commons/thumb/c/c7/Saturn_during_Equinox.jpg/1920px-Saturn_during_Equinox.jpg" alt="Saturn">
138+
Saturn is the sixth planet from the Sun and the second-largest in the Solar System, after
139+
Jupiter. It is a gas giant with an average radius about nine times that of Earth. It has
140+
only one-eighth the average density of Earth, but with its larger volume Saturn is over
141+
95 times more massive. Saturn is named after the Roman god of agriculture; its astronomical
142+
symbol (♄) represents the god's sickle.
143+
144+
Saturn's interior is probably composed of a core of iron–nickel and rock
145+
(silicon and oxygen compounds). This core is surrounded by a deep layer of metallic hydrogen,
146+
an intermediate layer of liquid hydrogen and liquid helium, and finally a gaseous outer layer.
147+
Saturn has a pale yellow hue due to ammonia crystals in its upper atmosphere. Electrical
148+
current within the metallic hydrogen layer is thought to give rise to Saturn's planetary
149+
magnetic field, which is weaker than Earth's, but has a magnetic moment 580 times that of
150+
Earth due to Saturn's larger size. Saturn's magnetic field strength is around one-twentieth
151+
of Jupiter's. The outer atmosphere is generally bland and lacking in contrast, although
152+
long-lived features can appear. Wind speeds on Saturn can reach 1,800 km/h (1,100 mph),
153+
higher than on Jupiter, but not as high as those on Neptune.
154+
</mat-dialog-content>
155+
156+
<mat-dialog-actions>
157+
<button mat-raised-button color="primary" (click)="dialogRef.close()">Close dialog</button>
158+
</mat-dialog-actions>
127159
</ng-template>

src/demo-app/dialog/dialog-demo.ts

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*/
88

99
import {DOCUMENT} from '@angular/common';
10-
import {Component, Inject, TemplateRef, ViewChild} from '@angular/core';
10+
import {Component, Inject, TemplateRef} from '@angular/core';
1111
import {MAT_DIALOG_DATA, MatDialog, MatDialogConfig, MatDialogRef} from '@angular/material';
1212

1313

@@ -29,12 +29,12 @@ export class DialogDemo {
2929
panelClass: 'custom-overlay-pane-class',
3030
hasBackdrop: true,
3131
backdropClass: '',
32-
width: '',
33-
height: '',
34-
minWidth: '',
35-
minHeight: '',
32+
width: defaultDialogConfig.width,
33+
height: defaultDialogConfig.height,
34+
minWidth: defaultDialogConfig.minWidth,
35+
minHeight: defaultDialogConfig.minHeight,
3636
maxWidth: defaultDialogConfig.maxWidth,
37-
maxHeight: '',
37+
maxHeight: defaultDialogConfig.maxHeight,
3838
position: {
3939
top: '',
4040
bottom: '',
@@ -47,8 +47,6 @@ export class DialogDemo {
4747
};
4848
numTemplateOpens = 0;
4949

50-
@ViewChild(TemplateRef) template: TemplateRef<any>;
51-
5250
constructor(public dialog: MatDialog, @Inject(DOCUMENT) doc: any) {
5351
// Possible useful example for the open and closeAll events.
5452
// Adding a class to the body if a dialog opens and
@@ -80,9 +78,9 @@ export class DialogDemo {
8078
dialogRef.componentInstance.actionsAlignment = this.actionsAlignment;
8179
}
8280

83-
openTemplate() {
81+
openTemplate(template: TemplateRef<any>) {
8482
this.numTemplateOpens++;
85-
this.dialog.open(this.template, this.config);
83+
this.dialog.open(template, this.config);
8684
}
8785
}
8886

src/lib/dialog/dialog-config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ export class MatDialogConfig<D = any> {
7575
maxWidth?: number | string = '80vw';
7676

7777
/** Max-height of the dialog. If a number is provided, pixel units are assumed. */
78-
maxHeight?: number | string;
78+
maxHeight?: number | string = '80vh';
7979

8080
/** Position overrides. */
8181
position?: DialogPosition;

src/lib/dialog/dialog-container.html

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1-
<ng-template cdkPortalOutlet></ng-template>
1+
<div class="mat-dialog-component-host">
2+
<ng-template cdkPortalOutlet></ng-template>
3+
</div>

src/lib/dialog/dialog-container.ts

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,15 @@ export class MatDialogContainer extends BasePortalOutlet {
112112
}
113113

114114
this._savePreviouslyFocusedElement();
115-
return this._portalOutlet.attachComponentPortal(portal);
115+
116+
const componentRef = this._portalOutlet.attachComponentPortal(portal);
117+
118+
// We need to add an extra class to the root of the instantiated component, which
119+
// allows us to propagate some width/height overrides down from the overlay pane.
120+
componentRef.location.nativeElement.classList.add('mat-dialog-component-host');
121+
this._toggleScrollableContentClass();
122+
123+
return componentRef;
116124
}
117125

118126
/**
@@ -125,7 +133,9 @@ export class MatDialogContainer extends BasePortalOutlet {
125133
}
126134

127135
this._savePreviouslyFocusedElement();
128-
return this._portalOutlet.attachTemplatePortal(portal);
136+
const viewRef = this._portalOutlet.attachTemplatePortal(portal);
137+
this._toggleScrollableContentClass();
138+
return viewRef;
129139
}
130140

131141
/** Moves the focus inside the focus trap. */
@@ -195,4 +205,19 @@ export class MatDialogContainer extends BasePortalOutlet {
195205
// view container is using OnPush change detection.
196206
this._changeDetectorRef.markForCheck();
197207
}
208+
209+
/**
210+
* Toggles a class on the host element, depending on whether it has
211+
* scrollable content. Used to activate particular flexbox styling.
212+
*/
213+
private _toggleScrollableContentClass() {
214+
const element: HTMLElement = this._elementRef.nativeElement;
215+
const cssClass = 'mat-dialog-container-scrollable';
216+
217+
if (element.querySelector('.mat-dialog-content')) {
218+
element.classList.add(cssClass);
219+
} else {
220+
element.classList.remove(cssClass);
221+
}
222+
}
198223
}

src/lib/dialog/dialog.scss

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@
44

55

66
$mat-dialog-padding: 24px !default;
7+
$mat-dialog-title-padding: 24px !default;
78
$mat-dialog-border-radius: 2px !default;
8-
$mat-dialog-max-height: 65vh !default;
99
$mat-dialog-button-margin: 8px !default;
1010

11+
// TODO(crisbeto): not used anywhere, to be removed next major release.
12+
$mat-dialog-max-height: 65vh !default;
13+
1114
.mat-dialog-container {
1215
@include mat-elevation(24);
1316

@@ -32,27 +35,58 @@ $mat-dialog-button-margin: 8px !default;
3235
}
3336
}
3437

38+
.mat-dialog-container-scrollable {
39+
padding: 0;
40+
41+
// Since there are 5-6 levels of elements down before we can reach
42+
// the projected content, we have to use a class that lets us propagate
43+
// the dimensions down to the relevant flexboxes, in order for IE to
44+
// work correctly.
45+
&, .mat-dialog-component-host {
46+
width: inherit;
47+
min-width: inherit;
48+
max-width: inherit;
49+
height: inherit;
50+
min-height: inherit;
51+
max-height: inherit;
52+
display: flex;
53+
flex-direction: column;
54+
overflow: auto;
55+
}
56+
}
57+
58+
.mat-dialog-title {
59+
display: flex;
60+
flex-direction: column;
61+
justify-content: center;
62+
width: 100%;
63+
flex-shrink: 0;
64+
margin: 0;
65+
padding: $mat-dialog-title-padding;
66+
box-sizing: border-box;
67+
}
68+
3569
.mat-dialog-content {
3670
display: block;
37-
margin: 0 $mat-dialog-padding * -1;
38-
padding: 0 $mat-dialog-padding;
39-
max-height: $mat-dialog-max-height;
71+
padding: $mat-dialog-padding $mat-dialog-padding 0;
4072
overflow: auto;
4173
-webkit-overflow-scrolling: touch;
42-
}
4374

44-
.mat-dialog-title {
45-
margin: 0 0 20px;
46-
display: block;
75+
// Avoid stacking the padding if there's a title.
76+
.mat-dialog-title ~ & {
77+
padding-top: 0;
78+
}
4779
}
4880

4981
.mat-dialog-actions {
50-
padding: $mat-dialog-padding / 2 0;
82+
padding: $mat-dialog-padding / 2 $mat-dialog-padding;
5183
display: flex;
5284
flex-wrap: wrap;
5385

5486
// Pull the actions down to avoid their padding stacking with the dialog's padding.
5587
margin-bottom: -$mat-dialog-padding;
88+
align-items: center;
89+
flex-shrink: 0;
5690

5791
&[align='end'] {
5892
justify-content: flex-end;

src/lib/dialog/dialog.spec.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -976,6 +976,13 @@ describe('MatDialog', () => {
976976
}));
977977

978978
runContentElementTests();
979+
980+
it('should set the `mat-dialog-component-host` class on the rendered component', () => {
981+
const container = overlayContainerElement.querySelector('mat-dialog-container')!;
982+
const host = container.querySelector('content-element-dialog')!;
983+
984+
expect(host.classList).toContain('mat-dialog-component-host');
985+
});
979986
});
980987

981988
describe('inside template portal', () => {
@@ -1047,6 +1054,11 @@ describe('MatDialog', () => {
10471054
expect(container.getAttribute('aria-labelledby'))
10481055
.toBe(title.id, 'Expected the aria-labelledby to match the title id.');
10491056
}));
1057+
1058+
it('should set the `mat-dialog-container-scrollable` class on the container', () => {
1059+
const container = overlayContainerElement.querySelector('mat-dialog-container')!;
1060+
expect(container.classList).toContain('mat-dialog-container-scrollable');
1061+
});
10501062
}
10511063
});
10521064

@@ -1297,6 +1309,7 @@ class PizzaMsg {
12971309
}
12981310

12991311
@Component({
1312+
selector: 'content-element-dialog',
13001313
template: `
13011314
<h1 mat-dialog-title>This is the title</h1>
13021315
<mat-dialog-content>Lorem ipsum dolor sit amet.</mat-dialog-content>
@@ -1314,6 +1327,7 @@ class PizzaMsg {
13141327
class ContentElementDialog {}
13151328

13161329
@Component({
1330+
selector: 'content-element-dialog',
13171331
template: `
13181332
<ng-template>
13191333
<h1 mat-dialog-title>This is the title</h1>

0 commit comments

Comments
 (0)