Skip to content

Commit 1efbbb9

Browse files
kararobertmesserle
authored andcommitted
fix(overlay): lazily create container (#894)
1 parent 3179fec commit 1efbbb9

File tree

12 files changed

+83
-63
lines changed

12 files changed

+83
-63
lines changed

src/components/dialog/dialog.spec.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ import {
1313
ChangeDetectorRef,
1414
} from '@angular/core';
1515
import {MdDialog} from './dialog';
16-
import {OVERLAY_PROVIDERS, OVERLAY_CONTAINER_TOKEN} from '@angular2-material/core/overlay/overlay';
16+
import {OVERLAY_PROVIDERS} from '@angular2-material/core/overlay/overlay';
17+
import {OverlayContainer} from '@angular2-material/core/overlay/overlay-container';
1718
import {MdDialogConfig} from './dialog-config';
1819
import {MdDialogRef} from './dialog-ref';
1920

@@ -31,10 +32,14 @@ describe('MdDialog', () => {
3132
addProviders([
3233
OVERLAY_PROVIDERS,
3334
MdDialog,
34-
{provide: OVERLAY_CONTAINER_TOKEN, useFactory: () => {
35-
overlayContainerElement = document.createElement('div');
36-
return overlayContainerElement;
37-
}}
35+
{provide: OverlayContainer, useFactory: () => {
36+
return {
37+
getContainerElement: () => {
38+
overlayContainerElement = document.createElement('div');
39+
return overlayContainerElement;
40+
}
41+
};
42+
}},
3843
]);
3944
});
4045

src/components/menu/menu-trigger.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ export class MdMenuTrigger implements AfterViewInit, OnDestroy {
7171
}
7272

7373
destroyMenu(): void {
74-
this._overlayRef.dispose();
74+
if (this._overlayRef) { this._overlayRef.dispose(); }
7575
}
7676

7777
// set state rather than toggle to support triggers sharing a menu

src/components/tooltip/tooltip.spec.ts

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,24 +8,27 @@ import {
88
beforeEachProviders,
99
} from '@angular/core/testing';
1010
import {TestComponentBuilder, ComponentFixture} from '@angular/compiler/testing';
11-
import {Component, provide, DebugElement} from '@angular/core';
11+
import {Component, DebugElement} from '@angular/core';
1212
import {By} from '@angular/platform-browser';
1313
import {MD_TOOLTIP_DIRECTIVES, TooltipPosition, MdTooltip} from
1414
'@angular2-material/tooltip/tooltip';
15-
import {OVERLAY_PROVIDERS, OVERLAY_CONTAINER_TOKEN} from '@angular2-material/core/overlay/overlay';
15+
import {OVERLAY_PROVIDERS} from '@angular2-material/core/overlay/overlay';
16+
import {OverlayContainer} from '@angular2-material/core/overlay/overlay-container';
1617

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

2122
beforeEachProviders(() => [
2223
OVERLAY_PROVIDERS,
23-
provide(OVERLAY_CONTAINER_TOKEN, {
24-
useFactory: () => {
25-
overlayContainerElement = document.createElement('div');
26-
return overlayContainerElement;
27-
}
28-
})
24+
{provide: OverlayContainer, useFactory: () => {
25+
return {
26+
getContainerElement: () => {
27+
overlayContainerElement = document.createElement('div');
28+
return overlayContainerElement;
29+
}
30+
};
31+
}},
2932
]);
3033

3134
beforeEach(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {

src/core/core.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ export {
1717
export {DomPortalHost} from './portal/dom-portal-host';
1818

1919
// Overlay
20-
export {Overlay, OVERLAY_CONTAINER_TOKEN, OVERLAY_PROVIDERS} from './overlay/overlay';
20+
export {Overlay, OVERLAY_PROVIDERS} from './overlay/overlay';
21+
export {OverlayContainer} from './overlay/overlay-container';
2122
export {OverlayRef} from './overlay/overlay-ref';
2223
export {OverlayState} from './overlay/overlay-state';
2324
export {

src/core/overlay/overlay-container.ts

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,29 @@
1-
2-
31
/**
4-
* Create the overlay container element, which is simply a div
5-
* with the 'md-overlay-container' class on the document body.
2+
* The OverlayContainer is the container in which all overlays will load.
3+
* It should be provided in the root component to ensure it is properly shared.
64
*/
7-
export function createOverlayContainer(): Element {
8-
let container = document.createElement('div');
9-
container.classList.add('md-overlay-container');
10-
document.body.appendChild(container);
11-
return container;
5+
export class OverlayContainer {
6+
private _containerElement: HTMLElement;
7+
8+
/**
9+
* This method returns the overlay container element. It will lazily
10+
* create the element the first time it is called to facilitate using
11+
* the container in non-browser environments.
12+
* @returns {HTMLElement} the container element
13+
*/
14+
getContainerElement(): HTMLElement {
15+
if (!this._containerElement) { this._createContainer(); }
16+
return this._containerElement;
17+
}
18+
19+
/**
20+
* Create the overlay container element, which is simply a div
21+
* with the 'md-overlay-container' class on the document body.
22+
*/
23+
private _createContainer(): void {
24+
let container = document.createElement('div');
25+
container.classList.add('md-overlay-container');
26+
document.body.appendChild(container);
27+
this._containerElement = container;
28+
}
1229
}

src/core/overlay/overlay-directives.spec.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@ import {
88
import {TestComponentBuilder, ComponentFixture} from '@angular/compiler/testing';
99
import {Component, ViewChild} from '@angular/core';
1010
import {ConnectedOverlayDirective, OverlayOrigin} from './overlay-directives';
11-
import {OVERLAY_CONTAINER_TOKEN, Overlay} from './overlay';
11+
import {Overlay} from './overlay';
12+
import {OverlayContainer} from './overlay-container';
1213
import {ViewportRuler} from './position/viewport-ruler';
1314
import {OverlayPositionBuilder} from './position/overlay-position-builder';
1415
import {ConnectedPositionStrategy} from './position/connected-position-strategy';
1516

16-
1717
describe('Overlay directives', () => {
1818
let builder: TestComponentBuilder;
1919
let overlayContainerElement: HTMLElement;
@@ -24,9 +24,13 @@ describe('Overlay directives', () => {
2424
Overlay,
2525
OverlayPositionBuilder,
2626
ViewportRuler,
27-
{provide: OVERLAY_CONTAINER_TOKEN, useFactory: () => {
28-
overlayContainerElement = document.createElement('div');
29-
return overlayContainerElement;
27+
{provide: OverlayContainer, useFactory: () => {
28+
return {
29+
getContainerElement: () => {
30+
overlayContainerElement = document.createElement('div');
31+
return overlayContainerElement;
32+
}
33+
};
3034
}},
3135
]);
3236
});

src/core/overlay/overlay.scss

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
// TODO(jelbourn): change from the `md` prefix to something else for everything in the toolkit.
22

3+
@import 'variables';
4+
35
/** The overlay-container is an invisible element which contains all individual overlays. */
46
.md-overlay-container {
57
position: absolute;
@@ -19,5 +21,5 @@
1921
position: absolute;
2022
pointer-events: auto;
2123
box-sizing: border-box;
22-
z-index: 1;
24+
z-index: $z-index-overlay;
2325
}

src/core/overlay/overlay.spec.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ import {
1212
} from '@angular/core';
1313
import {TemplatePortalDirective} from '../portal/portal-directives';
1414
import {TemplatePortal, ComponentPortal} from '../portal/portal';
15-
import {Overlay, OVERLAY_CONTAINER_TOKEN} from './overlay';
15+
import {Overlay} from './overlay';
16+
import {OverlayContainer} from './overlay-container';
1617
import {OverlayRef} from './overlay-ref';
1718
import {OverlayState} from './overlay-state';
1819
import {PositionStrategy} from './position/position-strategy';
@@ -32,9 +33,14 @@ describe('Overlay', () => {
3233
Overlay,
3334
OverlayPositionBuilder,
3435
ViewportRuler,
35-
{provide: OVERLAY_CONTAINER_TOKEN, useFactory: () => {
36-
overlayContainerElement = document.createElement('div');
37-
return overlayContainerElement;
36+
{provide: OverlayContainer, useFactory: () => {
37+
return {
38+
getContainerElement: () => {
39+
if (overlayContainerElement) { return overlayContainerElement; }
40+
overlayContainerElement = document.createElement('div');
41+
return overlayContainerElement;
42+
}
43+
};
3844
}}
3945
]);
4046
});

src/core/overlay/overlay.ts

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import {
22
ComponentResolver,
3-
OpaqueToken,
4-
Inject,
53
Injectable,
64
} from '@angular/core';
75
import {OverlayState} from './overlay-state';
@@ -10,10 +8,7 @@ import {OverlayRef} from './overlay-ref';
108

119
import {OverlayPositionBuilder} from './position/overlay-position-builder';
1210
import {ViewportRuler} from './position/viewport-ruler';
13-
14-
15-
/** Token used to inject the DOM element that serves as the overlay container. */
16-
export const OVERLAY_CONTAINER_TOKEN = new OpaqueToken('overlayContainer');
11+
import {OverlayContainer} from './overlay-container';
1712

1813
/** Next overlay unique ID. */
1914
let nextUniqueId = 0;
@@ -32,18 +27,9 @@ let defaultState = new OverlayState();
3227
*/
3328
@Injectable()
3429
export class Overlay {
35-
private _overlayContainerElement: HTMLElement;
36-
37-
constructor(
38-
@Inject(OVERLAY_CONTAINER_TOKEN) overlayContainerElement: any,
39-
private _componentResolver: ComponentResolver,
40-
private _positionBuilder: OverlayPositionBuilder) {
41-
42-
// We inject the container as `any` because the constructor signature cannot reference
43-
// browser globals (HTMLElement) on non-browser environments, since having a class decorator
44-
// causes TypeScript to preserve the constructor signature types.
45-
this._overlayContainerElement = overlayContainerElement;
46-
}
30+
constructor(private _overlayContainer: OverlayContainer,
31+
private _componentResolver: ComponentResolver,
32+
private _positionBuilder: OverlayPositionBuilder) {}
4733

4834
/**
4935
* Creates an overlay.
@@ -71,7 +57,7 @@ export class Overlay {
7157
pane.id = `md-overlay-${nextUniqueId++}`;
7258
pane.classList.add('md-overlay-pane');
7359

74-
this._overlayContainerElement.appendChild(pane);
60+
this._overlayContainer.getContainerElement().appendChild(pane);
7561

7662
return Promise.resolve(pane);
7763
}

src/core/style/_variables.scss

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@ $md-font-family: Roboto, 'Helvetica Neue', sans-serif !default;
88
// Media queries
99
$md-xsmall: 'max-width: 600px';
1010

11+
// TODO: Revisit all z-indices before beta
1112
// z-index master list
1213
$z-index-fab: 20 !default;
1314
$z-index-drawer: 100 !default;
15+
$z-index-overlay: 1000 !default;
1416

1517
// Global constants
1618
$pi: 3.14159265;

src/demo-app/main.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,10 @@ import {bootstrap} from '@angular/platform-browser-dynamic';
22
import {HAMMER_GESTURE_CONFIG} from '@angular/platform-browser';
33
import {HTTP_PROVIDERS} from '@angular/http';
44
import {disableDeprecatedForms, provideForms} from '@angular/forms';
5-
6-
import {OVERLAY_CONTAINER_TOKEN} from '@angular2-material/core/overlay/overlay';
75
import {MdLiveAnnouncer} from '@angular2-material/core/a11y/live-announcer';
8-
import {createOverlayContainer} from '@angular2-material/core/overlay/overlay-container';
96
import {MdGestureConfig} from '@angular2-material/core/gestures/MdGestureConfig';
107
import {MdIconRegistry} from '@angular2-material/icon/icon-registry';
11-
8+
import {OverlayContainer} from '@angular2-material/core/overlay/overlay-container';
129
import {DemoApp} from './demo-app/demo-app';
1310
import {DEMO_APP_ROUTE_PROVIDER} from './demo-app/routes';
1411

@@ -17,8 +14,8 @@ bootstrap(DemoApp, [
1714
disableDeprecatedForms(),
1815
provideForms(),
1916
MdLiveAnnouncer,
20-
{provide: OVERLAY_CONTAINER_TOKEN, useValue: createOverlayContainer()},
2117
HTTP_PROVIDERS,
18+
OverlayContainer,
2219
MdIconRegistry,
2320
{provide: HAMMER_GESTURE_CONFIG, useClass: MdGestureConfig},
2421
]);

src/e2e-app/main.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,10 @@ import {HAMMER_GESTURE_CONFIG} from '@angular/platform-browser';
33
import {HTTP_PROVIDERS} from '@angular/http';
44
import {Renderer} from '@angular/core';
55
import {disableDeprecatedForms, provideForms} from '@angular/forms';
6-
7-
import {OVERLAY_CONTAINER_TOKEN} from '@angular2-material/core/overlay/overlay';
86
import {MdLiveAnnouncer} from '@angular2-material/core/a11y/live-announcer';
9-
import {createOverlayContainer} from '@angular2-material/core/overlay/overlay-container';
107
import {MdGestureConfig} from '@angular2-material/core/gestures/MdGestureConfig';
118
import {MdIconRegistry} from '@angular2-material/icon/icon-registry';
12-
9+
import {OverlayContainer} from '@angular2-material/core/overlay/overlay-container';
1310
import {E2EApp} from './e2e-app/e2e-app';
1411
import {E2E_APP_ROUTE_PROVIDER} from './e2e-app/routes';
1512

@@ -18,7 +15,7 @@ bootstrap(E2EApp, [
1815
disableDeprecatedForms(),
1916
provideForms(),
2017
MdLiveAnnouncer,
21-
{provide: OVERLAY_CONTAINER_TOKEN, useValue: createOverlayContainer()},
18+
OverlayContainer,
2219
HTTP_PROVIDERS,
2320
MdIconRegistry,
2421
Renderer,

0 commit comments

Comments
 (0)