Skip to content

Commit

Permalink
feat(overlay): allow theming overlay-based components (#2967)
Browse files Browse the repository at this point in the history
Fixes #2662.
  • Loading branch information
crisbeto authored and kara committed Feb 27, 2017
1 parent 8482bbf commit cbd42f0
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 0 deletions.
18 changes: 18 additions & 0 deletions guides/theming.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,24 @@ Finally, if your app's content **is not** placed inside of a `md-sidenav-contain
need to add the `mat-app-background` class to your wrapper element (for example the `body`). This
ensures that the proper theme background is applied to your page.

#### Theming overlay-based components
Since certain components (e.g. `dialog`) are inside of a global overlay container, your theme may
not be applied to them. In order to define the theme that will be used for overlay components, you
have to specify it on the global `OverlayContainer` instance:

```ts
import {OverlayContainer} from '@angular/material';

@NgModule({
// misc config goes here
})
export class YourAppModule {
constructor(overlayContainer: OverlayContainer) {
overlayContainer.themeClass = 'your-theme';
}
}
```

### Defining a custom theme
When you want more customization than a pre-built theme offers, you can create your own theme file.

Expand Down
23 changes: 23 additions & 0 deletions src/lib/core/overlay/overlay-container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,24 @@ import {Injectable, Optional, SkipSelf} from '@angular/core';
export class OverlayContainer {
protected _containerElement: HTMLElement;

private _themeClass: string;

/**
* Base theme to be applied to all overlay-based components.
*/
get themeClass(): string { return this._themeClass; }
set themeClass(value: string) {
if (this._containerElement) {
this._containerElement.classList.remove(this._themeClass);

if (value) {
this._containerElement.classList.add(value);
}
}

this._themeClass = value;
}

/**
* This method returns the overlay container element. It will lazily
* create the element the first time it is called to facilitate using
Expand All @@ -27,6 +45,11 @@ export class OverlayContainer {
protected _createContainer(): void {
let container = document.createElement('div');
container.classList.add('cdk-overlay-container');

if (this._themeClass) {
container.classList.add(this._themeClass);
}

document.body.appendChild(container);
this._containerElement = container;
}
Expand Down
34 changes: 34 additions & 0 deletions src/lib/core/overlay/overlay.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,34 @@ describe('Overlay', () => {
});
});

describe('OverlayContainer theming', () => {
let overlayContainer: OverlayContainer;
let overlayContainerElement: HTMLElement;

beforeEach(async(() => {
TestBed.configureTestingModule({ imports: [OverlayContainerThemingTestModule] });
TestBed.compileComponents();
}));

beforeEach(inject([OverlayContainer], (o: OverlayContainer) => {
overlayContainer = o;
overlayContainerElement = overlayContainer.getContainerElement();
}));

it('should be able to set a theme on the overlay container', () => {
overlayContainer.themeClass = 'my-theme';
expect(overlayContainerElement.classList).toContain('my-theme');
});

it('should clear any previously-set themes when a new theme is set', () => {
overlayContainer.themeClass = 'initial-theme';
expect(overlayContainerElement.classList).toContain('initial-theme');

overlayContainer.themeClass = 'new-theme';
expect(overlayContainerElement.classList).not.toContain('initial-theme');
expect(overlayContainerElement.classList).toContain('new-theme');
});
});

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

/** Component for testing the overlay container theming. */
@NgModule({
imports: [OverlayModule, PortalModule],
})
class OverlayContainerThemingTestModule { }

class FakePositionStrategy implements PositionStrategy {
apply(element: Element): Promise<void> {
element.classList.add('fake-positioned');
Expand Down

0 comments on commit cbd42f0

Please sign in to comment.