Skip to content

Commit

Permalink
fix(slide-toggle): disabled theme not working and dragging works if d…
Browse files Browse the repository at this point in the history
…isabled (#1268)
  • Loading branch information
devversion authored and jelbourn committed Oct 5, 2016
1 parent b91e983 commit 8908366
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 13 deletions.
4 changes: 3 additions & 1 deletion src/lib/slide-toggle/_slide-toggle-theme.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@


@mixin _md-slide-toggle-checked($palette) {
&.md-checked {
// Do not apply the checked colors if the toggle is disabled, because the specificity would be to high for
// the disabled styles.
&.md-checked:not(.md-disabled) {
.md-slide-toggle-thumb {
background-color: md-color($palette);
}
Expand Down
7 changes: 6 additions & 1 deletion src/lib/slide-toggle/slide-toggle.scss
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ $md-slide-toggle-margin: 16px !default;
line-height: $md-slide-toggle-height;

white-space: nowrap;

// Disable user selection to ensure that dragging is smooth without grabbing some elements
// accidentally. Manually prefixing here, because the un-prefixed property is not supported yet.
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;

outline: none;
Expand Down Expand Up @@ -68,7 +74,6 @@ $md-slide-toggle-margin: 16px !default;
height: $md-slide-toggle-height;

position: relative;
user-select: none;

margin-right: 8px;
}
Expand Down
100 changes: 98 additions & 2 deletions src/lib/slide-toggle/slide-toggle.spec.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
import {By} from '@angular/platform-browser';
import {async, ComponentFixture, TestBed, fakeAsync, tick} from '@angular/core/testing';
import {By, HAMMER_GESTURE_CONFIG} from '@angular/platform-browser';
import {Component} from '@angular/core';
import {MdSlideToggle, MdSlideToggleChange, MdSlideToggleModule} from './slide-toggle';
import {FormsModule, NgControl} from '@angular/forms';
import {TestGestureConfig} from '../slider/test-gesture-config';

describe('MdSlideToggle', () => {

let gestureConfig: TestGestureConfig;

beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [MdSlideToggleModule.forRoot(), FormsModule],
declarations: [SlideToggleTestApp, SlideToggleFormsTestApp],
providers: [
{provide: HAMMER_GESTURE_CONFIG, useFactory: () => gestureConfig = new TestGestureConfig()}
]
});

TestBed.compileComponents();
Expand Down Expand Up @@ -392,6 +398,96 @@ describe('MdSlideToggle', () => {

});

describe('with dragging', () => {

let fixture: ComponentFixture<any>;

let testComponent: SlideToggleTestApp;
let slideToggle: MdSlideToggle;
let slideToggleElement: HTMLElement;
let slideToggleControl: NgControl;
let slideThumbContainer: HTMLElement;

beforeEach(async(() => {
fixture = TestBed.createComponent(SlideToggleTestApp);

testComponent = fixture.debugElement.componentInstance;

fixture.detectChanges();

let slideToggleDebug = fixture.debugElement.query(By.css('md-slide-toggle'));
let thumbContainerDebug = slideToggleDebug.query(By.css('.md-slide-toggle-thumb-container'));

slideToggle = slideToggleDebug.componentInstance;
slideToggleElement = slideToggleDebug.nativeElement;
slideToggleControl = slideToggleDebug.injector.get(NgControl);
slideThumbContainer = thumbContainerDebug.nativeElement;
}));

it('should drag from start to end', fakeAsync(() => {
expect(slideToggle.checked).toBe(false);

gestureConfig.emitEventForElement('slidestart', slideThumbContainer);

expect(slideThumbContainer.classList).toContain('md-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(true);
expect(slideThumbContainer.classList).not.toContain('md-dragging');
}));

it('should drag from end to start', fakeAsync(() => {
slideToggle.checked = true;

gestureConfig.emitEventForElement('slidestart', slideThumbContainer);

expect(slideThumbContainer.classList).toContain('md-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(slideThumbContainer.classList).not.toContain('md-dragging');
}));

it('should not drag when disbaled', fakeAsync(() => {
slideToggle.disabled = true;

expect(slideToggle.checked).toBe(false);

gestureConfig.emitEventForElement('slidestart', slideThumbContainer);

expect(slideThumbContainer.classList).not.toContain('md-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(slideThumbContainer.classList).not.toContain('md-dragging');
}));

});

});

/**
Expand Down
22 changes: 14 additions & 8 deletions src/lib/slide-toggle/slide-toggle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,16 +215,24 @@ export class MdSlideToggle implements AfterContentInit, ControlValueAccessor {

/** TODO: internal */
_onDragStart() {
this._slideRenderer.startThumbDrag(this.checked);
if (!this.disabled) {
this._slideRenderer.startThumbDrag(this.checked);
}
}

/** TODO: internal */
_onDrag(event: HammerInput) {
this._slideRenderer.updateThumbPosition(event.deltaX);
if (this._slideRenderer.isDragging()) {
this._slideRenderer.updateThumbPosition(event.deltaX);
}
}

/** TODO: internal */
_onDragEnd() {
if (!this._slideRenderer.isDragging()) {
return;
}

// Notice that we have to stop outside of the current event handler,
// because otherwise the click event will be fired and will reset the new checked variable.
setTimeout(() => {
Expand Down Expand Up @@ -258,7 +266,7 @@ class SlideToggleRenderer {

/** Initializes the drag of the slide-toggle. */
startThumbDrag(checked: boolean) {
if (!this._thumbBarWidth) {
if (!this.isDragging()) {
this._thumbBarWidth = this._thumbBarEl.clientWidth - this._thumbEl.clientWidth;
this._checked = checked;
this._thumbEl.classList.add('md-dragging');
Expand All @@ -267,7 +275,7 @@ class SlideToggleRenderer {

/** Stops the current drag and returns the new checked value. */
stopThumbDrag(): boolean {
if (this._thumbBarWidth) {
if (this.isDragging()) {
this._thumbBarWidth = null;
this._thumbEl.classList.remove('md-dragging');

Expand All @@ -279,10 +287,8 @@ class SlideToggleRenderer {

/** Updates the thumb containers position from the specified distance. */
updateThumbPosition(distance: number) {
if (this._thumbBarWidth) {
this._percentage = this._getThumbPercentage(distance);
applyCssTransform(this._thumbEl, `translate3d(${this._percentage}%, 0, 0)`);
}
this._percentage = this._getThumbPercentage(distance);
applyCssTransform(this._thumbEl, `translate3d(${this._percentage}%, 0, 0)`);
}

/** Retrieves the percentage of thumb from the moved distance. */
Expand Down
2 changes: 1 addition & 1 deletion src/lib/slider/test-gesture-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export class TestGestureConfig extends MdGestureConfig {
* The Angular event plugin for Hammer creates a new HammerManager instance for each listener,
* so we need to apply our event on all instances to hit the correct listener.
*/
emitEventForElement(eventType: string, element: HTMLElement, eventData: Object) {
emitEventForElement(eventType: string, element: HTMLElement, eventData = {}) {
let instances = this.hammerInstances.get(element);
instances.forEach(instance => instance.emit(eventType, eventData));
}
Expand Down

0 comments on commit 8908366

Please sign in to comment.