Skip to content

Commit

Permalink
feat(ripple): support for global ripple options (#3463)
Browse files Browse the repository at this point in the history
* feat(ripple): add option to change global speedFactor

* Refactors the `MD_DISABLE_RIPPLES` token to a configuration object, that can also include options like a global `baseSpeedFactor`.

* Fix Angular issue
  • Loading branch information
devversion authored and tinayuangao committed Mar 10, 2017
1 parent 17bf5e5 commit fb75a13
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 17 deletions.
2 changes: 1 addition & 1 deletion src/lib/core/ripple/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {CompatibilityModule} from '../compatibility/compatibility';
import {VIEWPORT_RULER_PROVIDER} from '../overlay/position/viewport-ruler';
import {SCROLL_DISPATCHER_PROVIDER} from '../overlay/scroll/scroll-dispatcher';

export {MdRipple, MD_DISABLE_RIPPLES} from './ripple';
export {MdRipple, RippleGlobalOptions, MD_RIPPLE_GLOBAL_OPTIONS} from './ripple';
export {RippleRef, RippleState} from './ripple-ref';
export {RippleConfig} from './ripple-renderer';

Expand Down
59 changes: 49 additions & 10 deletions src/lib/core/ripple/ripple.spec.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import {TestBed, ComponentFixture, fakeAsync, tick, inject} from '@angular/core/testing';
import {Component, ViewChild} from '@angular/core';
import {MdRipple, MdRippleModule, MD_DISABLE_RIPPLES, RippleState} from './index';
import {ViewportRuler} from '../overlay/position/viewport-ruler';
import {RIPPLE_FADE_OUT_DURATION, RIPPLE_FADE_IN_DURATION} from './ripple-renderer';
import {dispatchMouseEvent} from '../testing/dispatch-events';
import {
MdRipple, MdRippleModule, MD_RIPPLE_GLOBAL_OPTIONS, RippleState, RippleGlobalOptions
} from './index';

/** Extracts the numeric value of a pixel size string like '123px'. */
const pxStringToFloat = (s: string) => {
Expand Down Expand Up @@ -346,29 +348,29 @@ describe('MdRipple', () => {

});

describe('with ripples disabled', () => {
describe('global ripple options', () => {
let rippleDirective: MdRipple;

beforeEach(() => {
// Reset the previously configured testing module to be able to disable ripples globally.
function createTestComponent(rippleConfig: RippleGlobalOptions) {
// Reset the previously configured testing module to be able set new providers.
// The testing module has been initialized in the root describe group for the ripples.
TestBed.resetTestingModule();
TestBed.configureTestingModule({
imports: [MdRippleModule],
declarations: [BasicRippleContainer],
providers: [{ provide: MD_DISABLE_RIPPLES, useValue: true }]
providers: [{ provide: MD_RIPPLE_GLOBAL_OPTIONS, useValue: rippleConfig }]
});
});

beforeEach(() => {
fixture = TestBed.createComponent(BasicRippleContainer);
fixture.detectChanges();

rippleTarget = fixture.nativeElement.querySelector('[mat-ripple]');
rippleDirective = fixture.componentInstance.ripple;
});
}

it('when disabled should not show any ripples on mousedown', () => {
createTestComponent({ disabled: true });

it('should not show any ripples on mousedown', () => {
dispatchMouseEvent(rippleTarget, 'mousedown');
dispatchMouseEvent(rippleTarget, 'mouseup');

Expand All @@ -380,14 +382,51 @@ describe('MdRipple', () => {
expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0);
});

it('should still allow manual ripples', () => {
it('when disabled should still allow manual ripples', () => {
createTestComponent({ disabled: true });

expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0);

rippleDirective.launch(0, 0);

expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1);
});

it('should support changing the baseSpeedFactor', fakeAsync(() => {
createTestComponent({ baseSpeedFactor: 0.5 });

dispatchMouseEvent(rippleTarget, 'mousedown');
dispatchMouseEvent(rippleTarget, 'mouseup');

expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1);

// Calculates the speedFactor for the duration. Those factors needs to be inverted, because
// a lower speed factor, will make the duration longer. For example: 0.5 => 2x duration.
let fadeInFactor = 1 / 0.5;

// Calculates the duration for fading-in and fading-out the ripple.
tick(RIPPLE_FADE_IN_DURATION * fadeInFactor + RIPPLE_FADE_OUT_DURATION);

expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0);
}));

it('should combine individual speed factor with baseSpeedFactor', fakeAsync(() => {
createTestComponent({ baseSpeedFactor: 0.5 });

rippleDirective.launch(0, 0, { speedFactor: 1.5 });

expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1);

// Calculates the speedFactor for the duration. Those factors needs to be inverted, because
// a lower speed factor, will make the duration longer. For example: 0.5 => 2x duration.
let fadeInFactor = 1 / (0.5 * 1.5);

// Calculates the duration for fading-in and fading-out the ripple.
tick(RIPPLE_FADE_IN_DURATION * fadeInFactor + RIPPLE_FADE_OUT_DURATION);

expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0);
}));

});

describe('configuring behavior', () => {
Expand Down
25 changes: 19 additions & 6 deletions src/lib/core/ripple/ripple.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,13 @@ import {RippleConfig, RippleRenderer} from './ripple-renderer';
import {ViewportRuler} from '../overlay/position/viewport-ruler';
import {RippleRef} from './ripple-ref';

/** OpaqueToken that can be used to globally disable all ripples. Except programmatic ones. */
export const MD_DISABLE_RIPPLES = new OpaqueToken('md-disable-ripples');
/** OpaqueToken that can be used to specify the global ripple options. */
export const MD_RIPPLE_GLOBAL_OPTIONS = new OpaqueToken('md-ripple-global-options');

export type RippleGlobalOptions = {
disabled?: boolean;
baseSpeedFactor?: number;
};

@Directive({
selector: '[md-ripple], [mat-ripple], [mdRipple], [matRipple]',
Expand Down Expand Up @@ -70,18 +75,26 @@ export class MdRipple implements OnChanges, OnDestroy {
/** Renderer for the ripple DOM manipulations. */
private _rippleRenderer: RippleRenderer;

constructor(elementRef: ElementRef, ngZone: NgZone, ruler: ViewportRuler,
@Optional() @Inject(MD_DISABLE_RIPPLES) private _forceDisableRipples: boolean) {
/** Options that are set globally for all ripples. */
private _globalOptions: RippleGlobalOptions;

constructor(
elementRef: ElementRef,
ngZone: NgZone,
ruler: ViewportRuler,
// Type needs to be `any` because of https://github.com/angular/angular/issues/12631
@Optional() @Inject(MD_RIPPLE_GLOBAL_OPTIONS) globalOptions: any
) {
this._rippleRenderer = new RippleRenderer(elementRef, ngZone, ruler);
this._globalOptions = globalOptions ? globalOptions : {};
}

ngOnChanges(changes: SimpleChanges) {
if (changes['trigger'] && this.trigger) {
this._rippleRenderer.setTriggerElement(this.trigger);
}

this._rippleRenderer.rippleDisabled = this._forceDisableRipples || this.disabled;
this._rippleRenderer.rippleDisabled = this._globalOptions.disabled || this.disabled;
this._rippleRenderer.rippleConfig = this.rippleConfig;
}

Expand All @@ -104,7 +117,7 @@ export class MdRipple implements OnChanges, OnDestroy {
get rippleConfig(): RippleConfig {
return {
centered: this.centered,
speedFactor: this.speedFactor,
speedFactor: this.speedFactor * (this._globalOptions.baseSpeedFactor || 1),
radius: this.radius,
color: this.color
};
Expand Down

0 comments on commit fb75a13

Please sign in to comment.