Permalink
Browse files

feat(overlay): add fullscreen-enabled overlay class (#1949)

* feat(overlay): add fullscreen overlay class

* fix lint error

* move fs container to a new file and rename it

* add e2e tests

* fix tests

* rebased and fix comments

* fix typings

* fix e2e tests

* address comments

* fix tests
  • Loading branch information...
1 parent f9dd34f commit 06403028a6c50ab82ce56121e4267be0e3f33c8a @quanterion quanterion committed with tinayuangao Jan 11, 2017
@@ -0,0 +1,38 @@
+import {browser, by, element, Key, ProtractorBy} from 'protractor';
+
+describe('fullscreen', () => {
+ beforeEach(() => browser.get('/fullscreen'));
+
+ let overlayInBody = () =>
+ browser.isElementPresent(by.css('body > .cdk-overlay-container') as ProtractorBy);
+ let overlayInFullscreen = () =>
+ browser.isElementPresent(by.css('#fullscreenpane > .cdk-overlay-container') as ProtractorBy);
+
+ it('should open a dialog inside a fullscreen element and move it to the document body', () => {
+ element(by.id('fullscreen')).click();
+ element(by.id('dialog')).click();
+
+ overlayInFullscreen().then((isPresent: boolean) => {
+ expect(isPresent).toBe(true);
+ element(by.id('exitfullscreenindialog')).click();
+ overlayInBody().then((isPresent: boolean) => {
+ expect(isPresent).toBe(true);
+ });
+ });
+ });
+
+ it('should open a dialog inside the document body and move it to a fullscreen element', () => {
+ element(by.id('dialog')).click();
+ overlayInBody().then((isPresent: boolean) => {
+ expect(isPresent).toBe(true);
+ element(by.id('fullscreenindialog')).click();
+ overlayInFullscreen().then((isPresent: boolean) => {
+ expect(isPresent).toBe(true);
+ element(by.id('exitfullscreenindialog')).click();
+ overlayInBody().then((isPresent: boolean) => {
+ expect(isPresent).toBe(true);
+ });
+ });
+ });
+ });
+});
@@ -4,7 +4,8 @@ import {HttpModule} from '@angular/http';
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
import {DemoApp, Home} from './demo-app/demo-app';
import {RouterModule} from '@angular/router';
-import {MaterialModule} from '@angular/material';
+import {MaterialModule, OverlayContainer,
+ FullscreenOverlayContainer} from '@angular/material';
import {DEMO_APP_ROUTES} from './demo-app/routes';
import {ProgressBarDemo} from './progress-bar/progress-bar-demo';
import {JazzDialog, ContentElementDialog, DialogDemo} from './dialog/dialog-demo';
@@ -94,6 +95,9 @@ import {InputContainerDemo} from './input/input-container-demo';
FoggyTabContent,
PlatformDemo
],
+ providers: [
+ {provide: OverlayContainer, useClass: FullscreenOverlayContainer}
+ ],
entryComponents: [
DemoApp,
JazzDialog,
@@ -25,6 +25,9 @@
</button>
<div class="demo-toolbar">
<h1>Angular Material 2 Demos</h1>
+ <button md-button (click)="toggleFullscreen()" title="Toggle fullscreen">
+ Fullscreen
+ </button>
<button md-button (click)="root.dir = (root.dir == 'rtl' ? 'ltr' : 'rtl')" title="Toggle between RTL and LTR">
{{root.dir.toUpperCase()}}
</button>
@@ -39,3 +39,9 @@ body {
font-size: 20px;
}
}
+
+// stretch to screen size in fullscreen mode
+.demo-content {
+ width: 100%;
+ height: 100%;
+}
@@ -1,4 +1,4 @@
-import {Component, ViewEncapsulation} from '@angular/core';
+import {Component, ViewEncapsulation, ElementRef} from '@angular/core';
@Component({
@@ -52,4 +52,21 @@ export class DemoApp {
{name: 'Tooltip', route: 'tooltip'},
{name: 'Platform', route: 'platform'}
];
+
+ constructor(private _element: ElementRef) {
+
+ }
+
+ toggleFullscreen() {
+ let elem = this._element.nativeElement.querySelector('.demo-content');
+ if (elem.requestFullscreen) {
+ elem.requestFullscreen();
+ } else if (elem.webkitRequestFullScreen) {
+ elem.webkitRequestFullScreen();
+ } else if (elem.mozRequestFullScreen) {
+ elem.mozRequestFullScreen();
+ } else if (elem.msRequestFullScreen) {
+ elem.msRequestFullScreen();
+ }
+ }
}
@@ -13,11 +13,11 @@ import {GridListE2E} from './grid-list/grid-list-e2e';
import {ListE2E} from './list/list-e2e';
import {ProgressBarE2E} from './progress-bar/progress-bar-e2e';
import {ProgressSpinnerE2E} from './progress-spinner/progress-spinner-e2e';
-import {MaterialModule} from '@angular/material';
+import {FullscreenE2E, TestDialog as TestDialogFullScreen} from './fullscreen/fullscreen-e2e';
+import {MaterialModule, OverlayContainer, FullscreenOverlayContainer} from '@angular/material';
import {E2E_APP_ROUTES} from './e2e-app/routes';
import {SlideToggleE2E} from './slide-toggle/slide-toggle-e2e';
-
@NgModule({
imports: [
BrowserModule,
@@ -39,12 +39,15 @@ import {SlideToggleE2E} from './slide-toggle/slide-toggle-e2e';
ListE2E,
ProgressBarE2E,
ProgressSpinnerE2E,
- SlideToggleE2E
+ SlideToggleE2E,
+ FullscreenE2E,
+ TestDialogFullScreen
],
bootstrap: [E2EApp],
providers: [
- {provide: AnimationDriver, useValue: AnimationDriver.NOOP}
+ {provide: AnimationDriver, useValue: AnimationDriver.NOOP},
+ {provide: OverlayContainer, useClass: FullscreenOverlayContainer}
],
- entryComponents: [TestDialog]
+ entryComponents: [TestDialog, TestDialogFullScreen]
})
export class E2eAppModule { }
@@ -13,6 +13,7 @@
<a md-list-item [routerLink]="['radio']">Radios</a>
<a md-list-item [routerLink]="['slide-toggle']">Slide Toggle</a>
<a md-list-item [routerLink]="['tabs']">Tabs</a>
+ <a md-list-item [routerLink]="['fullscreen']">Fullscreen</a>
</md-nav-list>
<main>
@@ -12,6 +12,7 @@ import {ListE2E} from '../list/list-e2e';
import {ProgressBarE2E} from '../progress-bar/progress-bar-e2e';
import {ProgressSpinnerE2E} from '../progress-spinner/progress-spinner-e2e';
import {SlideToggleE2E} from '../slide-toggle/slide-toggle-e2e';
+import {FullscreenE2E} from '../fullscreen/fullscreen-e2e';
export const E2E_APP_ROUTES: Routes = [
{path: '', component: Home},
@@ -26,5 +27,6 @@ export const E2E_APP_ROUTES: Routes = [
{path: 'list', component: ListE2E},
{path: 'progress-bar', component: ProgressBarE2E},
{path: 'progress-spinner', component: ProgressSpinnerE2E},
- {path: 'slide-toggle', component: SlideToggleE2E}
+ {path: 'slide-toggle', component: SlideToggleE2E},
+ {path: 'fullscreen', component: FullscreenE2E}
];
@@ -0,0 +1,5 @@
+<button id="fullscreen" (click)="toggleFullScreen()">FULLSCREEN</button>
+<div id="fullscreenpane" style="width: 100%; height: 100%">
+ <button id="dialog" (click)="openDialog()">DIALOG</button>
+ <button id="exitfullscreen" (click)="exitFullscreen()">EXIT FULLSCREEN</button>
+</div>
@@ -0,0 +1,61 @@
+import {Component, ElementRef, Output, EventEmitter} from '@angular/core';
+import {MdDialog, MdDialogRef} from '@angular/material';
+
+@Component({
+ selector: 'fullscreen-e2e',
+ moduleId: module.id,
+ templateUrl: 'fullscreen-e2e.html'
+})
+export class FullscreenE2E {
+ dialogRef: MdDialogRef<TestDialog>;
+
+ constructor (private _element: ElementRef, private _dialog: MdDialog) { }
+
+ openDialog() {
+ this.dialogRef = this._dialog.open(TestDialog);
+ this.dialogRef.componentInstance.fullscreen.subscribe(() => this.toggleFullScreen());
+ this.dialogRef.componentInstance.exitfullscreen.subscribe(() => this.exitFullscreen());
+ this.dialogRef.afterClosed().subscribe(() => {
+ this.dialogRef = null;
+ });
+ }
+
+ toggleFullScreen() {
+ let element = this._element.nativeElement.querySelector('#fullscreenpane');
+ if (element.requestFullscreen) {
+ element.requestFullscreen();
+ } else if (element.webkitRequestFullScreen) {
+ element.webkitRequestFullScreen();
+ } else if ((element as any).mozRequestFullScreen) {
+ (element as any).mozRequestFullScreen();
+ } else if ((element as any).msRequestFullScreen) {
+ (element as any).msRequestFullScreen();
+ }
+ }
+
+ exitFullscreen() {
+ if (document.exitFullscreen) {
+ document.exitFullscreen();
+ } else if (document.webkitExitFullscreen) {
+ document.webkitExitFullscreen();
+ } else if ((document as any).mozExitFullScreen) {
+ (document as any).mozExitFullScreen();
+ } else if ((document as any).msExitFullScreen) {
+ (document as any).msExitFullScreen();
+ }
+ }
+}
+
+@Component({
+ selector: 'fullscreen-dialog-e2e-test',
+ template: `
+ <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</p>
+ <button id="fullscreenindialog" (click)="fullscreen.emit()">FULLSCREEN</button>
+ <button id="exitfullscreenindialog" (click)="exitfullscreen.emit()">EXIT FULLSCREEN</button>
+ <button type="button" (click)="dialogRef.close()" id="close">CLOSE</button>`
+})
+export class TestDialog {
+ constructor(public dialogRef: MdDialogRef<TestDialog>) { }
+ @Output() fullscreen = new EventEmitter<void>();
+ @Output() exitfullscreen = new EventEmitter<void>();
+}
@@ -45,6 +45,7 @@ export {Platform as MdPlatform} from './platform/platform';
// Overlay
export {Overlay, OVERLAY_PROVIDERS} from './overlay/overlay';
export {OverlayContainer} from './overlay/overlay-container';
+export {FullscreenOverlayContainer} from './overlay/fullscreen-overlay-container';
export {OverlayRef} from './overlay/overlay-ref';
export {OverlayState} from './overlay/overlay-state';
export {
@@ -0,0 +1,53 @@
+import {Injectable} from '@angular/core';
+import {OverlayContainer} from './overlay-container';
+
+/**
+ * The FullscreenOverlayContainer is the alternative to OverlayContainer
+ * that supports correct displaying of overlay elements in Fullscreen mode
+ * https://developer.mozilla.org/en-US/docs/Web/API/Element/requestFullScreen
+ * It should be provided in the root component that way:
+ * providers: [
+ * {provide: OverlayContainer, useClass: FullscreenOverlayContainer}
+ * ],
+ */
+@Injectable()
+export class FullscreenOverlayContainer extends OverlayContainer {
+ protected _createContainer(): void {
+ super._createContainer();
+ this._adjustParentForFullscreenChange();
+ this._addFullscreenChangeListener(() => this._adjustParentForFullscreenChange());
+ }
+
+ private _adjustParentForFullscreenChange(): void {
+ if (!this._containerElement) {
+ return;
+ }
+ let fullscreenElement = this.getFullscreenElement();
+ let parent = fullscreenElement || document.body;
+ parent.appendChild(this._containerElement);
+ }
+
+ private _addFullscreenChangeListener(fn: () => void) {
+ if (document.fullscreenEnabled) {
+ document.addEventListener('fullscreenchange', fn);
+ } else if (document.webkitFullscreenEnabled) {
+ document.addEventListener('webkitfullscreenchange', fn);
+ } else if ((document as any).mozFullScreenEnabled) {
+ document.addEventListener('mozfullscreenchange', fn);
+ } else if ((document as any).msFullscreenEnabled) {
+ document.addEventListener('MSFullscreenChange', fn);
+ }
+ }
+
+ /**
+ * When the page is put into fullscreen mode, a specific element is specified.
+ * Only that element and its children are visible when in fullscreen mode.
+ */
+ getFullscreenElement(): Element {
+ return document.fullscreenElement ||
+ document.webkitFullscreenElement ||
+ (document as any).mozFullScreenElement ||
+ (document as any).msFullscreenElement ||
+ null;
+ }
+}
@@ -1,9 +1,12 @@
+import {Injectable} from '@angular/core';
+
/**
* The OverlayContainer is the container in which all overlays will load.
* It should be provided in the root component to ensure it is properly shared.
*/
+@Injectable()
export class OverlayContainer {
- private _containerElement: HTMLElement;
+ protected _containerElement: HTMLElement;
/**
* This method returns the overlay container element. It will lazily
@@ -20,7 +23,7 @@ export class OverlayContainer {
* Create the overlay container element, which is simply a div
* with the 'cdk-overlay-container' class on the document body.
*/
- private _createContainer(): void {
+ protected _createContainer(): void {
let container = document.createElement('div');
container.classList.add('cdk-overlay-container');
document.body.appendChild(container);

0 comments on commit 0640302

Please sign in to comment.