Skip to content

Commit 6b9e11c

Browse files
authored
fix: disable ripples when parent component is disabled (#1778)
1 parent 187d141 commit 6b9e11c

File tree

9 files changed

+76
-27
lines changed

9 files changed

+76
-27
lines changed

src/lib/button/button.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<span class="md-button-wrapper"><ng-content></ng-content></span>
2-
<div md-ripple *ngIf="isRippleEnabled()" class="md-button-ripple"
2+
<div md-ripple *ngIf="!_isRippleDisabled()" class="md-button-ripple"
33
[class.md-button-ripple-round]="isRoundButton()"
44
[md-ripple-trigger]="getHostElement()"
55
[md-ripple-color]="isRoundButton() ? 'rgba(255, 255, 255, 0.2)' : ''"

src/lib/button/button.spec.ts

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
import {
2-
async,
3-
TestBed,
4-
} from '@angular/core/testing';
1+
import {async, TestBed, ComponentFixture} from '@angular/core/testing';
52
import {Component} from '@angular/core';
63
import {By} from '@angular/platform-browser';
74
import {MdButtonModule} from './button';
@@ -124,17 +121,43 @@ describe('MdButton', () => {
124121

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

129+
beforeEach(() => {
130+
fixture = TestBed.createComponent(TestApp);
132131
fixture.detectChanges();
133-
expect(buttonDebugElement.nativeElement.querySelectorAll('[md-ripple]').length).toBe(1);
132+
133+
testComponent = fixture.componentInstance;
134+
buttonElement = fixture.nativeElement.querySelector('button[md-button]');
135+
anchorElement = fixture.nativeElement.querySelector('a[md-button]');
136+
});
137+
138+
it('should remove ripple if md-ripple-disabled input is set', () => {
139+
expect(buttonElement.querySelectorAll('[md-ripple]').length).toBe(1);
134140

135141
testComponent.rippleDisabled = true;
136142
fixture.detectChanges();
137-
expect(buttonDebugElement.nativeElement.querySelectorAll('[md-ripple]').length).toBe(0);
143+
expect(buttonElement.querySelectorAll('[md-ripple]').length).toBe(0);
144+
});
145+
146+
it('should not have a ripple when the button is disabled', () => {
147+
let buttonRipple = buttonElement.querySelector('[md-ripple]');
148+
let anchorRipple = anchorElement.querySelector('[md-ripple]');
149+
150+
expect(buttonRipple).toBeTruthy('Expected an enabled button[md-button] to have a ripple');
151+
expect(anchorRipple).toBeTruthy('Expected an enabled a[md-button] to have a ripple');
152+
153+
testComponent.isDisabled = true;
154+
fixture.detectChanges();
155+
156+
buttonRipple = buttonElement.querySelector('button [md-ripple]');
157+
anchorRipple = anchorElement.querySelector('a [md-ripple]');
158+
159+
expect(buttonRipple).toBeFalsy('Expected a disabled button[md-button] not to have a ripple');
160+
expect(anchorRipple).toBeFalsy('Expected a disabled a[md-button] not to have a ripple');
138161
});
139162
});
140163
});

