Skip to content

Commit bdad0d6

Browse files
committed
feat(overlay): allow theming overlay-based components
Allows for a `themeClass` to be passed to overlay-based components which don't usually inherit their parent theme, because they're outside the DOM order. Fixes #2662.
1 parent 2f10a95 commit bdad0d6

File tree

3 files changed

+75
-0
lines changed

3 files changed

+75
-0
lines changed

guides/theming.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,24 @@ Finally, if your app's content **is not** placed inside of a `md-sidenav-contain
4444
need to add the `md-app-background` class to your wrapper element (for example the `body`). This
4545
ensures that the proper theme background is applied to your page.
4646

47+
#### Theming overlay-based components
48+
Since certain components (e.g. `dialog`) are inside of a global overlay container, your theme may
49+
not be applied to them. In order to define the theme, that will be used for overlay components, you
50+
have to specify it on the global `OverlayContainer` instance:
51+
52+
```ts
53+
import {OverlayContainer} from '@angular/material';
54+
55+
@NgModule({
56+
// misc config goes here
57+
})
58+
export class YourAppModule {
59+
constructor(overlayContainer: OverlayContainer) {
60+
overlayContainer.themeClass = 'your-theme';
61+
}
62+
}
63+
```
64+
4765
### Defining a custom theme
4866
When you want more customization than a pre-built theme offers, you can create your own theme file.
4967

src/lib/core/overlay/overlay-container.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,24 @@ import {Injectable, Optional, SkipSelf} from '@angular/core';
99
export class OverlayContainer {
1010
protected _containerElement: HTMLElement;
1111

12+
private _themeClass: string;
13+
14+
/**
15+
* Base theme to be applied to all overlay-based components.
16+
*/
17+
get themeClass(): string { return this._themeClass; }
18+
set themeClass(value: string) {
19+
if (this._containerElement) {
20+
this._containerElement.classList.remove(this._themeClass);
21+
22+
if (value) {
23+
this._containerElement.classList.add(value);
24+
}
25+
}
26+
27+
this._themeClass = value;
28+
}
29+
1230
/**
1331
* This method returns the overlay container element. It will lazily
1432
* create the element the first time it is called to facilitate using
@@ -27,6 +45,11 @@ export class OverlayContainer {
2745
protected _createContainer(): void {
2846
let container = document.createElement('div');
2947
container.classList.add('cdk-overlay-container');
48+
49+
if (this._themeClass) {
50+
container.classList.add(this._themeClass);
51+
}
52+
3053
document.body.appendChild(container);
3154
this._containerElement = container;
3255
}

src/lib/core/overlay/overlay.spec.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,34 @@ describe('Overlay', () => {
271271
});
272272
});
273273

274+
describe('OverlayContainer theming', () => {
275+
let overlayContainer: OverlayContainer;
276+
let overlayContainerElement: HTMLElement;
277+
278+
beforeEach(async(() => {
279+
TestBed.configureTestingModule({ imports: [OverlayContainerThemingTestModule] });
280+
TestBed.compileComponents();
281+
}));
282+
283+
beforeEach(inject([OverlayContainer], (o: OverlayContainer) => {
284+
overlayContainer = o;
285+
overlayContainerElement = overlayContainer.getContainerElement();
286+
}));
287+
288+
it('should be able to set a theme on the overlay container', () => {
289+
overlayContainer.themeClass = 'my-theme';
290+
expect(overlayContainerElement.classList).toContain('my-theme');
291+
});
292+
293+
it('should clear any previously-set themes when a new theme is set', () => {
294+
overlayContainer.themeClass = 'initial-theme';
295+
expect(overlayContainerElement.classList).toContain('initial-theme');
296+
297+
overlayContainer.themeClass = 'new-theme';
298+
expect(overlayContainerElement.classList).not.toContain('initial-theme');
299+
expect(overlayContainerElement.classList).toContain('new-theme');
300+
});
301+
});
274302

275303
/** Simple component for testing ComponentPortal. */
276304
@Component({template: '<p>Pizza</p>'})
@@ -296,6 +324,12 @@ const TEST_COMPONENTS = [PizzaMsg, TestComponentWithTemplatePortals];
296324
})
297325
class OverlayTestModule { }
298326

327+
/** Component for testing the overlay container theming. */
328+
@NgModule({
329+
imports: [OverlayModule, PortalModule],
330+
})
331+
class OverlayContainerThemingTestModule { }
332+
299333
class FakePositionStrategy implements PositionStrategy {
300334
apply(element: Element): Promise<void> {
301335
element.classList.add('fake-positioned');

0 commit comments

Comments
 (0)