From d1ac9cd5cd537fc03c7bdf857a3b9c63d36b191c Mon Sep 17 00:00:00 2001 From: crisbeto Date: Thu, 8 Feb 2018 21:26:01 +0100 Subject: [PATCH] feat(snack-bar): add injection token for overriding the default options Adds the `MAT_SNACK_BAR_DEFAULT_OPTIONS` injection token that allows consumers to set the default snack bar options globally. Fixes #9821. --- src/lib/snack-bar/snack-bar-module.ts | 17 +++++++++++++-- src/lib/snack-bar/snack-bar.md | 13 ++++++++++++ src/lib/snack-bar/snack-bar.spec.ts | 28 ++++++++++++++++++++++++- src/lib/snack-bar/snack-bar.ts | 30 +++++++++++++++------------ 4 files changed, 72 insertions(+), 16 deletions(-) diff --git a/src/lib/snack-bar/snack-bar-module.ts b/src/lib/snack-bar/snack-bar-module.ts index 79cf441daa02..650caa205d42 100644 --- a/src/lib/snack-bar/snack-bar-module.ts +++ b/src/lib/snack-bar/snack-bar-module.ts @@ -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, @@ -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 {} diff --git a/src/lib/snack-bar/snack-bar.md b/src/lib/snack-bar/snack-bar.md index eee50987e6a8..eef66603ff2b 100644 --- a/src/lib/snack-bar/snack-bar.md +++ b/src/lib/snack-bar/snack-bar.md @@ -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 diff --git a/src/lib/snack-bar/snack-bar.spec.ts b/src/lib/snack-bar/snack-bar.spec.ts index 3b6d5ef9834c..a939a797da22 100644 --- a/src/lib/snack-bar/snack-bar.spec.ts +++ b/src/lib/snack-bar/snack-bar.spec.ts @@ -18,6 +18,7 @@ import { MatSnackBarRef, SimpleSnackBar, MAT_SNACK_BAR_DATA, + MAT_SNACK_BAR_DEFAULT_OPTIONS, } from './index'; describe('MatSnackBar', () => { @@ -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); @@ -504,7 +531,6 @@ describe('MatSnackBar with parent MatSnackBar', () => { })); }); - describe('MatSnackBar Positioning', () => { let snackBar: MatSnackBar; let liveAnnouncer: LiveAnnouncer; diff --git a/src/lib/snack-bar/snack-bar.ts b/src/lib/snack-bar/snack-bar.ts index 16c101d9557f..c5c2d38ce9ac 100644 --- a/src/lib/snack-bar/snack-bar.ts +++ b/src/lib/snack-bar/snack-bar.ts @@ -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'; @@ -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('mat-snack-bar-default-options'); + /** * Service to dispatch Material Design snack bar messages. */ @@ -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 @@ -60,7 +73,7 @@ export class MatSnackBar { * @param config Extra configuration for the snack bar. */ openFromComponent(component: ComponentType, config?: MatSnackBarConfig): MatSnackBarRef { - 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. @@ -104,7 +117,7 @@ export class MatSnackBar { */ open(message: string, action: string = '', config?: MatSnackBarConfig): MatSnackBarRef { - 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. @@ -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}; -}