Skip to content

Commit

Permalink
feat(snack-bar): add injection token for overriding the default optio…
Browse files Browse the repository at this point in the history
…ns (#9849)

Adds the `MAT_SNACK_BAR_DEFAULT_OPTIONS` injection token that allows consumers to set the default snack bar options globally.

Fixes #9821.
  • Loading branch information
crisbeto authored and tinayuangao committed Mar 2, 2018
1 parent ccbab87 commit a943b36
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 16 deletions.
17 changes: 15 additions & 2 deletions src/lib/snack-bar/snack-bar-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,17 @@ import {PortalModule} from '@angular/cdk/portal';
import {LIVE_ANNOUNCER_PROVIDER} from '@angular/cdk/a11y';
import {LayoutModule} from '@angular/cdk/layout';
import {MatCommonModule} from '@angular/material/core';
import {MatSnackBar} from './snack-bar';
import {MatSnackBar, MAT_SNACK_BAR_DEFAULT_OPTIONS} from './snack-bar';
import {MatSnackBarContainer} from './snack-bar-container';
import {MatSnackBarConfig} from './snack-bar-config';
import {SimpleSnackBar} from './simple-snack-bar';


/** @docs-private */
export function MAT_SNACK_BAR_DEFAULT_OPTIONS_PROVIDER_FACTORY() {
return new MatSnackBarConfig();
}

@NgModule({
imports: [
OverlayModule,
Expand All @@ -29,6 +35,13 @@ import {SimpleSnackBar} from './simple-snack-bar';
exports: [MatSnackBarContainer, MatCommonModule],
declarations: [MatSnackBarContainer, SimpleSnackBar],
entryComponents: [MatSnackBarContainer, SimpleSnackBar],
providers: [MatSnackBar, LIVE_ANNOUNCER_PROVIDER]
providers: [
MatSnackBar,
LIVE_ANNOUNCER_PROVIDER,
{
provide: MAT_SNACK_BAR_DEFAULT_OPTIONS,
useFactory: MAT_SNACK_BAR_DEFAULT_OPTIONS_PROVIDER_FACTORY
},
]
})
export class MatSnackBarModule {}
13 changes: 13 additions & 0 deletions src/lib/snack-bar/snack-bar.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,19 @@ export class MessageArchivedComponent {
constructor(@Inject(MAT_SNACK_BAR_DATA) public data: any) { }
}
```

### Setting the global configuration defaults
If you want to override the default snack bar options, you can do so using the
`MAT_SNACK_BAR_DEFAULT_OPTIONS` injection token.

```ts
@NgModule({
providers: [
{provide: MAT_SNACK_BAR_DEFAULT_OPTIONS, useValue: {duration: 2500}}
]
})
```

### Accessibility
Snack-bar messages are announced via an `aria-live` region. Focus is not moved to
the snack-bar element, as this would be disruptive to a user in the middle of a
Expand Down
28 changes: 27 additions & 1 deletion src/lib/snack-bar/snack-bar.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
MatSnackBarRef,
SimpleSnackBar,
MAT_SNACK_BAR_DATA,
MAT_SNACK_BAR_DEFAULT_OPTIONS,
} from './index';

describe('MatSnackBar', () => {
Expand Down Expand Up @@ -387,6 +388,32 @@ describe('MatSnackBar', () => {
expect(pane.getAttribute('dir')).toBe('rtl', 'Expected the pane to be in RTL mode.');
});

it('should be able to override the default config', fakeAsync(() => {
overlayContainer.ngOnDestroy();
viewContainerFixture.destroy();

TestBed
.resetTestingModule()
.overrideProvider(MAT_SNACK_BAR_DEFAULT_OPTIONS, {
deps: [],
useFactory: () => ({panelClass: 'custom-class'})
})
.configureTestingModule({imports: [MatSnackBarModule, NoopAnimationsModule]})
.compileComponents();

inject([MatSnackBar, OverlayContainer], (sb: MatSnackBar, oc: OverlayContainer) => {
snackBar = sb;
overlayContainer = oc;
overlayContainerElement = oc.getContainerElement();
})();

snackBar.open(simpleMessage);
flush();

expect(overlayContainerElement.querySelector('snack-bar-container')!.classList)
.toContain('custom-class', 'Expected class applied through the defaults to be applied.');
}));

describe('with custom component', () => {
it('should open a custom component', () => {
const snackBarRef = snackBar.openFromComponent(BurritosNotification);
Expand Down Expand Up @@ -504,7 +531,6 @@ describe('MatSnackBar with parent MatSnackBar', () => {
}));
});


describe('MatSnackBar Positioning', () => {
let snackBar: MatSnackBar;
let liveAnnouncer: LiveAnnouncer;
Expand Down
30 changes: 17 additions & 13 deletions src/lib/snack-bar/snack-bar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,15 @@ import {LiveAnnouncer} from '@angular/cdk/a11y';
import {BreakpointObserver, Breakpoints} from '@angular/cdk/layout';
import {Overlay, OverlayConfig, OverlayRef} from '@angular/cdk/overlay';
import {ComponentPortal, ComponentType, PortalInjector} from '@angular/cdk/portal';
import {ComponentRef, Injectable, Injector, Optional, SkipSelf} from '@angular/core';
import {
ComponentRef,
Inject,
Injectable,
Injector,
InjectionToken,
Optional,
SkipSelf,
} from '@angular/core';
import {take} from 'rxjs/operators/take';
import {takeUntil} from 'rxjs/operators/takeUntil';
import {SimpleSnackBar} from './simple-snack-bar';
Expand All @@ -19,6 +27,10 @@ import {MatSnackBarContainer} from './snack-bar-container';
import {MatSnackBarRef} from './snack-bar-ref';


/** Injection token that can be used to specify default snack bar. */
export const MAT_SNACK_BAR_DEFAULT_OPTIONS =
new InjectionToken<MatSnackBarConfig>('mat-snack-bar-default-options');

/**
* Service to dispatch Material Design snack bar messages.
*/
Expand Down Expand Up @@ -50,7 +62,8 @@ export class MatSnackBar {
private _live: LiveAnnouncer,
private _injector: Injector,
private _breakpointObserver: BreakpointObserver,
@Optional() @SkipSelf() private _parentSnackBar: MatSnackBar) {}
@Optional() @SkipSelf() private _parentSnackBar: MatSnackBar,
@Inject(MAT_SNACK_BAR_DEFAULT_OPTIONS) private _defaultConfig: MatSnackBarConfig) {}

/**
* Creates and dispatches a snack bar with a custom component for the content, removing any
Expand All @@ -60,7 +73,7 @@ export class MatSnackBar {
* @param config Extra configuration for the snack bar.
*/
openFromComponent<T>(component: ComponentType<T>, config?: MatSnackBarConfig): MatSnackBarRef<T> {
const _config = _applyConfigDefaults(config);
const _config = {...this._defaultConfig, ...config};
const snackBarRef = this._attach(component, _config);

// When the snackbar is dismissed, clear the reference to it.
Expand Down Expand Up @@ -104,7 +117,7 @@ export class MatSnackBar {
*/
open(message: string, action: string = '', config?: MatSnackBarConfig):
MatSnackBarRef<SimpleSnackBar> {
const _config = _applyConfigDefaults(config);
const _config = {...this._defaultConfig, ...config};

// Since the user doesn't have access to the component, we can
// override the data to pass in our own message and action.
Expand Down Expand Up @@ -216,12 +229,3 @@ export class MatSnackBar {
return new PortalInjector(userInjector || this._injector, injectionTokens);
}
}

/**
* Applies default options to the snackbar config.
* @param config The configuration to which the defaults will be applied.
* @returns The new configuration object with defaults applied.
*/
function _applyConfigDefaults(config?: MatSnackBarConfig): MatSnackBarConfig {
return {...new MatSnackBarConfig(), ...config};
}

0 comments on commit a943b36

Please sign in to comment.