diff --git a/src/lib/bottom-sheet/bottom-sheet.md b/src/lib/bottom-sheet/bottom-sheet.md index 87625c1255d..963a510393c 100644 --- a/src/lib/bottom-sheet/bottom-sheet.md +++ b/src/lib/bottom-sheet/bottom-sheet.md @@ -81,6 +81,19 @@ inside your `NgModule`. export class AppModule {} ``` +### Specifying global configuration defaults +Default bottom sheet options can be specified by providing an instance of `MatBottomSheetConfig` +for `MAT_BOTTOM_SHEET_DEFAULT_OPTIONS` in your application's root module. + +```ts +@NgModule({ + providers: [ + {provide: MAT_BOTTOM_SHEET_DEFAULT_OPTIONS, useValue: {hasBackdrop: false}} + ] +}) +``` + + ### Accessibility By default, the bottom sheet has `role="dialog"` on the root element and can be labelled using the `ariaLabel` property on the `MatBottomSheetConfig`. diff --git a/src/lib/bottom-sheet/bottom-sheet.spec.ts b/src/lib/bottom-sheet/bottom-sheet.spec.ts index faa4e5b833c..4d3f076a8ff 100644 --- a/src/lib/bottom-sheet/bottom-sheet.spec.ts +++ b/src/lib/bottom-sheet/bottom-sheet.spec.ts @@ -25,7 +25,7 @@ import { import {Location} from '@angular/common'; import {SpyLocation} from '@angular/common/testing'; import {NoopAnimationsModule} from '@angular/platform-browser/animations'; -import {MatBottomSheet} from './bottom-sheet'; +import {MatBottomSheet, MAT_BOTTOM_SHEET_DEFAULT_OPTIONS} from './bottom-sheet'; import {MAT_BOTTOM_SHEET_DATA, MatBottomSheetConfig} from './bottom-sheet-config'; import {MatBottomSheetModule} from './bottom-sheet-module'; import {MatBottomSheetRef} from './bottom-sheet-ref'; @@ -616,6 +616,82 @@ describe('MatBottomSheet with parent MatBottomSheet', () => { })); }); +describe('MatBottomSheet with default options', () => { + let bottomSheet: MatBottomSheet; + let overlayContainer: OverlayContainer; + let overlayContainerElement: HTMLElement; + + let testViewContainerRef: ViewContainerRef; + let viewContainerFixture: ComponentFixture; + + beforeEach(fakeAsync(() => { + const defaultConfig: MatBottomSheetConfig = { + hasBackdrop: false, + disableClose: true, + autoFocus: false + }; + + TestBed.configureTestingModule({ + imports: [MatBottomSheetModule, BottomSheetTestModule], + providers: [ + {provide: MAT_BOTTOM_SHEET_DEFAULT_OPTIONS, useValue: defaultConfig}, + ], + }); + + TestBed.compileComponents(); + })); + + beforeEach(inject([MatBottomSheet, OverlayContainer], + (b: MatBottomSheet, oc: OverlayContainer) => { + bottomSheet = b; + overlayContainer = oc; + overlayContainerElement = oc.getContainerElement(); + })); + + afterEach(() => { + overlayContainer.ngOnDestroy(); + }); + + beforeEach(() => { + viewContainerFixture = TestBed.createComponent(ComponentWithChildViewContainer); + + viewContainerFixture.detectChanges(); + testViewContainerRef = viewContainerFixture.componentInstance.childViewContainer; + }); + + it('should use the provided defaults', () => { + bottomSheet.open(PizzaMsg, {viewContainerRef: testViewContainerRef}); + + viewContainerFixture.detectChanges(); + + expect(overlayContainerElement.querySelector('.cdk-overlay-backdrop')).toBeFalsy(); + + dispatchKeyboardEvent(document.body, 'keydown', ESCAPE); + + expect(overlayContainerElement.querySelector('mat-bottom-sheet-container')).toBeTruthy(); + expect(document.activeElement.tagName).not.toBe('INPUT'); + }); + + it('should be overridable by open() options', fakeAsync(() => { + bottomSheet.open(PizzaMsg, { + hasBackdrop: true, + disableClose: false, + viewContainerRef: testViewContainerRef + }); + + viewContainerFixture.detectChanges(); + + expect(overlayContainerElement.querySelector('.cdk-overlay-backdrop')).toBeTruthy(); + + dispatchKeyboardEvent(document.body, 'keydown', ESCAPE); + viewContainerFixture.detectChanges(); + flush(); + + expect(overlayContainerElement.querySelector('mat-bottom-sheet-container')).toBeFalsy(); + })); +}); + + @Directive({selector: 'dir-with-view-container'}) class DirectiveWithViewContainer { diff --git a/src/lib/bottom-sheet/bottom-sheet.ts b/src/lib/bottom-sheet/bottom-sheet.ts index 9222711e830..6a169a6e92b 100644 --- a/src/lib/bottom-sheet/bottom-sheet.ts +++ b/src/lib/bottom-sheet/bottom-sheet.ts @@ -9,7 +9,16 @@ import {Directionality} from '@angular/cdk/bidi'; import {Overlay, OverlayConfig, OverlayRef} from '@angular/cdk/overlay'; import {ComponentPortal, ComponentType, PortalInjector, TemplatePortal} from '@angular/cdk/portal'; -import {ComponentRef, Injectable, Injector, Optional, SkipSelf, TemplateRef} from '@angular/core'; +import { + ComponentRef, + Injectable, + Injector, + Optional, + SkipSelf, + TemplateRef, + InjectionToken, + Inject, +} from '@angular/core'; import {Location} from '@angular/common'; import {of as observableOf} from 'rxjs'; import {MAT_BOTTOM_SHEET_DATA, MatBottomSheetConfig} from './bottom-sheet-config'; @@ -18,6 +27,10 @@ import {MatBottomSheetModule} from './bottom-sheet-module'; import {MatBottomSheetRef} from './bottom-sheet-ref'; +/** Injection token that can be used to specify default bottom sheet options. */ +export const MAT_BOTTOM_SHEET_DEFAULT_OPTIONS = + new InjectionToken('mat-bottom-sheet-default-options'); + /** * Service to trigger Material Design bottom sheets. */ @@ -43,7 +56,9 @@ export class MatBottomSheet { private _overlay: Overlay, private _injector: Injector, @Optional() @SkipSelf() private _parentBottomSheet: MatBottomSheet, - @Optional() private _location?: Location) {} + @Optional() private _location?: Location, + @Optional() @Inject(MAT_BOTTOM_SHEET_DEFAULT_OPTIONS) + private _defaultOptions?: MatBottomSheetConfig) {} open(component: ComponentType, config?: MatBottomSheetConfig): MatBottomSheetRef; @@ -53,7 +68,8 @@ export class MatBottomSheet { open(componentOrTemplateRef: ComponentType | TemplateRef, config?: MatBottomSheetConfig): MatBottomSheetRef { - const _config = _applyConfigDefaults(config); + const _config = + _applyConfigDefaults(this._defaultOptions || new MatBottomSheetConfig(), config); const overlayRef = this._createOverlay(_config); const container = this._attachContainer(overlayRef, _config); const ref = new MatBottomSheetRef(container, overlayRef, this._location); @@ -171,9 +187,11 @@ export class MatBottomSheet { /** * Applies default options to the bottom sheet config. + * @param defaults Object containing the default values to which to fall back. * @param config The configuration to which the defaults will be applied. * @returns The new configuration object with defaults applied. */ -function _applyConfigDefaults(config?: MatBottomSheetConfig): MatBottomSheetConfig { - return {...new MatBottomSheetConfig(), ...config}; +function _applyConfigDefaults(defaults: MatBottomSheetConfig, + config?: MatBottomSheetConfig): MatBottomSheetConfig { + return {...defaults, ...config}; }