src/lib/button/button.ts

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {MdRippleModule, coerceBooleanProperty} from '../core';
2121
selector: 'button[md-button], button[md-raised-button], button[md-icon-button], ' +
2222
'button[md-fab], button[md-mini-fab]',
2323
host: {
24+
'[attr.disabled]': 'disabled',
2425
'[class.md-button-focus]': '_isKeyboardFocused',
2526
'(mousedown)': '_setMousedown()',
2627
'(focus)': '_setKeyboardFocus()',
@@ -42,11 +43,16 @@ export class MdButton {
4243

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

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

52+
@Input()
53+
get disabled() { return this._disabled; }
54+
set disabled(value: boolean) { this._disabled = coerceBooleanProperty(value); }
55+
5056
constructor(private _elementRef: ElementRef, private _renderer: Renderer) { }
5157

5258
@Input()
@@ -103,16 +109,17 @@ export class MdButton {
103109
el.hasAttribute('md-mini-fab');
104110
}
105111

106-
isRippleEnabled() {
107-
return !this.disableRipple;
112+
_isRippleDisabled() {
113+
return this.disableRipple || this.disabled;
108114
}
109115
}
110116

111117
@Component({
112118
moduleId: module.id,
113119
selector: 'a[md-button], a[md-raised-button], a[md-icon-button], a[md-fab], a[md-mini-fab]',
114-
inputs: ['color'],
120+
inputs: ['color', 'disabled', 'disableRipple'],
115121
host: {
122+
'[attr.disabled]': 'disabled',
116123
'[class.md-button-focus]': '_isKeyboardFocused',
117124
'(mousedown)': '_setMousedown()',
118125
'(focus)': '_setKeyboardFocus()',
@@ -124,8 +131,6 @@ export class MdButton {
124131
encapsulation: ViewEncapsulation.None
125132
})
126133
export class MdAnchor extends MdButton {
127-
_disabled: boolean = null;
128-
129134
constructor(elementRef: ElementRef, renderer: Renderer) {
130135
super(elementRef, renderer);
131136
}
@@ -141,15 +146,6 @@ export class MdAnchor extends MdButton {
141146
return this.disabled ? 'true' : 'false';
142147
}
143148

144-
@HostBinding('attr.disabled')
145-
@Input('disabled')
146-
get disabled() { return this._disabled; }
147-
148-
set disabled(value: boolean) {
149-
// The presence of *any* disabled value makes the component disabled, *except* for false.
150-
this._disabled = (value != null && value != false) ? true : null;
151-
}
152-
153149
_haltDisabledEvents(event: Event) {
154150
// A disabled button shouldn't apply any actions
155151
if (this.disabled) {

src/lib/checkbox/checkbox.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
(blur)="_onInputBlur()"
1515
(change)="_onInteractionEvent($event)"
1616
(click)="_onInputClick($event)">
17-
<div md-ripple *ngIf="!disableRipple" class="md-checkbox-ripple"
17+
<div md-ripple *ngIf="!_isRippleDisabled()" class="md-checkbox-ripple"
1818
[md-ripple-trigger]="getHostElement()"
1919
[md-ripple-centered]="true"
2020
[md-ripple-speed-factor]="0.3"

src/lib/checkbox/checkbox.spec.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,17 @@ describe('MdCheckbox', () => {
153153
expect(inputElement.disabled).toBe(false);
154154
});
155155

156+
it('should not have a ripple when disabled', () => {
157+
let rippleElement = checkboxNativeElement.querySelector('[md-ripple]');
158+
expect(rippleElement).toBeTruthy('Expected an enabled checkbox to have a ripple');
159+
160+
testComponent.isDisabled = true;
161+
fixture.detectChanges();
162+
163+
rippleElement = checkboxNativeElement.querySelector('[md-ripple]');
164+
expect(rippleElement).toBeFalsy('Expected a disabled checkbox not to have a ripple');
165+
});
166+
156167
it('should not toggle `checked` state upon interation while disabled', () => {
157168
testComponent.isDisabled = true;
158169
fixture.detectChanges();

src/lib/checkbox/checkbox.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,10 @@ export class MdCheckbox implements ControlValueAccessor {
217217
}
218218
}
219219

220+
_isRippleDisabled() {
221+
return this.disableRipple || this.disabled;
222+
}
223+
220224
/**
221225
* Implemented as part of ControlValueAccessor.
222226
* TODO: internal

src/lib/radio/radio.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<div class="md-radio-container">
66
<div class="md-radio-outer-circle"></div>
77
<div class="md-radio-inner-circle"></div>
8-
<div md-ripple *ngIf="!disableRipple" class="md-radio-ripple"
8+
<div md-ripple *ngIf="!_isRippleDisabled()" class="md-radio-ripple"
99
[md-ripple-trigger]="getHostElement()"
1010
[md-ripple-centered]="true"
1111
[md-ripple-speed-factor]="0.3"

src/lib/radio/radio.spec.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,17 @@ describe('MdRadio', () => {
218218
expect(radioInstances.every(radio => !radio.checked)).toBe(true);
219219
});
220220

221+
it('should not have a ripple on disabled radio buttons', () => {
222+
let rippleElement = radioNativeElements[0].querySelector('[md-ripple]');
223+
expect(rippleElement).toBeTruthy('Expected an enabled radio button to have a ripple');
224+
225+
radioInstances[0].disabled = true;
226+
fixture.detectChanges();
227+
228+
rippleElement = radioNativeElements[0].querySelector('[md-ripple]');
229+
expect(rippleElement).toBeFalsy('Expected a disabled radio button not to have a ripple');
230+
});
231+
221232
it('should remove ripple if md-ripple-disabled input is set', async(() => {
222233
fixture.detectChanges();
223234
for (let radioNativeElement of radioNativeElements)

src/lib/radio/radio.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,10 @@ export class MdRadioButton implements OnInit {
374374
this.change.emit(event);
375375
}
376376

377+
_isRippleDisabled() {
378+
return this.disableRipple || this.disabled;
379+
}
380+
377381
/**
378382
* We use a hidden native input field to handle changes to focus state via keyboard navigation,
379383
* with visual rendering done separately. The native element is kept in sync with the overall

0 commit comments

Comments
 (0)