Skip to content

Commit

Permalink
refactor: add mixin for disableRipple properties (#5598)
Browse files Browse the repository at this point in the history
Closes #5586
  • Loading branch information
devversion authored and kara committed Jul 20, 2017
1 parent e0fc526 commit 69c9dac
Show file tree
Hide file tree
Showing 9 changed files with 134 additions and 92 deletions.
22 changes: 8 additions & 14 deletions src/lib/button/button.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,17 @@ import {
Directive,
ElementRef,
forwardRef,
Input,
OnDestroy,
Optional,
Renderer2,
Self,
ViewEncapsulation,
Inject,
} from '@angular/core';
import {coerceBooleanProperty, FocusOriginMonitor, Platform} from '../core';
import {FocusOriginMonitor, Platform} from '../core';
import {mixinDisabled, CanDisable} from '../core/common-behaviors/disabled';
import {CanColor, mixinColor} from '../core/common-behaviors/color';
import {CanDisableRipple, mixinDisableRipple} from '../core/common-behaviors/disable-ripple';


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


/**
Expand All @@ -120,25 +120,19 @@ export const _MdButtonMixinBase = mixinColor(mixinDisabled(MdButtonBase));
},
templateUrl: 'button.html',
styleUrls: ['button.css'],
inputs: ['disabled', 'color'],
inputs: ['disabled', 'disableRipple', 'color'],
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class MdButton extends _MdButtonMixinBase implements OnDestroy, CanDisable, CanColor {
export class MdButton extends _MdButtonMixinBase
implements OnDestroy, CanDisable, CanColor, CanDisableRipple {

/** Whether the button is round. */
_isRoundButton: boolean = this._hasAttributeWithPrefix('fab', 'mini-fab');

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

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

/** Whether the ripple effect for this button is disabled. */
@Input()
get disableRipple() { return this._disableRipple; }
set disableRipple(v) { this._disableRipple = coerceBooleanProperty(v); }

constructor(renderer: Renderer2,
elementRef: ElementRef,
private _platform: Platform,
Expand Down Expand Up @@ -197,7 +191,7 @@ export class MdButton extends _MdButtonMixinBase implements OnDestroy, CanDisabl
'[attr.aria-disabled]': 'disabled.toString()',
'(click)': '_haltDisabledEvents($event)',
},
inputs: ['disabled', 'color'],
inputs: ['disabled', 'disableRipple', 'color'],
templateUrl: 'button.html',
styleUrls: ['button.css'],
encapsulation: ViewEncapsulation.None,
Expand Down
19 changes: 7 additions & 12 deletions src/lib/checkbox/checkbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {coerceBooleanProperty} from '@angular/cdk';
import {FocusOrigin, FocusOriginMonitor, MdRipple, RippleRef} from '../core';
import {mixinDisabled, CanDisable} from '../core/common-behaviors/disabled';
import {CanColor, mixinColor} from '../core/common-behaviors/color';
import {CanDisableRipple, mixinDisableRipple} from '../core/common-behaviors/disable-ripple';

// Increasing integer for generating unique ids for checkbox components.
let nextUniqueId = 0;
Expand Down Expand Up @@ -69,7 +70,8 @@ export class MdCheckboxChange {
export class MdCheckboxBase {
constructor(public _renderer: Renderer2, public _elementRef: ElementRef) {}
}
export const _MdCheckboxMixinBase = mixinColor(mixinDisabled(MdCheckboxBase), 'accent');
export const _MdCheckboxMixinBase =
mixinColor(mixinDisableRipple(mixinDisabled(MdCheckboxBase)), 'accent');


/**
Expand All @@ -94,12 +96,13 @@ export const _MdCheckboxMixinBase = mixinColor(mixinDisabled(MdCheckboxBase), 'a
'[class.mat-checkbox-label-before]': 'labelPosition == "before"',
},
providers: [MD_CHECKBOX_CONTROL_VALUE_ACCESSOR],
inputs: ['disabled', 'color'],
inputs: ['disabled', 'disableRipple', 'color'],
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class MdCheckbox extends _MdCheckboxMixinBase
implements ControlValueAccessor, AfterViewInit, OnDestroy, CanColor, CanDisable {
export class MdCheckbox extends _MdCheckboxMixinBase implements ControlValueAccessor, AfterViewInit,
OnDestroy, CanColor, CanDisable, CanDisableRipple {

/**
* Attached to the aria-label attribute of the host element. In most cases, arial-labelledby will
* take precedence so this may be omitted.
Expand All @@ -119,14 +122,6 @@ export class MdCheckbox extends _MdCheckboxMixinBase
/** Returns the unique id for the visual hidden input. */
get inputId(): string { return `${this.id || this._uniqueId}-input`; }

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

/** Whether the ripple effect for this checkbox is disabled. */
@Input()
get disableRipple(): boolean { return this._disableRipple; }
set disableRipple(value) { this._disableRipple = coerceBooleanProperty(value); }

private _required: boolean;

/** Whether the checkbox is required. */
Expand Down
35 changes: 35 additions & 0 deletions src/lib/core/common-behaviors/disable-ripple.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import {mixinDisableRipple} from './disable-ripple';

describe('mixinDisableRipple', () => {

it('should augment an existing class with a disableRipple property', () => {
const classWithMixin = mixinDisableRipple(TestClass);
const instance = new classWithMixin();

expect(instance.disableRipple)
.toBe(false, 'Expected the mixed-into class to have a disable-ripple property');

instance.disableRipple = true;

expect(instance.disableRipple)
.toBe(true, 'Expected the mixed-into class to have an updated disable-ripple property');
});

it('should coerce values being passed to the disableRipple property', () => {
const classWithMixin = mixinDisableRipple(TestClass);
const instance = new classWithMixin();

expect(instance.disableRipple)
.toBe(false, 'Expected disableRipple to be set to false initially');

// Setting string values to the disableRipple property should be prevented by TypeScript's
// type checking, but developers can still set string values from their template bindings.
(instance as any).disableRipple = '';

expect(instance.disableRipple)
.toBe(true, 'Expected disableRipple to be set to true if an empty string is set as value');
});

});

class TestClass {}
29 changes: 29 additions & 0 deletions src/lib/core/common-behaviors/disable-ripple.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/

import {coerceBooleanProperty} from '@angular/cdk';
import {Constructor} from './constructor';

/** @docs-private */
export interface CanDisableRipple {
disableRipple: boolean;
}

/** Mixin to augment a directive with a `disableRipple` property. */
export function mixinDisableRipple<T extends Constructor<{}>>(base: T)
: Constructor<CanDisableRipple> & T {
return class extends base {
private _disableRipple: boolean = false;

/** Whether the ripple effect is disabled or not. */
get disableRipple() { return this._disableRipple; }
set disableRipple(value: any) { this._disableRipple = coerceBooleanProperty(value); }

constructor(...args: any[]) { super(...args); }
};
}
40 changes: 17 additions & 23 deletions src/lib/list/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,24 @@ import {
ContentChildren,
Directive,
ElementRef,
Input,
Optional,
QueryList,
Renderer2,
ViewEncapsulation,
ChangeDetectionStrategy,
} from '@angular/core';
import {coerceBooleanProperty, MdLine, MdLineSetter} from '../core';
import {MdLine, MdLineSetter} from '../core';
import {CanDisableRipple, mixinDisableRipple} from '../core/common-behaviors/disable-ripple';

// Boilerplate for applying mixins to MdList.
/** @docs-private */
export class MdListBase {}
export const _MdListMixinBase = mixinDisableRipple(MdListBase);

// Boilerplate for applying mixins to MdListItem.
/** @docs-private */
export class MdListItemBase {}
export const _MdListItemMixinBase = mixinDisableRipple(MdListItemBase);

@Directive({
selector: 'md-divider, mat-divider',
Expand All @@ -37,20 +47,11 @@ export class MdListDivider {}
host: {'role': 'list'},
template: '<ng-content></ng-content>',
styleUrls: ['list.css'],
inputs: ['disableRipple'],
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class MdList {
private _disableRipple: boolean = false;

/**
* Whether the ripple effect should be disabled on the list-items or not.
* This flag only has an effect for `md-nav-list` components.
*/
@Input()
get disableRipple() { return this._disableRipple; }
set disableRipple(value: boolean) { this._disableRipple = coerceBooleanProperty(value); }
}
export class MdList extends _MdListMixinBase implements CanDisableRipple {}

/**
* Directive whose purpose is to add the mat- CSS styling to this selector.
Expand Down Expand Up @@ -121,23 +122,15 @@ export class MdListSubheaderCssMatStyler {}
'(focus)': '_handleFocus()',
'(blur)': '_handleBlur()',
},
inputs: ['disableRipple'],
templateUrl: 'list-item.html',
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class MdListItem implements AfterContentInit {
export class MdListItem extends _MdListItemMixinBase implements AfterContentInit, CanDisableRipple {
private _lineSetter: MdLineSetter;
private _disableRipple: boolean = false;
private _isNavList: boolean = false;

/**
* Whether the ripple effect on click should be disabled. This applies only to list items that are
* part of a nav list. The value of `disableRipple` on the `md-nav-list` overrides this flag.
*/
@Input()
get disableRipple() { return this._disableRipple; }
set disableRipple(value: boolean) { this._disableRipple = coerceBooleanProperty(value); }

@ContentChildren(MdLine) _lines: QueryList<MdLine>;

@ContentChild(MdListAvatarCssMatStyler)
Expand All @@ -153,6 +146,7 @@ export class MdListItem implements AfterContentInit {
private _element: ElementRef,
@Optional() private _list: MdList,
@Optional() navList: MdNavListCssMatStyler) {
super();
this._isNavList = !!navList;
}

Expand Down
15 changes: 4 additions & 11 deletions src/lib/radio/radio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {
import {coerceBooleanProperty} from '@angular/cdk';
import {mixinDisabled, CanDisable} from '../core/common-behaviors/disabled';
import {CanColor, mixinColor} from '../core/common-behaviors/color';
import {CanDisableRipple, mixinDisableRipple} from '../core/common-behaviors/disable-ripple';

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

/**
* A radio-button. May be inside of
Expand All @@ -313,7 +314,7 @@ export const _MdRadioButtonMixinBase = mixinColor(MdRadioButtonBase, 'accent');
selector: 'md-radio-button, mat-radio-button',
templateUrl: 'radio.html',
styleUrls: ['radio.css'],
inputs: ['color'],
inputs: ['color', 'disableRipple'],
encapsulation: ViewEncapsulation.None,
host: {
'class': 'mat-radio-button',
Expand All @@ -324,7 +325,7 @@ export const _MdRadioButtonMixinBase = mixinColor(MdRadioButtonBase, 'accent');
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class MdRadioButton extends _MdRadioButtonMixinBase
implements OnInit, AfterViewInit, OnDestroy, CanColor {
implements OnInit, AfterViewInit, OnDestroy, CanColor, CanDisableRipple {

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

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

/** Whether the ripple effect for this radio button is disabled. */
@Input()
get disableRipple(): boolean { return this._disableRipple; }
set disableRipple(value) { this._disableRipple = coerceBooleanProperty(value); }

/** Whether this radio button is checked. */
@Input()
get checked(): boolean {
Expand Down Expand Up @@ -450,9 +446,6 @@ export class MdRadioButton extends _MdRadioButtonMixinBase
/** Value assigned to this radio.*/
private _value: any = null;

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

/** The child ripple instance. */
@ViewChild(MdRipple) _ripple: MdRipple;

Expand Down
17 changes: 7 additions & 10 deletions src/lib/slide-toggle/slide-toggle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
import {mixinDisabled, CanDisable} from '../core/common-behaviors/disabled';
import {CanColor, mixinColor} from '../core/common-behaviors/color';
import {CanDisableRipple, mixinDisableRipple} from '../core/common-behaviors/disable-ripple';

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

/** Represents a slidable "switch" toggle that can be moved between on and off. */
@Component({
Expand All @@ -71,20 +73,20 @@ export const _MdSlideToggleMixinBase = mixinColor(mixinDisabled(MdSlideToggleBas
templateUrl: 'slide-toggle.html',
styleUrls: ['slide-toggle.css'],
providers: [MD_SLIDE_TOGGLE_VALUE_ACCESSOR],
inputs: ['disabled', 'color'],
inputs: ['disabled', 'disableRipple', 'color'],
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class MdSlideToggle extends _MdSlideToggleMixinBase
implements OnDestroy, AfterContentInit, ControlValueAccessor, CanDisable, CanColor {
export class MdSlideToggle extends _MdSlideToggleMixinBase implements OnDestroy, AfterContentInit,
ControlValueAccessor, CanDisable, CanColor, CanDisableRipple {

private onChange = (_: any) => {};
private onTouched = () => {};

private _uniqueId: string = `md-slide-toggle-${++nextUniqueId}`;
private _checked: boolean = false;
private _slideRenderer: SlideToggleRenderer;
private _required: boolean = false;
private _disableRipple: boolean = false;

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

/** Whether the ripple effect for this slide-toggle is disabled. */
@Input()
get disableRipple(): boolean { return this._disableRipple; }
set disableRipple(value) { this._disableRipple = coerceBooleanProperty(value); }

/** An event will be dispatched each time the slide-toggle changes its value. */
@Output() change: EventEmitter<MdSlideToggleChange> = new EventEmitter<MdSlideToggleChange>();

Expand Down
Loading

0 comments on commit 69c9dac

Please sign in to comment.