diff --git a/src/lib/core/option/option.spec.ts b/src/lib/core/option/option.spec.ts index f5951971043..d0a9c79470d 100644 --- a/src/lib/core/option/option.spec.ts +++ b/src/lib/core/option/option.spec.ts @@ -9,18 +9,18 @@ describe('MdOption component', () => { beforeEach(async(() => { TestBed.configureTestingModule({ imports: [MdOptionModule], - declarations: [OptionWithDisableRipple] + declarations: [OptionWithDisable] }).compileComponents(); })); describe('ripples', () => { - let fixture: ComponentFixture; + let fixture: ComponentFixture; let optionDebugElement: DebugElement; let optionNativeElement: HTMLElement; let optionInstance: MdOption; beforeEach(() => { - fixture = TestBed.createComponent(OptionWithDisableRipple); + fixture = TestBed.createComponent(OptionWithDisable); fixture.detectChanges(); optionDebugElement = fixture.debugElement.query(By.directive(MdOption)); @@ -58,7 +58,7 @@ describe('MdOption component', () => { expect(optionNativeElement.querySelectorAll('.mat-ripple-element').length) .toBe(0, 'Expected no ripples to show up initially'); - fixture.componentInstance.disableRipple = true; + optionInstance.disableRipple = true; fixture.detectChanges(); dispatchFakeEvent(optionNativeElement, 'mousedown'); @@ -73,9 +73,8 @@ describe('MdOption component', () => { }); @Component({ - template: `` + template: `` }) -export class OptionWithDisableRipple { - disableRipple: boolean; +export class OptionWithDisable { disabled: boolean; } diff --git a/src/lib/core/option/option.ts b/src/lib/core/option/option.ts index e09955c86cf..f1820d34658 100644 --- a/src/lib/core/option/option.ts +++ b/src/lib/core/option/option.ts @@ -22,7 +22,6 @@ import {ENTER, SPACE} from '../keyboard/keycodes'; import {coerceBooleanProperty} from '@angular/cdk/coercion'; import {MATERIAL_COMPATIBILITY_MODE} from '../../core/compatibility/compatibility'; import {MdOptgroup} from './optgroup'; -import {CanDisableRipple, mixinDisableRipple} from '../common-behaviors/disable-ripple'; /** * Option IDs need to be unique across components, so this counter exists outside of @@ -35,19 +34,12 @@ export class MdOptionSelectionChange { constructor(public source: MdOption, public isUserInput = false) { } } -// Boilerplate for applying mixins to MdOption. -/** @docs-private */ -export class MdOptionBase {} -export const _MdOptionMixinBase = mixinDisableRipple(MdOptionBase); - - /** * Single option inside of a `` element. */ @Component({ moduleId: module.id, selector: 'md-option, mat-option', - inputs: ['disableRipple'], host: { 'role': 'option', '[attr.tabindex]': '_getTabIndex()', @@ -66,10 +58,11 @@ export const _MdOptionMixinBase = mixinDisableRipple(MdOptionBase); encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, }) -export class MdOption extends _MdOptionMixinBase implements CanDisableRipple { +export class MdOption { private _selected: boolean = false; private _active: boolean = false; private _multiple: boolean = false; + private _disableRipple: boolean = false; /** Whether the option is disabled. */ private _disabled: boolean = false; @@ -99,6 +92,13 @@ export class MdOption extends _MdOptionMixinBase implements CanDisableRipple { get disabled() { return (this.group && this.group.disabled) || this._disabled; } set disabled(value: any) { this._disabled = coerceBooleanProperty(value); } + /** Whether ripples for the option are disabled. */ + get disableRipple() { return this._disableRipple; } + set disableRipple(value: boolean) { + this._disableRipple = value; + this._changeDetectorRef.markForCheck(); + } + /** Event emitted when the option is selected or deselected. */ @Output() onSelectionChange = new EventEmitter(); @@ -106,10 +106,7 @@ export class MdOption extends _MdOptionMixinBase implements CanDisableRipple { private _element: ElementRef, private _changeDetectorRef: ChangeDetectorRef, @Optional() public readonly group: MdOptgroup, - @Optional() @Inject(MATERIAL_COMPATIBILITY_MODE) public _isCompatibilityMode: boolean - ) { - super(); - } + @Optional() @Inject(MATERIAL_COMPATIBILITY_MODE) public _isCompatibilityMode: boolean) {} /** * Whether or not the option is currently active and ready to be selected. diff --git a/src/lib/select/select.spec.ts b/src/lib/select/select.spec.ts index 14a97523536..2b00b161f9d 100644 --- a/src/lib/select/select.spec.ts +++ b/src/lib/select/select.spec.ts @@ -308,6 +308,18 @@ describe('MdSelect', () => { expect(event.defaultPrevented).toBe(true); }); + it('should update disableRipple properly on each option', () => { + const options = fixture.componentInstance.options.toArray(); + + expect(options.every(option => option.disableRipple === false)) + .toBeTruthy('Expected all options to have disableRipple set to false initially.'); + + fixture.componentInstance.disableRipple = true; + fixture.detectChanges(); + + expect(options.every(option => option.disableRipple === true)) + .toBeTruthy('Expected all options to have disableRipple set to true.'); + }); }); describe('selection logic', () => { @@ -924,7 +936,6 @@ describe('MdSelect', () => { }); }); })); - }); describe('misc forms', () => { @@ -2660,7 +2671,7 @@ describe('MdSelect', () => {
+ [panelClass]="panelClass" [disableRipple]="disableRipple"> {{ food.viewValue }} @@ -2687,6 +2698,7 @@ class BasicSelect { ariaLabel: string; ariaLabelledby: string; panelClass = ['custom-one', 'custom-two']; + disableRipple: boolean; @ViewChild(MdSelect) select: MdSelect; @ViewChildren(MdOption) options: QueryList; diff --git a/src/lib/select/select.ts b/src/lib/select/select.ts index 446e8a13129..989d79db053 100644 --- a/src/lib/select/select.ts +++ b/src/lib/select/select.ts @@ -344,6 +344,15 @@ export class MdSelect extends _MdSelectMixinBase implements AfterContentInit, On } private _value: any; + /** Whether ripples for all options in the select are disabled. */ + @Input() + get disableRipple(): boolean { return this._disableRipple; } + set disableRipple(value: boolean) { + this._disableRipple = coerceBooleanProperty(value); + this._setOptionDisableRipple(); + } + private _disableRipple: boolean = false; + /** Aria label of the select. If not specified, the placeholder will be used as label. */ @Input('aria-label') ariaLabel: string = ''; @@ -719,6 +728,7 @@ export class MdSelect extends _MdSelectMixinBase implements AfterContentInit, On this._listenToOptions(); this._setOptionIds(); this._setOptionMultiple(); + this._setOptionDisableRipple(); } /** Listens to user-generated selection events on each option. */ @@ -815,6 +825,12 @@ export class MdSelect extends _MdSelectMixinBase implements AfterContentInit, On } } + /** Sets the `disableRipple` property on each option. */ + private _setOptionDisableRipple() { + if (this.options) { + this.options.forEach(option => option.disableRipple = this.disableRipple); + } + } /** * Must set the width of the selected option's value programmatically * because it is absolutely positioned and otherwise will not clip