Skip to content

Commit

Permalink
fix(overlay): lazily create container (#894)
Browse files Browse the repository at this point in the history
  • Loading branch information
kara authored and robertmesserle committed Jul 22, 2016
1 parent 3179fec commit 1efbbb9
Show file tree
Hide file tree
Showing 12 changed files with 83 additions and 63 deletions.
15 changes: 10 additions & 5 deletions src/components/dialog/dialog.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import {
ChangeDetectorRef,
} from '@angular/core';
import {MdDialog} from './dialog';
import {OVERLAY_PROVIDERS, OVERLAY_CONTAINER_TOKEN} from '@angular2-material/core/overlay/overlay';
import {OVERLAY_PROVIDERS} from '@angular2-material/core/overlay/overlay';
import {OverlayContainer} from '@angular2-material/core/overlay/overlay-container';
import {MdDialogConfig} from './dialog-config';
import {MdDialogRef} from './dialog-ref';

Expand All @@ -31,10 +32,14 @@ describe('MdDialog', () => {
addProviders([
OVERLAY_PROVIDERS,
MdDialog,
{provide: OVERLAY_CONTAINER_TOKEN, useFactory: () => {
overlayContainerElement = document.createElement('div');
return overlayContainerElement;
}}
{provide: OverlayContainer, useFactory: () => {
return {
getContainerElement: () => {
overlayContainerElement = document.createElement('div');
return overlayContainerElement;
}
};
}},
]);
});

Expand Down
2 changes: 1 addition & 1 deletion src/components/menu/menu-trigger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export class MdMenuTrigger implements AfterViewInit, OnDestroy {
}

destroyMenu(): void {
this._overlayRef.dispose();
if (this._overlayRef) { this._overlayRef.dispose(); }
}

// set state rather than toggle to support triggers sharing a menu
Expand Down
19 changes: 11 additions & 8 deletions src/components/tooltip/tooltip.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,27 @@ import {
beforeEachProviders,
} from '@angular/core/testing';
import {TestComponentBuilder, ComponentFixture} from '@angular/compiler/testing';
import {Component, provide, DebugElement} from '@angular/core';
import {Component, DebugElement} from '@angular/core';
import {By} from '@angular/platform-browser';
import {MD_TOOLTIP_DIRECTIVES, TooltipPosition, MdTooltip} from
'@angular2-material/tooltip/tooltip';
import {OVERLAY_PROVIDERS, OVERLAY_CONTAINER_TOKEN} from '@angular2-material/core/overlay/overlay';
import {OVERLAY_PROVIDERS} from '@angular2-material/core/overlay/overlay';
import {OverlayContainer} from '@angular2-material/core/overlay/overlay-container';

describe('MdTooltip', () => {
let builder: TestComponentBuilder;
let overlayContainerElement: HTMLElement;

beforeEachProviders(() => [
OVERLAY_PROVIDERS,
provide(OVERLAY_CONTAINER_TOKEN, {
useFactory: () => {
overlayContainerElement = document.createElement('div');
return overlayContainerElement;
}
})
{provide: OverlayContainer, useFactory: () => {
return {
getContainerElement: () => {
overlayContainerElement = document.createElement('div');
return overlayContainerElement;
}
};
}},
]);

beforeEach(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
Expand Down
3 changes: 2 additions & 1 deletion src/core/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ export {
export {DomPortalHost} from './portal/dom-portal-host';

// Overlay
export {Overlay, OVERLAY_CONTAINER_TOKEN, OVERLAY_PROVIDERS} from './overlay/overlay';
export {Overlay, OVERLAY_PROVIDERS} from './overlay/overlay';
export {OverlayContainer} from './overlay/overlay-container';
export {OverlayRef} from './overlay/overlay-ref';
export {OverlayState} from './overlay/overlay-state';
export {
Expand Down
35 changes: 26 additions & 9 deletions src/core/overlay/overlay-container.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,29 @@


/**
* Create the overlay container element, which is simply a div
* with the 'md-overlay-container' class on the document body.
* 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.
*/
export function createOverlayContainer(): Element {
let container = document.createElement('div');
container.classList.add('md-overlay-container');
document.body.appendChild(container);
return container;
export class OverlayContainer {
private _containerElement: HTMLElement;

/**
* This method returns the overlay container element. It will lazily
* create the element the first time it is called to facilitate using
* the container in non-browser environments.
* @returns {HTMLElement} the container element
*/
getContainerElement(): HTMLElement {
if (!this._containerElement) { this._createContainer(); }
return this._containerElement;
}

/**
* Create the overlay container element, which is simply a div
* with the 'md-overlay-container' class on the document body.
*/
private _createContainer(): void {
let container = document.createElement('div');
container.classList.add('md-overlay-container');
document.body.appendChild(container);
this._containerElement = container;
}
}
14 changes: 9 additions & 5 deletions src/core/overlay/overlay-directives.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ import {
import {TestComponentBuilder, ComponentFixture} from '@angular/compiler/testing';
import {Component, ViewChild} from '@angular/core';
import {ConnectedOverlayDirective, OverlayOrigin} from './overlay-directives';
import {OVERLAY_CONTAINER_TOKEN, Overlay} from './overlay';
import {Overlay} from './overlay';
import {OverlayContainer} from './overlay-container';
import {ViewportRuler} from './position/viewport-ruler';
import {OverlayPositionBuilder} from './position/overlay-position-builder';
import {ConnectedPositionStrategy} from './position/connected-position-strategy';


describe('Overlay directives', () => {
let builder: TestComponentBuilder;
let overlayContainerElement: HTMLElement;
Expand All @@ -24,9 +24,13 @@ describe('Overlay directives', () => {
Overlay,
OverlayPositionBuilder,
ViewportRuler,
{provide: OVERLAY_CONTAINER_TOKEN, useFactory: () => {
overlayContainerElement = document.createElement('div');
return overlayContainerElement;
{provide: OverlayContainer, useFactory: () => {
return {
getContainerElement: () => {
overlayContainerElement = document.createElement('div');
return overlayContainerElement;
}
};
}},
]);
});
Expand Down
4 changes: 3 additions & 1 deletion src/core/overlay/overlay.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// TODO(jelbourn): change from the `md` prefix to something else for everything in the toolkit.

@import 'variables';

/** The overlay-container is an invisible element which contains all individual overlays. */
.md-overlay-container {
position: absolute;
Expand All @@ -19,5 +21,5 @@
position: absolute;
pointer-events: auto;
box-sizing: border-box;
z-index: 1;
z-index: $z-index-overlay;
}
14 changes: 10 additions & 4 deletions src/core/overlay/overlay.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import {
} from '@angular/core';
import {TemplatePortalDirective} from '../portal/portal-directives';
import {TemplatePortal, ComponentPortal} from '../portal/portal';
import {Overlay, OVERLAY_CONTAINER_TOKEN} from './overlay';
import {Overlay} from './overlay';
import {OverlayContainer} from './overlay-container';
import {OverlayRef} from './overlay-ref';
import {OverlayState} from './overlay-state';
import {PositionStrategy} from './position/position-strategy';
Expand All @@ -32,9 +33,14 @@ describe('Overlay', () => {
Overlay,
OverlayPositionBuilder,
ViewportRuler,
{provide: OVERLAY_CONTAINER_TOKEN, useFactory: () => {
overlayContainerElement = document.createElement('div');
return overlayContainerElement;
{provide: OverlayContainer, useFactory: () => {
return {
getContainerElement: () => {
if (overlayContainerElement) { return overlayContainerElement; }
overlayContainerElement = document.createElement('div');
return overlayContainerElement;
}
};
}}
]);
});
Expand Down
24 changes: 5 additions & 19 deletions src/core/overlay/overlay.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import {
ComponentResolver,
OpaqueToken,
Inject,
Injectable,
} from '@angular/core';
import {OverlayState} from './overlay-state';
Expand All @@ -10,10 +8,7 @@ import {OverlayRef} from './overlay-ref';

import {OverlayPositionBuilder} from './position/overlay-position-builder';
import {ViewportRuler} from './position/viewport-ruler';


/** Token used to inject the DOM element that serves as the overlay container. */
export const OVERLAY_CONTAINER_TOKEN = new OpaqueToken('overlayContainer');
import {OverlayContainer} from './overlay-container';

/** Next overlay unique ID. */
let nextUniqueId = 0;
Expand All @@ -32,18 +27,9 @@ let defaultState = new OverlayState();
*/
@Injectable()
export class Overlay {
private _overlayContainerElement: HTMLElement;

constructor(
@Inject(OVERLAY_CONTAINER_TOKEN) overlayContainerElement: any,
private _componentResolver: ComponentResolver,
private _positionBuilder: OverlayPositionBuilder) {

// We inject the container as `any` because the constructor signature cannot reference
// browser globals (HTMLElement) on non-browser environments, since having a class decorator
// causes TypeScript to preserve the constructor signature types.
this._overlayContainerElement = overlayContainerElement;
}
constructor(private _overlayContainer: OverlayContainer,
private _componentResolver: ComponentResolver,
private _positionBuilder: OverlayPositionBuilder) {}

/**
* Creates an overlay.
Expand Down Expand Up @@ -71,7 +57,7 @@ export class Overlay {
pane.id = `md-overlay-${nextUniqueId++}`;
pane.classList.add('md-overlay-pane');

this._overlayContainerElement.appendChild(pane);
this._overlayContainer.getContainerElement().appendChild(pane);

return Promise.resolve(pane);
}
Expand Down
2 changes: 2 additions & 0 deletions src/core/style/_variables.scss
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ $md-font-family: Roboto, 'Helvetica Neue', sans-serif !default;
// Media queries
$md-xsmall: 'max-width: 600px';

// TODO: Revisit all z-indices before beta
// z-index master list
$z-index-fab: 20 !default;
$z-index-drawer: 100 !default;
$z-index-overlay: 1000 !default;

// Global constants
$pi: 3.14159265;
Expand Down
7 changes: 2 additions & 5 deletions src/demo-app/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,10 @@ import {bootstrap} from '@angular/platform-browser-dynamic';
import {HAMMER_GESTURE_CONFIG} from '@angular/platform-browser';
import {HTTP_PROVIDERS} from '@angular/http';
import {disableDeprecatedForms, provideForms} from '@angular/forms';

import {OVERLAY_CONTAINER_TOKEN} from '@angular2-material/core/overlay/overlay';
import {MdLiveAnnouncer} from '@angular2-material/core/a11y/live-announcer';
import {createOverlayContainer} from '@angular2-material/core/overlay/overlay-container';
import {MdGestureConfig} from '@angular2-material/core/gestures/MdGestureConfig';
import {MdIconRegistry} from '@angular2-material/icon/icon-registry';

import {OverlayContainer} from '@angular2-material/core/overlay/overlay-container';
import {DemoApp} from './demo-app/demo-app';
import {DEMO_APP_ROUTE_PROVIDER} from './demo-app/routes';

Expand All @@ -17,8 +14,8 @@ bootstrap(DemoApp, [
disableDeprecatedForms(),
provideForms(),
MdLiveAnnouncer,
{provide: OVERLAY_CONTAINER_TOKEN, useValue: createOverlayContainer()},
HTTP_PROVIDERS,
OverlayContainer,
MdIconRegistry,
{provide: HAMMER_GESTURE_CONFIG, useClass: MdGestureConfig},
]);
7 changes: 2 additions & 5 deletions src/e2e-app/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,10 @@ import {HAMMER_GESTURE_CONFIG} from '@angular/platform-browser';
import {HTTP_PROVIDERS} from '@angular/http';
import {Renderer} from '@angular/core';
import {disableDeprecatedForms, provideForms} from '@angular/forms';

import {OVERLAY_CONTAINER_TOKEN} from '@angular2-material/core/overlay/overlay';
import {MdLiveAnnouncer} from '@angular2-material/core/a11y/live-announcer';
import {createOverlayContainer} from '@angular2-material/core/overlay/overlay-container';
import {MdGestureConfig} from '@angular2-material/core/gestures/MdGestureConfig';
import {MdIconRegistry} from '@angular2-material/icon/icon-registry';

import {OverlayContainer} from '@angular2-material/core/overlay/overlay-container';
import {E2EApp} from './e2e-app/e2e-app';
import {E2E_APP_ROUTE_PROVIDER} from './e2e-app/routes';

Expand All @@ -18,7 +15,7 @@ bootstrap(E2EApp, [
disableDeprecatedForms(),
provideForms(),
MdLiveAnnouncer,
{provide: OVERLAY_CONTAINER_TOKEN, useValue: createOverlayContainer()},
OverlayContainer,
HTTP_PROVIDERS,
MdIconRegistry,
Renderer,
Expand Down

0 comments on commit 1efbbb9

Please sign in to comment.