feat(slide-toggle): make slide-toggle click and drag actions configurable (#11719)

This commit is contained in:
tinayuangao
2018-06-20 10:55:56 -07:00
committed by GitHub
parent 50ea837250
commit 32193e915e
4 changed files with 155 additions and 6 deletions
+1 -1
View File
@@ -8,4 +8,4 @@
export * from './slide-toggle-module';
export * from './slide-toggle';
export * from './slide-toggle-config';
@@ -0,0 +1,24 @@
/**
* @license
* Copyright Google LLC 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 {InjectionToken} from '@angular/core';
/** Default `mat-slide-toggle` options that can be overridden. */
export interface MatSlideToggleDefaultOptions {
/** Whether toggle action triggers value changes in slide toggle. */
disableToggleValue?: boolean;
/** Whether drag action triggers value changes in slide toggle. */
disableDragValue?: boolean;
}
/** Injection token to be used to override the default options for `mat-slide-toggle`. */
export const MAT_SLIDE_TOGGLE_DEFAULT_OPTIONS =
new InjectionToken<MatSlideToggleDefaultOptions>('mat-slide-toggle-default-options', {
providedIn: 'root',
factory: () => ({disableToggleValue: false, disableDragValue: false})
});
+97
View File
@@ -6,6 +6,7 @@ import {FormControl, FormsModule, NgModel, ReactiveFormsModule} from '@angular/f
import {defaultRippleAnimationConfig} from '@angular/material/core';
import {By, HAMMER_GESTURE_CONFIG} from '@angular/platform-browser';
import {TestGestureConfig} from '../slider/test-gesture-config';
import {MAT_SLIDE_TOGGLE_DEFAULT_OPTIONS} from './slide-toggle-config';
import {MatSlideToggle, MatSlideToggleChange, MatSlideToggleModule} from './index';
describe('MatSlideToggle without forms', () => {
@@ -355,6 +356,96 @@ describe('MatSlideToggle without forms', () => {
}));
});
describe('custom action configuration', () => {
it('should not change value on click when click action is noop', fakeAsync(() => {
TestBed
.resetTestingModule()
.configureTestingModule({
imports: [MatSlideToggleModule],
declarations: [SlideToggleBasic],
providers: [
{
provide: HAMMER_GESTURE_CONFIG,
useFactory: () => gestureConfig = new TestGestureConfig()
},
{provide: MAT_SLIDE_TOGGLE_DEFAULT_OPTIONS, useValue: {disableToggleValue: true}},
]
});
const fixture = TestBed.createComponent(SlideToggleBasic);
const testComponent = fixture.debugElement.componentInstance;
const slideToggleDebug = fixture.debugElement.query(By.css('mat-slide-toggle'));
const slideToggle = slideToggleDebug.componentInstance;
const inputElement = fixture.debugElement.query(By.css('input')).nativeElement;
const labelElement = fixture.debugElement.query(By.css('label')).nativeElement;
expect(testComponent.toggleTriggered).toBe(0);
expect(testComponent.dragTriggered).toBe(0);
expect(slideToggle.checked).toBe(false, 'Expect slide toggle value not changed');
labelElement.click();
fixture.detectChanges();
expect(slideToggle.checked).toBe(false, 'Expect slide toggle value not changed');
expect(testComponent.toggleTriggered).toBe(1, 'Expect toggle once');
expect(testComponent.dragTriggered).toBe(0);
inputElement.click();
fixture.detectChanges();
expect(slideToggle.checked).toBe(false, 'Expect slide toggle value not changed');
expect(testComponent.toggleTriggered).toBe(2, 'Expect toggle twice');
expect(testComponent.dragTriggered).toBe(0);
}));
it('should not change value on dragging when drag action is noop', fakeAsync(() => {
TestBed
.resetTestingModule()
.configureTestingModule({
imports: [MatSlideToggleModule],
declarations: [SlideToggleBasic],
providers: [
{
provide: HAMMER_GESTURE_CONFIG,
useFactory: () => gestureConfig = new TestGestureConfig()
},
{provide: MAT_SLIDE_TOGGLE_DEFAULT_OPTIONS, useValue: {disableDragValue: true}},
]
});
const fixture = TestBed.createComponent(SlideToggleBasic);
const testComponent = fixture.debugElement.componentInstance;
const slideToggleDebug = fixture.debugElement.query(By.css('mat-slide-toggle'));
const thumbContainerDebug = slideToggleDebug
.query(By.css('.mat-slide-toggle-thumb-container'));
const slideThumbContainer = thumbContainerDebug.nativeElement;
const slideToggle = slideToggleDebug.componentInstance;
expect(testComponent.toggleTriggered).toBe(0);
expect(testComponent.dragTriggered).toBe(0);
expect(slideToggle.checked).toBe(false);
gestureConfig.emitEventForElement('slidestart', slideThumbContainer);
expect(slideThumbContainer.classList).toContain('mat-dragging');
gestureConfig.emitEventForElement('slide', slideThumbContainer, {
deltaX: 200 // Arbitrary, large delta that will be clamped to the end of the slide-toggle.
});
gestureConfig.emitEventForElement('slideend', slideThumbContainer);
// Flush the timeout for the slide ending.
tick();
expect(slideToggle.checked).toBe(false, 'Expect slide toggle value not changed');
expect(slideThumbContainer.classList).not.toContain('mat-dragging');
expect(testComponent.lastEvent).toBeUndefined();
expect(testComponent.toggleTriggered).toBe(0);
expect(testComponent.dragTriggered).toBe(1, 'Expect drag once');
}));
});
describe('with dragging', () => {
let fixture: ComponentFixture<any>;
@@ -849,6 +940,8 @@ describe('MatSlideToggle with forms', () => {
[tabIndex]="slideTabindex"
[labelPosition]="labelPosition"
[disableRipple]="disableRipple"
(toggleChange)="onSlideToggleChange()"
(dragChange)="onSlideDragChange()"
(change)="onSlideChange($event)"
(click)="onSlideClick($event)">
<span>Test Slide Toggle</span>
@@ -867,9 +960,13 @@ class SlideToggleBasic {
slideTabindex: number;
lastEvent: MatSlideToggleChange;
labelPosition: string;
toggleTriggered: number = 0;
dragTriggered: number = 0;
onSlideClick: (event?: Event) => void = () => {};
onSlideChange = (event: MatSlideToggleChange) => this.lastEvent = event;
onSlideToggleChange = () => this.toggleTriggered++;
onSlideDragChange = () => this.dragTriggered++;
}
@Component({
+33 -5
View File
@@ -42,6 +42,10 @@ import {
RippleRef,
} from '@angular/material/core';
import {ANIMATION_MODULE_TYPE} from '@angular/platform-browser/animations';
import {
MAT_SLIDE_TOGGLE_DEFAULT_OPTIONS,
MatSlideToggleDefaultOptions
} from './slide-toggle-config';
// Increasing integer for generating unique ids for slide-toggle components.
let nextUniqueId = 0;
@@ -153,6 +157,21 @@ export class MatSlideToggle extends _MatSlideToggleMixinBase implements OnDestro
@Output() readonly change: EventEmitter<MatSlideToggleChange> =
new EventEmitter<MatSlideToggleChange>();
/**
* An event will be dispatched each time the slide-toggle input is toggled.
* This event always fire when user toggle the slide toggle, but does not mean the slide toggle's
* value is changed. The event does not fire when user drag to change the slide toggle value.
*/
@Output() readonly toggleChange: EventEmitter<void> = new EventEmitter<void>();
/**
* An event will be dispatched each time the slide-toggle is dragged.
* This event always fire when user drag the slide toggle to make a change that greater than 50%.
* It does not mean the slide toggle's value is changed. The event does not fire when user toggle
* the slide toggle to change the slide toggle's value.
*/
@Output() readonly dragChange: EventEmitter<void> = new EventEmitter<void>();
/** Returns the unique id for the visual hidden input. */
get inputId(): string { return `${this.id || this._uniqueId}-input`; }
@@ -172,8 +191,9 @@ export class MatSlideToggle extends _MatSlideToggleMixinBase implements OnDestro
private _changeDetectorRef: ChangeDetectorRef,
@Attribute('tabindex') tabIndex: string,
private _ngZone: NgZone,
@Inject(MAT_SLIDE_TOGGLE_DEFAULT_OPTIONS)
public defaults: MatSlideToggleDefaultOptions,
@Optional() @Inject(ANIMATION_MODULE_TYPE) public _animationMode?: string) {
super(elementRef);
this.tabIndex = parseInt(tabIndex) || 0;
}
@@ -195,10 +215,15 @@ export class MatSlideToggle extends _MatSlideToggleMixinBase implements OnDestro
// emit its event object to the component's `change` output.
event.stopPropagation();
if (!this._dragging) {
this.toggleChange.emit();
}
// Releasing the pointer over the `<label>` element while dragging triggers another
// click event on the `<label>` element. This means that the checked state of the underlying
// input changed unintentionally and needs to be changed back.
if (this._dragging) {
// input changed unintentionally and needs to be changed back. Or when the slide toggle's config
// disabled toggle change event by setting `disableToggleValue: true`, the slide toggle's value
// does not change, and the checked state of the underlying input needs to be changed back.
if (this._dragging || this.defaults.disableToggleValue) {
this._inputElement.nativeElement.checked = this.checked;
return;
}
@@ -316,8 +341,11 @@ export class MatSlideToggle extends _MatSlideToggleMixinBase implements OnDestro
const newCheckedValue = this._dragPercentage > 50;
if (newCheckedValue !== this.checked) {
this.checked = newCheckedValue;
this._emitChangeEvent();
this.dragChange.emit();
if (!this.defaults.disableDragValue) {
this.checked = newCheckedValue;
this._emitChangeEvent();
}
}
// The drag should be stopped outside of the current event handler, otherwise the