diff --git a/src/demo-app/slide-toggle/slide-toggle-demo.html b/src/demo-app/slide-toggle/slide-toggle-demo.html
index a1c6192e1563..0a1bcca960fc 100644
--- a/src/demo-app/slide-toggle/slide-toggle-demo.html
+++ b/src/demo-app/slide-toggle/slide-toggle-demo.html
@@ -1,29 +1,29 @@
-
-
-
- Default Slide Toggle
-
-
-
- Disabled Slide Toggle
-
-
-
- Disable Bound
-
-
-
Example where the slide toggle is required inside of a form.
-
-
-
+
+
+
+ Default Slide Toggle
+
+
+
+ Disabled Slide Toggle
+
+
+
+ Disable Bound
+
+
+
Example where the slide toggle is required inside of a form.
+
+
+
\ No newline at end of file
diff --git a/src/demo-app/slide-toggle/slide-toggle-demo.ts b/src/demo-app/slide-toggle/slide-toggle-demo.ts
index 7a8127dc3ac7..26ba4280b577 100644
--- a/src/demo-app/slide-toggle/slide-toggle-demo.ts
+++ b/src/demo-app/slide-toggle/slide-toggle-demo.ts
@@ -1,17 +1,17 @@
-import {Component} from '@angular/core';
-
-
-@Component({
- moduleId: module.id,
- selector: 'switch-demo',
- templateUrl: 'slide-toggle-demo.html',
- styleUrls: ['slide-toggle-demo.css'],
-})
-export class SlideToggleDemo {
- firstToggle: boolean;
-
- onFormSubmit() {
- alert(`You submitted the form.`);
- }
-
-}
+import {Component} from '@angular/core';
+
+
+@Component({
+ moduleId: module.id,
+ selector: 'switch-demo',
+ templateUrl: 'slide-toggle-demo.html',
+ styleUrls: ['slide-toggle-demo.css'],
+})
+export class SlideToggleDemo {
+ firstToggle: boolean;
+
+ onFormSubmit() {
+ alert(`You submitted the form.`);
+ }
+
+}
diff --git a/src/lib/slide-toggle/slide-toggle.html b/src/lib/slide-toggle/slide-toggle.html
index 810fef49b038..ea096cdd8016 100644
--- a/src/lib/slide-toggle/slide-toggle.html
+++ b/src/lib/slide-toggle/slide-toggle.html
@@ -1,33 +1,33 @@
-
+
diff --git a/src/lib/slide-toggle/slide-toggle.scss b/src/lib/slide-toggle/slide-toggle.scss
index 2959da562c5b..90001626e110 100644
--- a/src/lib/slide-toggle/slide-toggle.scss
+++ b/src/lib/slide-toggle/slide-toggle.scss
@@ -1,148 +1,148 @@
-@import '../core/style/variables';
-@import '../core/ripple/ripple';
-@import '../core/style/elevation';
-
-
-$md-slide-toggle-width: 36px !default;
-$md-slide-toggle-height: 24px !default;
-$md-slide-toggle-bar-height: 14px !default;
-$md-slide-toggle-thumb-size: 20px !default;
-$md-slide-toggle-margin: 16px !default;
-$md-slide-toggle-spacing: 8px !default;
-
-
-@mixin md-switch-ripple() {
- // Temporary ripple effect for the thumb of the slide-toggle.
- // Bind to the parent selector and specify the current palette.
- @include md-temporary-ink-ripple(slide-toggle, true);
-}
-
-md-slide-toggle {
- display: flex;
- height: $md-slide-toggle-height;
-
- margin: $md-slide-toggle-margin 0;
- line-height: $md-slide-toggle-height;
-
- white-space: nowrap;
-
- // Disable user selection to ensure that dragging is smooth without grabbing
- // some elements accidentally.
- user-select: none;
-
- outline: none;
-
- &.md-checked {
- .md-slide-toggle-thumb-container {
- transform: translate3d(100%, 0, 0);
- }
- }
-
- @include md-switch-ripple();
-
- &.md-disabled {
-
- .md-slide-toggle-label, .md-slide-toggle-container {
- cursor: default;
- }
- }
-}
-
-// The content element is responsible for the users content.
-// It will apply the given typography styles and align at the end of the slide-toggle.
-.md-slide-toggle-content {
- font-size: $md-body-font-size-base;
- font-family: $md-font-family;
- font-weight: 500;
-}
-
-// The label element is our root container for the slide-toggle / switch indicator and label text.
-// It has to be a label, to support accessibility for the visual hidden input.
-.md-slide-toggle-label {
- display: flex;
- flex: 1;
-
- cursor: pointer;
-}
-
-// Container for the composition of the slide-toggle / switch indicator.
-.md-slide-toggle-container {
- cursor: grab;
- width: $md-slide-toggle-width;
- height: $md-slide-toggle-height;
-
- position: relative;
-
- margin-right: $md-slide-toggle-spacing;
-
- [dir='rtl'] & {
- margin-left: $md-slide-toggle-spacing;
- margin-right: 0;
- }
-}
-
-// The thumb container is responsible for the dragging functionality.
-// It moves around and holds the actual circle as a thumb.
-.md-slide-toggle-thumb-container {
- position: absolute;
- top: $md-slide-toggle-height / 2 - $md-slide-toggle-thumb-size / 2;
- left: 0;
- z-index: 1;
-
- width: $md-slide-toggle-width - $md-slide-toggle-thumb-size;
-
- transform: translate3d(0, 0, 0);
-
- transition: $swift-linear;
- transition-property: transform;
-
- // Once the thumb container is being dragged around, we remove the transition duration to
- // make the drag feeling fast and not delayed.
- &.md-dragging {
- transition-duration: 0ms;
- }
-}
-
-// The thumb will be elevated from the slide-toggle bar.
-// Also the thumb is bound to its parent thumb-container, which manages the movement of the thumb.
-.md-slide-toggle-thumb {
- position: absolute;
- margin: 0;
- left: 0;
- top: 0;
-
- height: $md-slide-toggle-thumb-size;
- width: $md-slide-toggle-thumb-size;
- border-radius: 50%;
-
- @include md-elevation(1);
-}
-
-// Horizontal bar for the slide-toggle.
-// The slide-toggle bar is shown behind the thumb container.
-.md-slide-toggle-bar {
- position: absolute;
- left: 1px;
- top: $md-slide-toggle-height / 2 - $md-slide-toggle-bar-height / 2;
-
- width: $md-slide-toggle-width - 2px;
- height: $md-slide-toggle-bar-height;
-
- border-radius: 8px;
-}
-
-// The slide toggle shows a visually hidden input inside of the component, which is used
-// to take advantage of the native browser functionality.
-.md-slide-toggle-input {
- // Move the input to the bottom and in the middle of the thumb.
- // Visual improvement to properly show browser popups when being required.
- bottom: 0;
- left: $md-slide-toggle-thumb-size / 2;
-}
-
-.md-slide-toggle-bar,
-.md-slide-toggle-thumb {
- transition: $swift-linear;
- transition-property: background-color;
- transition-delay: 50ms;
-}
+@import '../core/style/variables';
+@import '../core/ripple/ripple';
+@import '../core/style/elevation';
+
+
+$md-slide-toggle-width: 36px !default;
+$md-slide-toggle-height: 24px !default;
+$md-slide-toggle-bar-height: 14px !default;
+$md-slide-toggle-thumb-size: 20px !default;
+$md-slide-toggle-margin: 16px !default;
+$md-slide-toggle-spacing: 8px !default;
+
+
+@mixin md-switch-ripple() {
+ // Temporary ripple effect for the thumb of the slide-toggle.
+ // Bind to the parent selector and specify the current palette.
+ @include md-temporary-ink-ripple(slide-toggle, true);
+}
+
+md-slide-toggle {
+ display: flex;
+ height: $md-slide-toggle-height;
+
+ margin: $md-slide-toggle-margin 0;
+ line-height: $md-slide-toggle-height;
+
+ white-space: nowrap;
+
+ // Disable user selection to ensure that dragging is smooth without grabbing
+ // some elements accidentally.
+ user-select: none;
+
+ outline: none;
+
+ &.md-checked {
+ .md-slide-toggle-thumb-container {
+ transform: translate3d(100%, 0, 0);
+ }
+ }
+
+ @include md-switch-ripple();
+
+ &.md-disabled {
+
+ .md-slide-toggle-label, .md-slide-toggle-container {
+ cursor: default;
+ }
+ }
+}
+
+// The content element is responsible for the users content.
+// It will apply the given typography styles and align at the end of the slide-toggle.
+.md-slide-toggle-content {
+ font-size: $md-body-font-size-base;
+ font-family: $md-font-family;
+ font-weight: 500;
+}
+
+// The label element is our root container for the slide-toggle / switch indicator and label text.
+// It has to be a label, to support accessibility for the visual hidden input.
+.md-slide-toggle-label {
+ display: flex;
+ flex: 1;
+
+ cursor: pointer;
+}
+
+// Container for the composition of the slide-toggle / switch indicator.
+.md-slide-toggle-container {
+ cursor: grab;
+ width: $md-slide-toggle-width;
+ height: $md-slide-toggle-height;
+
+ position: relative;
+
+ margin-right: $md-slide-toggle-spacing;
+
+ [dir='rtl'] & {
+ margin-left: $md-slide-toggle-spacing;
+ margin-right: 0;
+ }
+}
+
+// The thumb container is responsible for the dragging functionality.
+// It moves around and holds the actual circle as a thumb.
+.md-slide-toggle-thumb-container {
+ position: absolute;
+ top: $md-slide-toggle-height / 2 - $md-slide-toggle-thumb-size / 2;
+ left: 0;
+ z-index: 1;
+
+ width: $md-slide-toggle-width - $md-slide-toggle-thumb-size;
+
+ transform: translate3d(0, 0, 0);
+
+ transition: $swift-linear;
+ transition-property: transform;
+
+ // Once the thumb container is being dragged around, we remove the transition duration to
+ // make the drag feeling fast and not delayed.
+ &.md-dragging {
+ transition-duration: 0ms;
+ }
+}
+
+// The thumb will be elevated from the slide-toggle bar.
+// Also the thumb is bound to its parent thumb-container, which manages the movement of the thumb.
+.md-slide-toggle-thumb {
+ position: absolute;
+ margin: 0;
+ left: 0;
+ top: 0;
+
+ height: $md-slide-toggle-thumb-size;
+ width: $md-slide-toggle-thumb-size;
+ border-radius: 50%;
+
+ @include md-elevation(1);
+}
+
+// Horizontal bar for the slide-toggle.
+// The slide-toggle bar is shown behind the thumb container.
+.md-slide-toggle-bar {
+ position: absolute;
+ left: 1px;
+ top: $md-slide-toggle-height / 2 - $md-slide-toggle-bar-height / 2;
+
+ width: $md-slide-toggle-width - 2px;
+ height: $md-slide-toggle-bar-height;
+
+ border-radius: 8px;
+}
+
+// The slide toggle shows a visually hidden input inside of the component, which is used
+// to take advantage of the native browser functionality.
+.md-slide-toggle-input {
+ // Move the input to the bottom and in the middle of the thumb.
+ // Visual improvement to properly show browser popups when being required.
+ bottom: 0;
+ left: $md-slide-toggle-thumb-size / 2;
+}
+
+.md-slide-toggle-bar,
+.md-slide-toggle-thumb {
+ transition: $swift-linear;
+ transition-property: background-color;
+ transition-delay: 50ms;
+}
diff --git a/src/lib/slide-toggle/slide-toggle.spec.ts b/src/lib/slide-toggle/slide-toggle.spec.ts
index 754dcd50b44c..c1b8061c3e78 100644
--- a/src/lib/slide-toggle/slide-toggle.spec.ts
+++ b/src/lib/slide-toggle/slide-toggle.spec.ts
@@ -1,574 +1,574 @@
-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();
- }));
-
- describe('basic behavior', () => {
- let fixture: ComponentFixture
;
-
- let testComponent: SlideToggleTestApp;
- let slideToggle: MdSlideToggle;
- let slideToggleElement: HTMLElement;
- let slideToggleControl: NgControl;
- let labelElement: HTMLLabelElement;
- let inputElement: HTMLInputElement;
-
- // This initialization is async() because it needs to wait for ngModel to set the initial value.
- beforeEach(async(() => {
- fixture = TestBed.createComponent(SlideToggleTestApp);
-
- testComponent = fixture.debugElement.componentInstance;
-
- // Enable jasmine spies on event functions, which may trigger at initialization
- // of the slide-toggle component.
- spyOn(fixture.debugElement.componentInstance, 'onSlideChange').and.callThrough();
- spyOn(fixture.debugElement.componentInstance, 'onSlideClick').and.callThrough();
-
- // Initialize the slide-toggle component, by triggering the first change detection cycle.
- fixture.detectChanges();
-
- let slideToggleDebug = fixture.debugElement.query(By.css('md-slide-toggle'));
-
- slideToggle = slideToggleDebug.componentInstance;
- slideToggleElement = slideToggleDebug.nativeElement;
- slideToggleControl = slideToggleDebug.injector.get(NgControl);
- inputElement = fixture.debugElement.query(By.css('input')).nativeElement;
- labelElement = fixture.debugElement.query(By.css('label')).nativeElement;
- }));
-
- // TODO(kara); update when core/testing adds fix
- it('should update the model correctly', async(() => {
- expect(slideToggleElement.classList).not.toContain('md-checked');
-
- testComponent.slideModel = true;
- fixture.detectChanges();
- fixture.whenStable().then(() => {
- fixture.detectChanges();
- expect(slideToggleElement.classList).toContain('md-checked');
- });
-
- }));
-
- it('should apply class based on color attribute', () => {
- testComponent.slideColor = 'primary';
- fixture.detectChanges();
-
- expect(slideToggleElement.classList).toContain('md-primary');
-
- testComponent.slideColor = 'accent';
- fixture.detectChanges();
-
- expect(slideToggleElement.classList).toContain('md-accent');
- });
-
- it('should correctly update the disabled property', () => {
- expect(inputElement.disabled).toBeFalsy();
-
- testComponent.isDisabled = true;
- fixture.detectChanges();
-
- expect(inputElement.disabled).toBeTruthy();
- });
-
- it('should correctly update the checked property', () => {
- expect(slideToggle.checked).toBeFalsy();
-
- testComponent.slideChecked = true;
- fixture.detectChanges();
-
- expect(inputElement.checked).toBeTruthy();
- });
-
- it('should set the toggle to checked on click', () => {
- expect(slideToggle.checked).toBe(false);
- expect(slideToggleElement.classList).not.toContain('md-checked');
-
- labelElement.click();
- fixture.detectChanges();
-
- expect(slideToggleElement.classList).toContain('md-checked');
- expect(slideToggle.checked).toBe(true);
- });
-
- it('should not trigger the click event multiple times', () => {
- // By default, when clicking on a label element, a generated click will be dispatched
- // on the associated input element.
- // Since we're using a label element and a visual hidden input, this behavior can led
- // to an issue, where the click events on the slide-toggle are getting executed twice.
-
- expect(slideToggle.checked).toBe(false);
- expect(slideToggleElement.classList).not.toContain('md-checked');
-
- labelElement.click();
- fixture.detectChanges();
-
- expect(slideToggleElement.classList).toContain('md-checked');
- expect(slideToggle.checked).toBe(true);
- expect(testComponent.onSlideClick).toHaveBeenCalledTimes(1);
- });
-
- it('should trigger the change event properly', () => {
- expect(inputElement.checked).toBe(false);
- expect(slideToggleElement.classList).not.toContain('md-checked');
-
- labelElement.click();
- fixture.detectChanges();
-
- expect(inputElement.checked).toBe(true);
- expect(slideToggleElement.classList).toContain('md-checked');
- expect(testComponent.onSlideChange).toHaveBeenCalledTimes(1);
- });
-
- it('should not trigger the change event by changing the native value', async(() => {
- expect(inputElement.checked).toBe(false);
- expect(slideToggleElement.classList).not.toContain('md-checked');
-
- testComponent.slideChecked = true;
- fixture.detectChanges();
-
- expect(inputElement.checked).toBe(true);
- expect(slideToggleElement.classList).toContain('md-checked');
-
- // The change event shouldn't fire because the value change was not caused
- // by any interaction. Use whenStable to ensure an event isn't fired asynchronously.
- fixture.whenStable().then(() => {
- expect(testComponent.onSlideChange).not.toHaveBeenCalled();
- });
- }));
-
- it('should not trigger the change event on initialization', async(() => {
- expect(inputElement.checked).toBe(false);
- expect(slideToggleElement.classList).not.toContain('md-checked');
-
- testComponent.slideChecked = true;
- fixture.detectChanges();
-
- expect(inputElement.checked).toBe(true);
- expect(slideToggleElement.classList).toContain('md-checked');
-
- // The change event shouldn't fire, because the native input element is not focused.
- // Use whenStable to ensure an event isn't fired asynchronously.
- fixture.whenStable().then(() => {
- expect(testComponent.onSlideChange).not.toHaveBeenCalled();
- });
- }));
-
- it('should add a suffix to the inputs id', () => {
- testComponent.slideId = 'myId';
- fixture.detectChanges();
-
- expect(inputElement.id).toBe('myId-input');
-
- testComponent.slideId = 'nextId';
- fixture.detectChanges();
-
- expect(inputElement.id).toBe('nextId-input');
-
- testComponent.slideId = null;
- fixture.detectChanges();
-
- // Once the id input is falsy, we use a default prefix with a incrementing unique number.
- expect(inputElement.id).toMatch(/md-slide-toggle-[0-9]+-input/g);
- });
-
- it('should forward the specified name to the input', () => {
- testComponent.slideName = 'myName';
- fixture.detectChanges();
-
- expect(inputElement.name).toBe('myName');
-
- testComponent.slideName = 'nextName';
- fixture.detectChanges();
-
- expect(inputElement.name).toBe('nextName');
-
- testComponent.slideName = null;
- fixture.detectChanges();
-
- expect(inputElement.name).toBe('');
- });
-
- it('should forward the aria-label attribute to the input', () => {
- testComponent.slideLabel = 'ariaLabel';
- fixture.detectChanges();
-
- expect(inputElement.getAttribute('aria-label')).toBe('ariaLabel');
-
- testComponent.slideLabel = null;
- fixture.detectChanges();
-
- expect(inputElement.hasAttribute('aria-label')).toBeFalsy();
- });
-
- it('should forward the aria-labelledby attribute to the input', () => {
- testComponent.slideLabelledBy = 'ariaLabelledBy';
- fixture.detectChanges();
-
- expect(inputElement.getAttribute('aria-labelledby')).toBe('ariaLabelledBy');
-
- testComponent.slideLabelledBy = null;
- fixture.detectChanges();
-
- expect(inputElement.hasAttribute('aria-labelledby')).toBeFalsy();
- });
-
- it('should be initially set to ng-pristine', () => {
- expect(slideToggleElement.classList).toContain('ng-pristine');
- expect(slideToggleElement.classList).not.toContain('ng-dirty');
- });
-
- it('should emit the new values properly', async(() => {
- labelElement.click();
- fixture.detectChanges();
-
- fixture.whenStable().then(() => {
- // We're checking the arguments type / emitted value to be a boolean, because sometimes the
- // emitted value can be a DOM Event, which is not valid.
- // See angular/angular#4059
- expect(testComponent.lastEvent.checked).toBe(true);
- });
- }));
-
- it('should support subscription on the change observable', () => {
- slideToggle.change.subscribe((event: MdSlideToggleChange) => {
- expect(event.checked).toBe(true);
- });
-
- slideToggle.toggle();
- fixture.detectChanges();
- });
-
- it('should have the correct control state initially and after interaction', () => {
- // The control should start off valid, pristine, and untouched.
- expect(slideToggleControl.valid).toBe(true);
- expect(slideToggleControl.pristine).toBe(true);
- expect(slideToggleControl.touched).toBe(false);
-
- // After changing the value programmatically, the control should
- // become dirty (not pristine), but remain untouched.
- slideToggle.checked = true;
- fixture.detectChanges();
-
- expect(slideToggleControl.valid).toBe(true);
- expect(slideToggleControl.pristine).toBe(false);
- expect(slideToggleControl.touched).toBe(false);
-
- // After a user interaction occurs (such as a click), the control should remain dirty and
- // now also be touched.
- labelElement.click();
- fixture.detectChanges();
-
- expect(slideToggleControl.valid).toBe(true);
- expect(slideToggleControl.pristine).toBe(false);
- expect(slideToggleControl.touched).toBe(true);
- });
-
- it('should not set the control to touched when changing the state programmatically', () => {
- // The control should start off with being untouched.
- expect(slideToggleControl.touched).toBe(false);
-
- testComponent.slideChecked = true;
- fixture.detectChanges();
-
- expect(slideToggleControl.touched).toBe(false);
- expect(slideToggleElement.classList).toContain('md-checked');
-
- // After a user interaction occurs (such as a click), the control should remain dirty and
- // now also be touched.
- inputElement.click();
- fixture.detectChanges();
-
- expect(slideToggleControl.touched).toBe(true);
- expect(slideToggleElement.classList).not.toContain('md-checked');
- });
-
- // TODO(kara): update when core/testing adds fix
- it('should not set the control to touched when changing the model', async(() => {
- // The control should start off with being untouched.
- expect(slideToggleControl.touched).toBe(false);
-
- testComponent.slideModel = true;
- fixture.detectChanges();
- fixture.whenStable().then(() => {
- fixture.detectChanges();
- expect(slideToggleControl.touched).toBe(false);
- expect(slideToggle.checked).toBe(true);
- expect(slideToggleElement.classList).toContain('md-checked');
- });
- }));
-
- it('should correctly set the slide-toggle to checked on focus', () => {
- expect(slideToggleElement.classList).not.toContain('md-slide-toggle-focused');
-
- dispatchFocusChangeEvent('focus', inputElement);
- fixture.detectChanges();
-
- expect(slideToggleElement.classList).toContain('md-slide-toggle-focused');
- });
-
- it('should forward the required attribute', () => {
- testComponent.isRequired = true;
- fixture.detectChanges();
-
- expect(inputElement.required).toBe(true);
-
- testComponent.isRequired = false;
- fixture.detectChanges();
-
- expect(inputElement.required).toBe(false);
- });
-
- });
-
- describe('custom template', () => {
- it('should not trigger the change event on initialization', async(() => {
- let fixture = TestBed.createComponent(SlideToggleTestApp);
- fixture.componentInstance.slideModel = true;
- fixture.componentInstance.slideChecked = true;
- fixture.detectChanges();
-
- expect(fixture.componentInstance.lastEvent).toBeFalsy();
- }));
- });
-
- describe('with forms', () => {
-
- let fixture: ComponentFixture;
- let testComponent: SlideToggleFormsTestApp;
- let buttonElement: HTMLButtonElement;
- let labelElement: HTMLLabelElement;
- let inputElement: HTMLInputElement;
-
- // This initialization is async() because it needs to wait for ngModel to set the initial value.
- beforeEach(async(() => {
- fixture = TestBed.createComponent(SlideToggleFormsTestApp);
-
- testComponent = fixture.debugElement.componentInstance;
-
- fixture.detectChanges();
-
- buttonElement = fixture.debugElement.query(By.css('button')).nativeElement;
- labelElement = fixture.debugElement.query(By.css('label')).nativeElement;
- inputElement = fixture.debugElement.query(By.css('input')).nativeElement;
- }));
-
- it('should prevent the form from submit when being required', () => {
-
- if ('reportValidity' in inputElement === false) {
- // If the browser does not report the validity then the tests will break.
- // e.g Safari 8 on Mobile.
- return;
- }
-
- testComponent.isRequired = true;
-
- fixture.detectChanges();
-
- buttonElement.click();
- fixture.detectChanges();
-
- expect(testComponent.isSubmitted).toBe(false);
-
- testComponent.isRequired = false;
- fixture.detectChanges();
-
- buttonElement.click();
- fixture.detectChanges();
-
- expect(testComponent.isSubmitted).toBe(true);
- });
-
- });
-
- describe('with dragging', () => {
-
- let fixture: ComponentFixture;
-
- 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');
- }));
-
- it('should should emit a change event after drag', 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');
- expect(testComponent.lastEvent.checked).toBe(true);
- }));
-
- });
-
-});
-
-/**
- * Dispatches a focus change event from an element.
- * @param eventName Name of the event, either 'focus' or 'blur'.
- * @param element The element from which the event will be dispatched.
- */
-function dispatchFocusChangeEvent(eventName: string, element: HTMLElement): void {
- let event = document.createEvent('Event');
- event.initEvent(eventName, true, true);
- element.dispatchEvent(event);
-}
-
-@Component({
- selector: 'slide-toggle-test-app',
- template: `
-
-
- Test Slide Toggle
-
- `,
-})
-class SlideToggleTestApp {
- isDisabled: boolean = false;
- isRequired: boolean = false;
- slideModel: boolean = false;
- slideChecked: boolean = false;
- slideColor: string;
- slideId: string;
- slideName: string;
- slideLabel: string;
- slideLabelledBy: string;
- lastEvent: MdSlideToggleChange;
-
- onSlideClick(event: Event) {}
- onSlideChange(event: MdSlideToggleChange) {
- this.lastEvent = event;
- }
-}
-
-
-@Component({
- selector: 'slide-toggle-forms-test-app',
- template: `
- `
-})
-class SlideToggleFormsTestApp {
- isSubmitted: boolean = false;
- isRequired: boolean = false;
-}
+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();
+ }));
+
+ describe('basic behavior', () => {
+ let fixture: ComponentFixture;
+
+ let testComponent: SlideToggleTestApp;
+ let slideToggle: MdSlideToggle;
+ let slideToggleElement: HTMLElement;
+ let slideToggleControl: NgControl;
+ let labelElement: HTMLLabelElement;
+ let inputElement: HTMLInputElement;
+
+ // This initialization is async() because it needs to wait for ngModel to set the initial value.
+ beforeEach(async(() => {
+ fixture = TestBed.createComponent(SlideToggleTestApp);
+
+ testComponent = fixture.debugElement.componentInstance;
+
+ // Enable jasmine spies on event functions, which may trigger at initialization
+ // of the slide-toggle component.
+ spyOn(fixture.debugElement.componentInstance, 'onSlideChange').and.callThrough();
+ spyOn(fixture.debugElement.componentInstance, 'onSlideClick').and.callThrough();
+
+ // Initialize the slide-toggle component, by triggering the first change detection cycle.
+ fixture.detectChanges();
+
+ let slideToggleDebug = fixture.debugElement.query(By.css('md-slide-toggle'));
+
+ slideToggle = slideToggleDebug.componentInstance;
+ slideToggleElement = slideToggleDebug.nativeElement;
+ slideToggleControl = slideToggleDebug.injector.get(NgControl);
+ inputElement = fixture.debugElement.query(By.css('input')).nativeElement;
+ labelElement = fixture.debugElement.query(By.css('label')).nativeElement;
+ }));
+
+ // TODO(kara); update when core/testing adds fix
+ it('should update the model correctly', async(() => {
+ expect(slideToggleElement.classList).not.toContain('md-checked');
+
+ testComponent.slideModel = true;
+ fixture.detectChanges();
+ fixture.whenStable().then(() => {
+ fixture.detectChanges();
+ expect(slideToggleElement.classList).toContain('md-checked');
+ });
+
+ }));
+
+ it('should apply class based on color attribute', () => {
+ testComponent.slideColor = 'primary';
+ fixture.detectChanges();
+
+ expect(slideToggleElement.classList).toContain('md-primary');
+
+ testComponent.slideColor = 'accent';
+ fixture.detectChanges();
+
+ expect(slideToggleElement.classList).toContain('md-accent');
+ });
+
+ it('should correctly update the disabled property', () => {
+ expect(inputElement.disabled).toBeFalsy();
+
+ testComponent.isDisabled = true;
+ fixture.detectChanges();
+
+ expect(inputElement.disabled).toBeTruthy();
+ });
+
+ it('should correctly update the checked property', () => {
+ expect(slideToggle.checked).toBeFalsy();
+
+ testComponent.slideChecked = true;
+ fixture.detectChanges();
+
+ expect(inputElement.checked).toBeTruthy();
+ });
+
+ it('should set the toggle to checked on click', () => {
+ expect(slideToggle.checked).toBe(false);
+ expect(slideToggleElement.classList).not.toContain('md-checked');
+
+ labelElement.click();
+ fixture.detectChanges();
+
+ expect(slideToggleElement.classList).toContain('md-checked');
+ expect(slideToggle.checked).toBe(true);
+ });
+
+ it('should not trigger the click event multiple times', () => {
+ // By default, when clicking on a label element, a generated click will be dispatched
+ // on the associated input element.
+ // Since we're using a label element and a visual hidden input, this behavior can led
+ // to an issue, where the click events on the slide-toggle are getting executed twice.
+
+ expect(slideToggle.checked).toBe(false);
+ expect(slideToggleElement.classList).not.toContain('md-checked');
+
+ labelElement.click();
+ fixture.detectChanges();
+
+ expect(slideToggleElement.classList).toContain('md-checked');
+ expect(slideToggle.checked).toBe(true);
+ expect(testComponent.onSlideClick).toHaveBeenCalledTimes(1);
+ });
+
+ it('should trigger the change event properly', () => {
+ expect(inputElement.checked).toBe(false);
+ expect(slideToggleElement.classList).not.toContain('md-checked');
+
+ labelElement.click();
+ fixture.detectChanges();
+
+ expect(inputElement.checked).toBe(true);
+ expect(slideToggleElement.classList).toContain('md-checked');
+ expect(testComponent.onSlideChange).toHaveBeenCalledTimes(1);
+ });
+
+ it('should not trigger the change event by changing the native value', async(() => {
+ expect(inputElement.checked).toBe(false);
+ expect(slideToggleElement.classList).not.toContain('md-checked');
+
+ testComponent.slideChecked = true;
+ fixture.detectChanges();
+
+ expect(inputElement.checked).toBe(true);
+ expect(slideToggleElement.classList).toContain('md-checked');
+
+ // The change event shouldn't fire because the value change was not caused
+ // by any interaction. Use whenStable to ensure an event isn't fired asynchronously.
+ fixture.whenStable().then(() => {
+ expect(testComponent.onSlideChange).not.toHaveBeenCalled();
+ });
+ }));
+
+ it('should not trigger the change event on initialization', async(() => {
+ expect(inputElement.checked).toBe(false);
+ expect(slideToggleElement.classList).not.toContain('md-checked');
+
+ testComponent.slideChecked = true;
+ fixture.detectChanges();
+
+ expect(inputElement.checked).toBe(true);
+ expect(slideToggleElement.classList).toContain('md-checked');
+
+ // The change event shouldn't fire, because the native input element is not focused.
+ // Use whenStable to ensure an event isn't fired asynchronously.
+ fixture.whenStable().then(() => {
+ expect(testComponent.onSlideChange).not.toHaveBeenCalled();
+ });
+ }));
+
+ it('should add a suffix to the inputs id', () => {
+ testComponent.slideId = 'myId';
+ fixture.detectChanges();
+
+ expect(inputElement.id).toBe('myId-input');
+
+ testComponent.slideId = 'nextId';
+ fixture.detectChanges();
+
+ expect(inputElement.id).toBe('nextId-input');
+
+ testComponent.slideId = null;
+ fixture.detectChanges();
+
+ // Once the id input is falsy, we use a default prefix with a incrementing unique number.
+ expect(inputElement.id).toMatch(/md-slide-toggle-[0-9]+-input/g);
+ });
+
+ it('should forward the specified name to the input', () => {
+ testComponent.slideName = 'myName';
+ fixture.detectChanges();
+
+ expect(inputElement.name).toBe('myName');
+
+ testComponent.slideName = 'nextName';
+ fixture.detectChanges();
+
+ expect(inputElement.name).toBe('nextName');
+
+ testComponent.slideName = null;
+ fixture.detectChanges();
+
+ expect(inputElement.name).toBe('');
+ });
+
+ it('should forward the aria-label attribute to the input', () => {
+ testComponent.slideLabel = 'ariaLabel';
+ fixture.detectChanges();
+
+ expect(inputElement.getAttribute('aria-label')).toBe('ariaLabel');
+
+ testComponent.slideLabel = null;
+ fixture.detectChanges();
+
+ expect(inputElement.hasAttribute('aria-label')).toBeFalsy();
+ });
+
+ it('should forward the aria-labelledby attribute to the input', () => {
+ testComponent.slideLabelledBy = 'ariaLabelledBy';
+ fixture.detectChanges();
+
+ expect(inputElement.getAttribute('aria-labelledby')).toBe('ariaLabelledBy');
+
+ testComponent.slideLabelledBy = null;
+ fixture.detectChanges();
+
+ expect(inputElement.hasAttribute('aria-labelledby')).toBeFalsy();
+ });
+
+ it('should be initially set to ng-pristine', () => {
+ expect(slideToggleElement.classList).toContain('ng-pristine');
+ expect(slideToggleElement.classList).not.toContain('ng-dirty');
+ });
+
+ it('should emit the new values properly', async(() => {
+ labelElement.click();
+ fixture.detectChanges();
+
+ fixture.whenStable().then(() => {
+ // We're checking the arguments type / emitted value to be a boolean, because sometimes the
+ // emitted value can be a DOM Event, which is not valid.
+ // See angular/angular#4059
+ expect(testComponent.lastEvent.checked).toBe(true);
+ });
+ }));
+
+ it('should support subscription on the change observable', () => {
+ slideToggle.change.subscribe((event: MdSlideToggleChange) => {
+ expect(event.checked).toBe(true);
+ });
+
+ slideToggle.toggle();
+ fixture.detectChanges();
+ });
+
+ it('should have the correct control state initially and after interaction', () => {
+ // The control should start off valid, pristine, and untouched.
+ expect(slideToggleControl.valid).toBe(true);
+ expect(slideToggleControl.pristine).toBe(true);
+ expect(slideToggleControl.touched).toBe(false);
+
+ // After changing the value programmatically, the control should
+ // become dirty (not pristine), but remain untouched.
+ slideToggle.checked = true;
+ fixture.detectChanges();
+
+ expect(slideToggleControl.valid).toBe(true);
+ expect(slideToggleControl.pristine).toBe(false);
+ expect(slideToggleControl.touched).toBe(false);
+
+ // After a user interaction occurs (such as a click), the control should remain dirty and
+ // now also be touched.
+ labelElement.click();
+ fixture.detectChanges();
+
+ expect(slideToggleControl.valid).toBe(true);
+ expect(slideToggleControl.pristine).toBe(false);
+ expect(slideToggleControl.touched).toBe(true);
+ });
+
+ it('should not set the control to touched when changing the state programmatically', () => {
+ // The control should start off with being untouched.
+ expect(slideToggleControl.touched).toBe(false);
+
+ testComponent.slideChecked = true;
+ fixture.detectChanges();
+
+ expect(slideToggleControl.touched).toBe(false);
+ expect(slideToggleElement.classList).toContain('md-checked');
+
+ // After a user interaction occurs (such as a click), the control should remain dirty and
+ // now also be touched.
+ inputElement.click();
+ fixture.detectChanges();
+
+ expect(slideToggleControl.touched).toBe(true);
+ expect(slideToggleElement.classList).not.toContain('md-checked');
+ });
+
+ // TODO(kara): update when core/testing adds fix
+ it('should not set the control to touched when changing the model', async(() => {
+ // The control should start off with being untouched.
+ expect(slideToggleControl.touched).toBe(false);
+
+ testComponent.slideModel = true;
+ fixture.detectChanges();
+ fixture.whenStable().then(() => {
+ fixture.detectChanges();
+ expect(slideToggleControl.touched).toBe(false);
+ expect(slideToggle.checked).toBe(true);
+ expect(slideToggleElement.classList).toContain('md-checked');
+ });
+ }));
+
+ it('should correctly set the slide-toggle to checked on focus', () => {
+ expect(slideToggleElement.classList).not.toContain('md-slide-toggle-focused');
+
+ dispatchFocusChangeEvent('focus', inputElement);
+ fixture.detectChanges();
+
+ expect(slideToggleElement.classList).toContain('md-slide-toggle-focused');
+ });
+
+ it('should forward the required attribute', () => {
+ testComponent.isRequired = true;
+ fixture.detectChanges();
+
+ expect(inputElement.required).toBe(true);
+
+ testComponent.isRequired = false;
+ fixture.detectChanges();
+
+ expect(inputElement.required).toBe(false);
+ });
+
+ });
+
+ describe('custom template', () => {
+ it('should not trigger the change event on initialization', async(() => {
+ let fixture = TestBed.createComponent(SlideToggleTestApp);
+ fixture.componentInstance.slideModel = true;
+ fixture.componentInstance.slideChecked = true;
+ fixture.detectChanges();
+
+ expect(fixture.componentInstance.lastEvent).toBeFalsy();
+ }));
+ });
+
+ describe('with forms', () => {
+
+ let fixture: ComponentFixture;
+ let testComponent: SlideToggleFormsTestApp;
+ let buttonElement: HTMLButtonElement;
+ let labelElement: HTMLLabelElement;
+ let inputElement: HTMLInputElement;
+
+ // This initialization is async() because it needs to wait for ngModel to set the initial value.
+ beforeEach(async(() => {
+ fixture = TestBed.createComponent(SlideToggleFormsTestApp);
+
+ testComponent = fixture.debugElement.componentInstance;
+
+ fixture.detectChanges();
+
+ buttonElement = fixture.debugElement.query(By.css('button')).nativeElement;
+ labelElement = fixture.debugElement.query(By.css('label')).nativeElement;
+ inputElement = fixture.debugElement.query(By.css('input')).nativeElement;
+ }));
+
+ it('should prevent the form from submit when being required', () => {
+
+ if ('reportValidity' in inputElement === false) {
+ // If the browser does not report the validity then the tests will break.
+ // e.g Safari 8 on Mobile.
+ return;
+ }
+
+ testComponent.isRequired = true;
+
+ fixture.detectChanges();
+
+ buttonElement.click();
+ fixture.detectChanges();
+
+ expect(testComponent.isSubmitted).toBe(false);
+
+ testComponent.isRequired = false;
+ fixture.detectChanges();
+
+ buttonElement.click();
+ fixture.detectChanges();
+
+ expect(testComponent.isSubmitted).toBe(true);
+ });
+
+ });
+
+ describe('with dragging', () => {
+
+ let fixture: ComponentFixture;
+
+ 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');
+ }));
+
+ it('should should emit a change event after drag', 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');
+ expect(testComponent.lastEvent.checked).toBe(true);
+ }));
+
+ });
+
+});
+
+/**
+ * Dispatches a focus change event from an element.
+ * @param eventName Name of the event, either 'focus' or 'blur'.
+ * @param element The element from which the event will be dispatched.
+ */
+function dispatchFocusChangeEvent(eventName: string, element: HTMLElement): void {
+ let event = document.createEvent('Event');
+ event.initEvent(eventName, true, true);
+ element.dispatchEvent(event);
+}
+
+@Component({
+ selector: 'slide-toggle-test-app',
+ template: `
+
+
+ Test Slide Toggle
+
+ `,
+})
+class SlideToggleTestApp {
+ isDisabled: boolean = false;
+ isRequired: boolean = false;
+ slideModel: boolean = false;
+ slideChecked: boolean = false;
+ slideColor: string;
+ slideId: string;
+ slideName: string;
+ slideLabel: string;
+ slideLabelledBy: string;
+ lastEvent: MdSlideToggleChange;
+
+ onSlideClick(event: Event) {}
+ onSlideChange(event: MdSlideToggleChange) {
+ this.lastEvent = event;
+ }
+}
+
+
+@Component({
+ selector: 'slide-toggle-forms-test-app',
+ template: `
+ `
+})
+class SlideToggleFormsTestApp {
+ isSubmitted: boolean = false;
+ isRequired: boolean = false;
+}