Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(overlay): allow theming overlay-based components #2967

Merged
merged 2 commits into from
Feb 27, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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