From c7905ae95e0b140c14a5199c5e2ccfa38864dfc4 Mon Sep 17 00:00:00 2001 From: crisbeto Date: Thu, 12 Jul 2018 14:54:44 +0200 Subject: [PATCH] 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. --- src/dev-app/dialog/dialog-demo.html | 38 ++++++++++++++++++-- src/dev-app/dialog/dialog-demo.ts | 18 +++++----- src/lib/dialog/dialog-config.ts | 2 +- src/lib/dialog/dialog-container.html | 4 ++- src/lib/dialog/dialog-container.ts | 29 +++++++++++++-- src/lib/dialog/dialog.scss | 54 +++++++++++++++++++++------- src/lib/dialog/dialog.spec.ts | 14 ++++++++ 7 files changed, 130 insertions(+), 29 deletions(-) diff --git a/src/dev-app/dialog/dialog-demo.html b/src/dev-app/dialog/dialog-demo.html index ebf76c003267..afad8ee075e5 100644 --- a/src/dev-app/dialog/dialog-demo.html +++ b/src/dev-app/dialog/dialog-demo.html @@ -6,9 +6,13 @@

Dialog demo

- + + @@ -111,7 +115,7 @@

Other options

Last afterClosed result: {{lastAfterClosedResult}}

Last beforeClose result: {{lastBeforeCloseResult}}

- + I'm a template dialog. I've been opened {{numTemplateOpens}} times!

It's Jazz!

@@ -123,5 +127,33 @@

Other options

{{ data.message }}

