Skip to content

Commit 69c9dac

Browse files
devversionkara
authored andcommitted
refactor: add mixin for disableRipple properties (#5598)
Closes #5586
1 parent e0fc526 commit 69c9dac

File tree

9 files changed

+134
-92
lines changed

9 files changed

+134
-92
lines changed

src/lib/button/button.ts

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,17 @@ import {
1212
Directive,
1313
ElementRef,
1414
forwardRef,
15-
Input,
1615
OnDestroy,
1716
Optional,
1817
Renderer2,
1918
Self,
2019
ViewEncapsulation,
2120
Inject,
2221
} from '@angular/core';
23-
import {coerceBooleanProperty, FocusOriginMonitor, Platform} from '../core';
22+
import {FocusOriginMonitor, Platform} from '../core';
2423
import {mixinDisabled, CanDisable} from '../core/common-behaviors/disabled';
2524
import {CanColor, mixinColor} from '../core/common-behaviors/color';
25+
import {CanDisableRipple, mixinDisableRipple} from '../core/common-behaviors/disable-ripple';
2626

2727

2828
// TODO(kara): Convert attribute selectors to classes when attr maps become available
@@ -103,7 +103,7 @@ export class MdMiniFab {
103103
export class MdButtonBase {
104104
constructor(public _renderer: Renderer2, public _elementRef: ElementRef) {}
105105
}
106-
export const _MdButtonMixinBase = mixinColor(mixinDisabled(MdButtonBase));
106+
export const _MdButtonMixinBase = mixinColor(mixinDisabled(mixinDisableRipple(MdButtonBase)));
107107

108108

109109
/**
@@ -120,25 +120,19 @@ export const _MdButtonMixinBase = mixinColor(mixinDisabled(MdButtonBase));
120120
},
121121
templateUrl: 'button.html',
122122
styleUrls: ['button.css'],
123-
inputs: ['disabled', 'color'],
123+
inputs: ['disabled', 'disableRipple', 'color'],
124124
encapsulation: ViewEncapsulation.None,
125125
changeDetection: ChangeDetectionStrategy.OnPush,
126126
})
127-
export class MdButton extends _MdButtonMixinBase implements OnDestroy, CanDisable, CanColor {
127+
export class MdButton extends _MdButtonMixinBase
128+
implements OnDestroy, CanDisable, CanColor, CanDisableRipple {
129+
128130
/** Whether the button is round. */
129131
_isRoundButton: boolean = this._hasAttributeWithPrefix('fab', 'mini-fab');
130132

131133
/** Whether the button is icon button. */
132134
_isIconButton: boolean = this._hasAttributeWithPrefix('icon-button');
133135

134-
/** Whether the ripple effect on click should be disabled. */
135-
private _disableRipple: boolean = false;
136-
137-
/** Whether the ripple effect for this button is disabled. */
138-
@Input()
139-
get disableRipple() { return this._disableRipple; }
140-
set disableRipple(v) { this._disableRipple = coerceBooleanProperty(v); }
141-
142136
constructor(renderer: Renderer2,
143137
elementRef: ElementRef,
144138
private _platform: Platform,
@@ -197,7 +191,7 @@ export class MdButton extends _MdButtonMixinBase implements OnDestroy, CanDisabl
197191
'[attr.aria-disabled]': 'disabled.toString()',
198192
'(click)': '_haltDisabledEvents($event)',
199193
},
200-
inputs: ['disabled', 'color'],
194+
inputs: ['disabled', 'disableRipple', 'color'],
201195
templateUrl: 'button.html',
202196
styleUrls: ['button.css'],
203197
encapsulation: ViewEncapsulation.None,

src/lib/checkbox/checkbox.ts

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import {coerceBooleanProperty} from '@angular/cdk';
2626
import {FocusOrigin, FocusOriginMonitor, MdRipple, RippleRef} from '../core';
2727
import {mixinDisabled, CanDisable} from '../core/common-behaviors/disabled';
2828
import {CanColor, mixinColor} from '../core/common-behaviors/color';
29+
import {CanDisableRipple, mixinDisableRipple} from '../core/common-behaviors/disable-ripple';
2930

3031
// Increasing integer for generating unique ids for checkbox components.
3132
let nextUniqueId = 0;
@@ -69,7 +70,8 @@ export class MdCheckboxChange {
6970
export class MdCheckboxBase {
7071
constructor(public _renderer: Renderer2, public _elementRef: ElementRef) {}
7172
}
72-
export const _MdCheckboxMixinBase = mixinColor(mixinDisabled(MdCheckboxBase), 'accent');
73+
export const _MdCheckboxMixinBase =
74+
mixinColor(mixinDisableRipple(mixinDisabled(MdCheckboxBase)), 'accent');
7375

7476

7577
/**
@@ -94,12 +96,13 @@ export const _MdCheckboxMixinBase = mixinColor(mixinDisabled(MdCheckboxBase), 'a
9496
'[class.mat-checkbox-label-before]': 'labelPosition == "before"',
9597
},
9698
providers: [MD_CHECKBOX_CONTROL_VALUE_ACCESSOR],
97-
inputs: ['disabled', 'color'],
99+
inputs: ['disabled', 'disableRipple', 'color'],
98100
encapsulation: ViewEncapsulation.None,
99101
changeDetection: ChangeDetectionStrategy.OnPush
100102
})
101-
export class MdCheckbox extends _MdCheckboxMixinBase
102-
implements ControlValueAccessor, AfterViewInit, OnDestroy, CanColor, CanDisable {
103+
export class MdCheckbox extends _MdCheckboxMixinBase implements ControlValueAccessor, AfterViewInit,
104+
OnDestroy, CanColor, CanDisable, CanDisableRipple {
105+
103106
/**
104107
* Attached to the aria-label attribute of the host element. In most cases, arial-labelledby will
105108
* take precedence so this may be omitted.
@@ -119,14 +122,6 @@ export class MdCheckbox extends _MdCheckboxMixinBase
119122
/** Returns the unique id for the visual hidden input. */
120123
get inputId(): string { return `${this.id || this._uniqueId}-input`; }
121124

122-
/** Whether the ripple effect on click should be disabled. */
123-
private _disableRipple: boolean;
124-
125-
/** Whether the ripple effect for this checkbox is disabled. */
126-
@Input()
127-
get disableRipple(): boolean { return this._disableRipple; }
128-
set disableRipple(value) { this._disableRipple = coerceBooleanProperty(value); }
129-
130125
private _required: boolean;
131126

132127
/** Whether the checkbox is required. */
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import {mixinDisableRipple} from './disable-ripple';
2+
3+
describe('mixinDisableRipple', () => {
4+
5+
it('should augment an existing class with a disableRipple property', () => {
6+
const classWithMixin = mixinDisableRipple(TestClass);
7+
const instance = new classWithMixin();
8+
9+
expect(instance.disableRipple)
10+
.toBe(false, 'Expected the mixed-into class to have a disable-ripple property');
11+
12+
instance.disableRipple = true;
13+
14+
expect(instance.disableRipple)
15+
.toBe(true, 'Expected the mixed-into class to have an updated disable-ripple property');
16+
});
17+
18+
it('should coerce values being passed to the disableRipple property', () => {
19+
const classWithMixin = mixinDisableRipple(TestClass);
20+
const instance = new classWithMixin();
21+
22+
expect(instance.disableRipple)
23+
.toBe(false, 'Expected disableRipple to be set to false initially');
24+
25+
// Setting string values to the disableRipple property should be prevented by TypeScript's
26+
// type checking, but developers can still set string values from their template bindings.
27+
(instance as any).disableRipple = '';
28+
29+
expect(instance.disableRipple)
30+
.toBe(true, 'Expected disableRipple to be set to true if an empty string is set as value');
31+
});
32+
33+
});
34+
35+
class TestClass {}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/**
2+
* @license
3+
* Copyright Google Inc. All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import {coerceBooleanProperty} from '@angular/cdk';
10+
import {Constructor} from './constructor';
11+
12+
/** @docs-private */
13+
export interface CanDisableRipple {
14+
disableRipple: boolean;
15+
}
16+
17+
/** Mixin to augment a directive with a `disableRipple` property. */
18+
export function mixinDisableRipple<T extends Constructor<{}>>(base: T)
19+
: Constructor<CanDisableRipple> & T {
20+
return class extends base {
21+
private _disableRipple: boolean = false;
22+
23+
/** Whether the ripple effect is disabled or not. */
24+
get disableRipple() { return this._disableRipple; }
25+
set disableRipple(value: any) { this._disableRipple = coerceBooleanProperty(value); }
26+
27+
constructor(...args: any[]) { super(...args); }
28+
};
29+
}

src/lib/list/list.ts

Lines changed: 17 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,24 @@ import {
1313
ContentChildren,
1414
Directive,
1515
ElementRef,
16-
Input,
1716
Optional,
1817
QueryList,
1918
Renderer2,
2019
ViewEncapsulation,
2120
ChangeDetectionStrategy,
2221
} from '@angular/core';
23-
import {coerceBooleanProperty, MdLine, MdLineSetter} from '../core';
22+
import {MdLine, MdLineSetter} from '../core';
23+
import {CanDisableRipple, mixinDisableRipple} from '../core/common-behaviors/disable-ripple';
24+
25+
// Boilerplate for applying mixins to MdList.
26+
/** @docs-private */
27+
export class MdListBase {}
28+
export const _MdListMixinBase = mixinDisableRipple(MdListBase);
29+
30+
// Boilerplate for applying mixins to MdListItem.
31+
/** @docs-private */
32+
export class MdListItemBase {}
33+
export const _MdListItemMixinBase = mixinDisableRipple(MdListItemBase);
2434

2535
@Directive({
2636
selector: 'md-divider, mat-divider',
@@ -37,20 +47,11 @@ export class MdListDivider {}
3747
host: {'role': 'list'},
3848
template: '<ng-content></ng-content>',
3949
styleUrls: ['list.css'],
50+
inputs: ['disableRipple'],
4051
encapsulation: ViewEncapsulation.None,
4152
changeDetection: ChangeDetectionStrategy.OnPush,
4253
})
43-
export class MdList {
44-
private _disableRipple: boolean = false;
45-
46-
/**
47-
* Whether the ripple effect should be disabled on the list-items or not.
48-
* This flag only has an effect for `md-nav-list` components.
49-
*/
50-
@Input()
51-
get disableRipple() { return this._disableRipple; }
52-
set disableRipple(value: boolean) { this._disableRipple = coerceBooleanProperty(value); }
53-
}
54+
export class MdList extends _MdListMixinBase implements CanDisableRipple {}
5455

5556
/**
5657
* Directive whose purpose is to add the mat- CSS styling to this selector.
@@ -121,23 +122,15 @@ export class MdListSubheaderCssMatStyler {}
121122
'(focus)': '_handleFocus()',
122123
'(blur)': '_handleBlur()',
123124
},
125+
inputs: ['disableRipple'],
124126
templateUrl: 'list-item.html',
125127
encapsulation: ViewEncapsulation.None,
126128
changeDetection: ChangeDetectionStrategy.OnPush,
127129
})
128-
export class MdListItem implements AfterContentInit {
130+
export class MdListItem extends _MdListItemMixinBase implements AfterContentInit, CanDisableRipple {
129131
private _lineSetter: MdLineSetter;
130-
private _disableRipple: boolean = false;
131132
private _isNavList: boolean = false;
132133

133-
/**
134-
* Whether the ripple effect on click should be disabled. This applies only to list items that are
135-
* part of a nav list. The value of `disableRipple` on the `md-nav-list` overrides this flag.
136-
*/
137-
@Input()
138-
get disableRipple() { return this._disableRipple; }
139-
set disableRipple(value: boolean) { this._disableRipple = coerceBooleanProperty(value); }
140-
141134
@ContentChildren(MdLine) _lines: QueryList<MdLine>;
142135

143136
@ContentChild(MdListAvatarCssMatStyler)
@@ -153,6 +146,7 @@ export class MdListItem implements AfterContentInit {
153146
private _element: ElementRef,
154147
@Optional() private _list: MdList,
155148
@Optional() navList: MdNavListCssMatStyler) {
149+
super();
156150
this._isNavList = !!navList;
157151
}
158152

src/lib/radio/radio.ts

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import {
3838
import {coerceBooleanProperty} from '@angular/cdk';
3939
import {mixinDisabled, CanDisable} from '../core/common-behaviors/disabled';
4040
import {CanColor, mixinColor} from '../core/common-behaviors/color';
41+
import {CanDisableRipple, mixinDisableRipple} from '../core/common-behaviors/disable-ripple';
4142

4243
// Increasing integer for generating unique ids for radio components.
4344
let nextUniqueId = 0;
@@ -303,7 +304,7 @@ export class MdRadioButtonBase {
303304
}
304305
// As per Material design specifications the selection control radio should use the accent color
305306
// palette by default. https://material.io/guidelines/components/selection-controls.html
306-
export const _MdRadioButtonMixinBase = mixinColor(MdRadioButtonBase, 'accent');
307+
export const _MdRadioButtonMixinBase = mixinColor(mixinDisableRipple(MdRadioButtonBase), 'accent');
307308

308309
/**
309310
* A radio-button. May be inside of
@@ -313,7 +314,7 @@ export const _MdRadioButtonMixinBase = mixinColor(MdRadioButtonBase, 'accent');
313314
selector: 'md-radio-button, mat-radio-button',
314315
templateUrl: 'radio.html',
315316
styleUrls: ['radio.css'],
316-
inputs: ['color'],
317+
inputs: ['color', 'disableRipple'],
317318
encapsulation: ViewEncapsulation.None,
318319
host: {
319320
'class': 'mat-radio-button',
@@ -324,7 +325,7 @@ export const _MdRadioButtonMixinBase = mixinColor(MdRadioButtonBase, 'accent');
324325
changeDetection: ChangeDetectionStrategy.OnPush,
325326
})
326327
export class MdRadioButton extends _MdRadioButtonMixinBase
327-
implements OnInit, AfterViewInit, OnDestroy, CanColor {
328+
implements OnInit, AfterViewInit, OnDestroy, CanColor, CanDisableRipple {
328329

329330
private _uniqueId: string = `md-radio-${++nextUniqueId}`;
330331

@@ -340,11 +341,6 @@ export class MdRadioButton extends _MdRadioButtonMixinBase
340341
/** The 'aria-labelledby' attribute takes precedence as the element's text alternative. */
341342
@Input('aria-labelledby') ariaLabelledby: string;
342343

343-
/** Whether the ripple effect for this radio button is disabled. */
344-
@Input()
345-
get disableRipple(): boolean { return this._disableRipple; }
346-
set disableRipple(value) { this._disableRipple = coerceBooleanProperty(value); }
347-
348344
/** Whether this radio button is checked. */
349345
@Input()
350346
get checked(): boolean {
@@ -450,9 +446,6 @@ export class MdRadioButton extends _MdRadioButtonMixinBase
450446
/** Value assigned to this radio.*/
451447
private _value: any = null;
452448

453-
/** Whether the ripple effect on click should be disabled. */
454-
private _disableRipple: boolean;
455-
456449
/** The child ripple instance. */
457450
@ViewChild(MdRipple) _ripple: MdRipple;
458451

src/lib/slide-toggle/slide-toggle.ts

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import {
3434
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
3535
import {mixinDisabled, CanDisable} from '../core/common-behaviors/disabled';
3636
import {CanColor, mixinColor} from '../core/common-behaviors/color';
37+
import {CanDisableRipple, mixinDisableRipple} from '../core/common-behaviors/disable-ripple';
3738

3839
// Increasing integer for generating unique ids for slide-toggle components.
3940
let nextUniqueId = 0;
@@ -55,7 +56,8 @@ export class MdSlideToggleChange {
5556
export class MdSlideToggleBase {
5657
constructor(public _renderer: Renderer2, public _elementRef: ElementRef) {}
5758
}
58-
export const _MdSlideToggleMixinBase = mixinColor(mixinDisabled(MdSlideToggleBase), 'accent');
59+
export const _MdSlideToggleMixinBase =
60+
mixinColor(mixinDisableRipple(mixinDisabled(MdSlideToggleBase)), 'accent');
5961

6062
/** Represents a slidable "switch" toggle that can be moved between on and off. */
6163
@Component({
@@ -71,20 +73,20 @@ export const _MdSlideToggleMixinBase = mixinColor(mixinDisabled(MdSlideToggleBas
7173
templateUrl: 'slide-toggle.html',
7274
styleUrls: ['slide-toggle.css'],
7375
providers: [MD_SLIDE_TOGGLE_VALUE_ACCESSOR],
74-
inputs: ['disabled', 'color'],
76+
inputs: ['disabled', 'disableRipple', 'color'],
7577
encapsulation: ViewEncapsulation.None,
7678
changeDetection: ChangeDetectionStrategy.OnPush
7779
})
78-
export class MdSlideToggle extends _MdSlideToggleMixinBase
79-
implements OnDestroy, AfterContentInit, ControlValueAccessor, CanDisable, CanColor {
80+
export class MdSlideToggle extends _MdSlideToggleMixinBase implements OnDestroy, AfterContentInit,
81+
ControlValueAccessor, CanDisable, CanColor, CanDisableRipple {
82+
8083
private onChange = (_: any) => {};
8184
private onTouched = () => {};
8285

8386
private _uniqueId: string = `md-slide-toggle-${++nextUniqueId}`;
8487
private _checked: boolean = false;
8588
private _slideRenderer: SlideToggleRenderer;
8689
private _required: boolean = false;
87-
private _disableRipple: boolean = false;
8890

8991
/** Reference to the focus state ripple. */
9092
private _focusRipple: RippleRef | null;
@@ -112,11 +114,6 @@ export class MdSlideToggle extends _MdSlideToggleMixinBase
112114
get required(): boolean { return this._required; }
113115
set required(value) { this._required = coerceBooleanProperty(value); }
114116

115-
/** Whether the ripple effect for this slide-toggle is disabled. */
116-
@Input()
117-
get disableRipple(): boolean { return this._disableRipple; }
118-
set disableRipple(value) { this._disableRipple = coerceBooleanProperty(value); }
119-
120117
/** An event will be dispatched each time the slide-toggle changes its value. */
121118
@Output() change: EventEmitter<MdSlideToggleChange> = new EventEmitter<MdSlideToggleChange>();
122119

0 commit comments

Comments
 (0)