Skip to content

Commit 3e6d57d

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 47cc025 commit 3e6d57d

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 `mat-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
@@ -272,6 +272,34 @@ describe('Overlay', () => {
272272
});
273273
});
274274

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

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

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

0 commit comments

Comments
 (0)