- ` + +
+ + +

Saturn

+ + + Saturn + Saturn is the sixth planet from the Sun and the second-largest in the Solar System, after + Jupiter. It is a gas giant with an average radius about nine times that of Earth. It has + only one-eighth the average density of Earth, but with its larger volume Saturn is over + 95 times more massive. Saturn is named after the Roman god of agriculture; its astronomical + symbol (♄) represents the god's sickle. + + Saturn's interior is probably composed of a core of iron–nickel and rock + (silicon and oxygen compounds). This core is surrounded by a deep layer of metallic hydrogen, + an intermediate layer of liquid hydrogen and liquid helium, and finally a gaseous outer layer. + Saturn has a pale yellow hue due to ammonia crystals in its upper atmosphere. Electrical + current within the metallic hydrogen layer is thought to give rise to Saturn's planetary + magnetic field, which is weaker than Earth's, but has a magnetic moment 580 times that of + Earth due to Saturn's larger size. Saturn's magnetic field strength is around one-twentieth + of Jupiter's. The outer atmosphere is generally bland and lacking in contrast, although + long-lived features can appear. Wind speeds on Saturn can reach 1,800 km/h (1,100 mph), + higher than on Jupiter, but not as high as those on Neptune. + + + + +
diff --git a/src/dev-app/dialog/dialog-demo.ts b/src/dev-app/dialog/dialog-demo.ts index b3640fa356be..9bb69855d29e 100644 --- a/src/dev-app/dialog/dialog-demo.ts +++ b/src/dev-app/dialog/dialog-demo.ts @@ -7,7 +7,7 @@ */ import {DOCUMENT} from '@angular/common'; -import {Component, Inject, TemplateRef, ViewChild} from '@angular/core'; +import {Component, Inject, TemplateRef} from '@angular/core'; import {MAT_DIALOG_DATA, MatDialog, MatDialogConfig, MatDialogRef} from '@angular/material'; @@ -29,12 +29,12 @@ export class DialogDemo { panelClass: 'custom-overlay-pane-class', hasBackdrop: true, backdropClass: '', - width: '', - height: '', - minWidth: '', - minHeight: '', + width: defaultDialogConfig.width, + height: defaultDialogConfig.height, + minWidth: defaultDialogConfig.minWidth, + minHeight: defaultDialogConfig.minHeight, maxWidth: defaultDialogConfig.maxWidth, - maxHeight: '', + maxHeight: defaultDialogConfig.maxHeight, position: { top: '', bottom: '', @@ -47,8 +47,6 @@ export class DialogDemo { }; numTemplateOpens = 0; - @ViewChild(TemplateRef) template: TemplateRef; - constructor(public dialog: MatDialog, @Inject(DOCUMENT) doc: any) { // Possible useful example for the open and closeAll events. // Adding a class to the body if a dialog opens and @@ -80,9 +78,9 @@ export class DialogDemo { dialogRef.componentInstance.actionsAlignment = this.actionsAlignment; } - openTemplate() { + openTemplate(template: TemplateRef) { this.numTemplateOpens++; - this.dialog.open(this.template, this.config); + this.dialog.open(template, this.config); } } diff --git a/src/lib/dialog/dialog-config.ts b/src/lib/dialog/dialog-config.ts index add3efbe9063..8598c44b83b1 100644 --- a/src/lib/dialog/dialog-config.ts +++ b/src/lib/dialog/dialog-config.ts @@ -75,7 +75,7 @@ export class MatDialogConfig { maxWidth?: number | string = '80vw'; /** Max-height of the dialog. If a number is provided, pixel units are assumed. */ - maxHeight?: number | string; + maxHeight?: number | string = '80vh'; /** Position overrides. */ position?: DialogPosition; diff --git a/src/lib/dialog/dialog-container.html b/src/lib/dialog/dialog-container.html index 215f0d9c55b3..6122e6b4b583 100644 --- a/src/lib/dialog/dialog-container.html +++ b/src/lib/dialog/dialog-container.html @@ -1 +1,3 @@ - +
+ +
diff --git a/src/lib/dialog/dialog-container.ts b/src/lib/dialog/dialog-container.ts index fbcbacc6c8dd..931fd6688d91 100644 --- a/src/lib/dialog/dialog-container.ts +++ b/src/lib/dialog/dialog-container.ts @@ -113,7 +113,15 @@ export class MatDialogContainer extends BasePortalOutlet { } this._savePreviouslyFocusedElement(); - return this._portalOutlet.attachComponentPortal(portal); + + const componentRef = this._portalOutlet.attachComponentPortal(portal); + + // We need to add an extra class to the root of the instantiated component, which + // allows us to propagate some width/height overrides down from the overlay pane. + componentRef.location.nativeElement.classList.add('mat-dialog-component-host'); + this._toggleScrollableContentClass(); + + return componentRef; } /** @@ -126,7 +134,9 @@ export class MatDialogContainer extends BasePortalOutlet { } this._savePreviouslyFocusedElement(); - return this._portalOutlet.attachTemplatePortal(portal); + const viewRef = this._portalOutlet.attachTemplatePortal(portal); + this._toggleScrollableContentClass(); + return viewRef; } /** Moves the focus inside the focus trap. */ @@ -196,4 +206,19 @@ export class MatDialogContainer extends BasePortalOutlet { // view container is using OnPush change detection. this._changeDetectorRef.markForCheck(); } + + /** + * Toggles a class on the host element, depending on whether it has + * scrollable content. Used to activate particular flexbox styling. + */ + private _toggleScrollableContentClass() { + const element: HTMLElement = this._elementRef.nativeElement; + const cssClass = 'mat-dialog-container-scrollable'; + + if (element.querySelector('.mat-dialog-content')) { + element.classList.add(cssClass); + } else { + element.classList.remove(cssClass); + } + } } diff --git a/src/lib/dialog/dialog.scss b/src/lib/dialog/dialog.scss index 18c3f1c353eb..8dec786791b4 100644 --- a/src/lib/dialog/dialog.scss +++ b/src/lib/dialog/dialog.scss @@ -4,9 +4,12 @@ $mat-dialog-padding: 24px !default; $mat-dialog-border-radius: 4px !default; -$mat-dialog-max-height: 65vh !default; +$mat-dialog-title-padding: 24px !default; $mat-dialog-button-margin: 8px !default; +// TODO(crisbeto): not used anywhere, to be removed next major release. +$mat-dialog-max-height: 65vh !default; + .mat-dialog-container { display: block; padding: $mat-dialog-padding; @@ -29,29 +32,56 @@ $mat-dialog-button-margin: 8px !default; } } +.mat-dialog-container-scrollable { + padding: 0; + + // Since there are 5-6 levels of elements down before we can reach + // the projected content, we have to use a class that lets us propagate + // the dimensions down to the relevant flexboxes, in order for IE to + // work correctly. + &, .mat-dialog-component-host { + width: inherit; + min-width: inherit; + max-width: inherit; + height: inherit; + min-height: inherit; + max-height: inherit; + display: flex; + flex-direction: column; + overflow: auto; + } +} + +.mat-dialog-title { + display: flex; + flex-direction: column; + justify-content: center; + width: 100%; + flex-shrink: 0; + margin: 0; + padding: $mat-dialog-title-padding; + box-sizing: border-box; +} + .mat-dialog-content { display: block; - margin: 0 $mat-dialog-padding * -1; - padding: 0 $mat-dialog-padding; - max-height: $mat-dialog-max-height; + padding: $mat-dialog-padding $mat-dialog-padding 0; overflow: auto; -webkit-overflow-scrolling: touch; -} -.mat-dialog-title { - margin: 0 0 20px; - display: block; + // Avoid stacking the padding if there's a title. + .mat-dialog-title ~ & { + padding-top: 0; + } } .mat-dialog-actions { - padding: 8px 0; + padding: 8px $mat-dialog-padding; display: flex; flex-wrap: wrap; min-height: 52px; align-items: center; - - // Pull the actions down to avoid their padding stacking with the dialog's padding. - margin-bottom: -$mat-dialog-padding; + flex-shrink: 0; &[align='end'] { justify-content: flex-end; diff --git a/src/lib/dialog/dialog.spec.ts b/src/lib/dialog/dialog.spec.ts index 0fd03cdeeb58..acb1083d4a9f 100644 --- a/src/lib/dialog/dialog.spec.ts +++ b/src/lib/dialog/dialog.spec.ts @@ -1072,6 +1072,13 @@ describe('MatDialog', () => { })); runContentElementTests(); + + it('should set the `mat-dialog-component-host` class on the rendered component', () => { + const container = overlayContainerElement.querySelector('mat-dialog-container')!; + const host = container.querySelector('content-element-dialog')!; + + expect(host.classList).toContain('mat-dialog-component-host'); + }); }); describe('inside template portal', () => { @@ -1143,6 +1150,11 @@ describe('MatDialog', () => { expect(container.getAttribute('aria-labelledby')) .toBe(title.id, 'Expected the aria-labelledby to match the title id.'); })); + + it('should set the `mat-dialog-container-scrollable` class on the container', () => { + const container = overlayContainerElement.querySelector('mat-dialog-container')!; + expect(container.classList).toContain('mat-dialog-container-scrollable'); + }); } }); @@ -1409,6 +1421,7 @@ class PizzaMsg { } @Component({ + selector: 'content-element-dialog', template: `

This is the title

Lorem ipsum dolor sit amet. @@ -1426,6 +1439,7 @@ class PizzaMsg { class ContentElementDialog {} @Component({ + selector: 'content-element-dialog', template: `

This is the title