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(select): allow disabling ripples for options #5967

Merged
merged 1 commit into from
Jul 28, 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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 6 additions & 7 deletions src/lib/core/option/option.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,18 @@ describe('MdOption component', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [MdOptionModule],
declarations: [OptionWithDisableRipple]
declarations: [OptionWithDisable]
}).compileComponents();
}));

describe('ripples', () => {
let fixture: ComponentFixture<OptionWithDisableRipple>;
let fixture: ComponentFixture<OptionWithDisable>;
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));
Expand Down Expand Up @@ -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');
Expand All @@ -73,9 +73,8 @@ describe('MdOption component', () => {
});

@Component({
template: `<md-option [disableRipple]="disableRipple" [disabled]="disabled"></md-option>`
template: `<md-option [disabled]="disabled"></md-option>`
})
export class OptionWithDisableRipple {
disableRipple: boolean;
export class OptionWithDisable {
disabled: boolean;
}
23 changes: 10 additions & 13 deletions src/lib/core/option/option.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 `<md-select>` element.
*/
@Component({
moduleId: module.id,
selector: 'md-option, mat-option',
inputs: ['disableRipple'],
host: {
'role': 'option',
'[attr.tabindex]': '_getTabIndex()',
Expand All @@ -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;
Expand Down Expand Up @@ -99,17 +92,21 @@ 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<MdOptionSelectionChange>();

constructor(
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.
Expand Down
16 changes: 14 additions & 2 deletions src/lib/select/select.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can be reduced to option => option.disableRipple.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True. But I want to make it clear that we are checking for a truthy value here.

.toBeTruthy('Expected all options to have disableRipple set to true.');
});
});

describe('selection logic', () => {
Expand Down Expand Up @@ -924,7 +936,6 @@ describe('MdSelect', () => {
});
});
}));

});

describe('misc forms', () => {
Expand Down Expand Up @@ -2660,7 +2671,7 @@ describe('MdSelect', () => {
<div [style.height.px]="heightAbove"></div>
<md-select placeholder="Food" [formControl]="control" [required]="isRequired"
[tabIndex]="tabIndexOverride" [aria-label]="ariaLabel" [aria-labelledby]="ariaLabelledby"
[panelClass]="panelClass">
[panelClass]="panelClass" [disableRipple]="disableRipple">
<md-option *ngFor="let food of foods" [value]="food.value" [disabled]="food.disabled">
{{ food.viewValue }}
</md-option>
Expand All @@ -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<MdOption>;
Expand Down
16 changes: 16 additions & 0 deletions src/lib/select/select.ts
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,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 = '';

Expand Down Expand Up @@ -708,6 +717,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. */
Expand Down Expand Up @@ -804,6 +814,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
Expand Down