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

fix: disable ripples when parent component is disabled #1778

Merged
merged 1 commit into from
Nov 9, 2016
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/lib/button/button.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<span class="md-button-wrapper"><ng-content></ng-content></span>
<div md-ripple *ngIf="isRippleEnabled()" class="md-button-ripple"
<div md-ripple *ngIf="!_isRippleDisabled()" class="md-button-ripple"
[class.md-button-ripple-round]="isRoundButton()"
[md-ripple-trigger]="getHostElement()"
[md-ripple-color]="isRoundButton() ? 'rgba(255, 255, 255, 0.2)' : ''"
Expand Down
43 changes: 33 additions & 10 deletions src/lib/button/button.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
import {
async,
TestBed,
} from '@angular/core/testing';
import {async, TestBed, ComponentFixture} from '@angular/core/testing';
import {Component} from '@angular/core';
import {By} from '@angular/platform-browser';
import {MdButtonModule} from './button';
Expand Down Expand Up @@ -124,17 +121,43 @@ describe('MdButton', () => {

// Ripple tests.
describe('button ripples', () => {
it('should remove ripple if md-ripple-disabled input is set', () => {
let fixture = TestBed.createComponent(TestApp);
let testComponent = fixture.debugElement.componentInstance;
let buttonDebugElement = fixture.debugElement.query(By.css('button'));
let fixture: ComponentFixture<TestApp>;
let testComponent: TestApp;
let buttonElement: HTMLButtonElement;
let anchorElement: HTMLAnchorElement;

beforeEach(() => {
fixture = TestBed.createComponent(TestApp);
fixture.detectChanges();
expect(buttonDebugElement.nativeElement.querySelectorAll('[md-ripple]').length).toBe(1);

testComponent = fixture.componentInstance;
buttonElement = fixture.nativeElement.querySelector('button[md-button]');
anchorElement = fixture.nativeElement.querySelector('a[md-button]');
});

it('should remove ripple if md-ripple-disabled input is set', () => {
expect(buttonElement.querySelectorAll('[md-ripple]').length).toBe(1);

testComponent.rippleDisabled = true;
fixture.detectChanges();
expect(buttonDebugElement.nativeElement.querySelectorAll('[md-ripple]').length).toBe(0);
expect(buttonElement.querySelectorAll('[md-ripple]').length).toBe(0);
});

it('should not have a ripple when the button is disabled', () => {
let buttonRipple = buttonElement.querySelector('[md-ripple]');
let anchorRipple = anchorElement.querySelector('[md-ripple]');

expect(buttonRipple).toBeTruthy('Expected an enabled button[md-button] to have a ripple');
expect(anchorRipple).toBeTruthy('Expected an enabled a[md-button] to have a ripple');

testComponent.isDisabled = true;
fixture.detectChanges();

buttonRipple = buttonElement.querySelector('button [md-ripple]');
anchorRipple = anchorElement.querySelector('a [md-ripple]');

expect(buttonRipple).toBeFalsy('Expected a disabled button[md-button] not to have a ripple');
expect(anchorRipple).toBeFalsy('Expected a disabled a[md-button] not to have a ripple');
});
});
});
Expand Down
24 changes: 10 additions & 14 deletions src/lib/button/button.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {MdRippleModule, coerceBooleanProperty} from '../core';
selector: 'button[md-button], button[md-raised-button], button[md-icon-button], ' +
'button[md-fab], button[md-mini-fab]',
host: {
'[attr.disabled]': 'disabled',
'[class.md-button-focus]': '_isKeyboardFocused',
'(mousedown)': '_setMousedown()',
'(focus)': '_setKeyboardFocus()',
Expand All @@ -42,11 +43,16 @@ export class MdButton {

/** Whether the ripple effect on click should be disabled. */
private _disableRipple: boolean = false;
private _disabled: boolean = false;

@Input()
get disableRipple() { return this._disableRipple; }
set disableRipple(v) { this._disableRipple = coerceBooleanProperty(v); }

@Input()
get disabled() { return this._disabled; }
set disabled(value: boolean) { this._disabled = coerceBooleanProperty(value); }

constructor(private _elementRef: ElementRef, private _renderer: Renderer) { }

@Input()
Expand Down Expand Up @@ -103,16 +109,17 @@ export class MdButton {
el.hasAttribute('md-mini-fab');
}

isRippleEnabled() {
return !this.disableRipple;
_isRippleDisabled() {
return this.disableRipple || this.disabled;
}
}

@Component({
moduleId: module.id,
selector: 'a[md-button], a[md-raised-button], a[md-icon-button], a[md-fab], a[md-mini-fab]',
inputs: ['color'],
inputs: ['color', 'disabled', 'disableRipple'],
host: {
'[attr.disabled]': 'disabled',
'[class.md-button-focus]': '_isKeyboardFocused',
'(mousedown)': '_setMousedown()',
'(focus)': '_setKeyboardFocus()',
Expand All @@ -124,8 +131,6 @@ export class MdButton {
encapsulation: ViewEncapsulation.None
})
export class MdAnchor extends MdButton {
_disabled: boolean = null;

constructor(elementRef: ElementRef, renderer: Renderer) {
super(elementRef, renderer);
}
Expand All @@ -141,15 +146,6 @@ export class MdAnchor extends MdButton {
return this.disabled ? 'true' : 'false';
}

@HostBinding('attr.disabled')
@Input('disabled')
get disabled() { return this._disabled; }

set disabled(value: boolean) {
// The presence of *any* disabled value makes the component disabled, *except* for false.
this._disabled = (value != null && value != false) ? true : null;
}

_haltDisabledEvents(event: Event) {
// A disabled button shouldn't apply any actions
if (this.disabled) {
Expand Down
2 changes: 1 addition & 1 deletion src/lib/checkbox/checkbox.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
(blur)="_onInputBlur()"
(change)="_onInteractionEvent($event)"
(click)="_onInputClick($event)">
<div md-ripple *ngIf="!disableRipple" class="md-checkbox-ripple"
<div md-ripple *ngIf="!_isRippleDisabled()" class="md-checkbox-ripple"
[md-ripple-trigger]="getHostElement()"
[md-ripple-centered]="true"
[md-ripple-speed-factor]="0.3"
Expand Down
11 changes: 11 additions & 0 deletions src/lib/checkbox/checkbox.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,17 @@ describe('MdCheckbox', () => {
expect(inputElement.disabled).toBe(false);
});

it('should not have a ripple when disabled', () => {
let rippleElement = checkboxNativeElement.querySelector('[md-ripple]');
expect(rippleElement).toBeTruthy('Expected an enabled checkbox to have a ripple');

testComponent.isDisabled = true;
fixture.detectChanges();

rippleElement = checkboxNativeElement.querySelector('[md-ripple]');
expect(rippleElement).toBeFalsy('Expected a disabled checkbox not to have a ripple');
});

it('should not toggle `checked` state upon interation while disabled', () => {
testComponent.isDisabled = true;
fixture.detectChanges();
Expand Down
4 changes: 4 additions & 0 deletions src/lib/checkbox/checkbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,10 @@ export class MdCheckbox implements ControlValueAccessor {
}
}

_isRippleDisabled() {
return this.disableRipple || this.disabled;
}

/**
* Implemented as part of ControlValueAccessor.
* TODO: internal
Expand Down
2 changes: 1 addition & 1 deletion src/lib/radio/radio.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<div class="md-radio-container">
<div class="md-radio-outer-circle"></div>
<div class="md-radio-inner-circle"></div>
<div md-ripple *ngIf="!disableRipple" class="md-radio-ripple"
<div md-ripple *ngIf="!_isRippleDisabled()" class="md-radio-ripple"
[md-ripple-trigger]="getHostElement()"
[md-ripple-centered]="true"
[md-ripple-speed-factor]="0.3"
Expand Down
11 changes: 11 additions & 0 deletions src/lib/radio/radio.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,17 @@ describe('MdRadio', () => {
expect(radioInstances.every(radio => !radio.checked)).toBe(true);
});

it('should not have a ripple on disabled radio buttons', () => {
let rippleElement = radioNativeElements[0].querySelector('[md-ripple]');
expect(rippleElement).toBeTruthy('Expected an enabled radio button to have a ripple');

radioInstances[0].disabled = true;
fixture.detectChanges();

rippleElement = radioNativeElements[0].querySelector('[md-ripple]');
expect(rippleElement).toBeFalsy('Expected a disabled radio button not to have a ripple');
});

it('should remove ripple if md-ripple-disabled input is set', async(() => {
fixture.detectChanges();
for (let radioNativeElement of radioNativeElements)
Expand Down
4 changes: 4 additions & 0 deletions src/lib/radio/radio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,10 @@ export class MdRadioButton implements OnInit {
this.change.emit(event);
}

_isRippleDisabled() {
return this.disableRipple || this.disabled;
}

/**
* We use a hidden native input field to handle changes to focus state via keyboard navigation,
* with visual rendering done separately. The native element is kept in sync with the overall
Expand Down