From fdf84772b95bfbe8c76d87a4869299528a52dab6 Mon Sep 17 00:00:00 2001 From: Miles Malerba Date: Wed, 26 Oct 2016 15:44:41 -0700 Subject: [PATCH 1/2] Fix(slider): Add support for change event. Also clean up test slide event test code. Closes #1070 --- src/lib/slider/slider.spec.ts | 201 ++++++++++++++++++++++------------ src/lib/slider/slider.ts | 30 ++++- 2 files changed, 158 insertions(+), 73 deletions(-) diff --git a/src/lib/slider/slider.spec.ts b/src/lib/slider/slider.spec.ts index 149d37f0ad12..188107e1acc0 100644 --- a/src/lib/slider/slider.spec.ts +++ b/src/lib/slider/slider.spec.ts @@ -26,6 +26,7 @@ describe('MdSlider', () => { SliderWithTwoWayBinding, SliderWithValueSmallerThanMin, SliderWithValueGreaterThanMax, + SliderWithChangeHandler, ], providers: [ {provide: HAMMER_GESTURE_CONFIG, useFactory: () => { @@ -75,33 +76,33 @@ describe('MdSlider', () => { it('should update the value on a click', () => { expect(sliderInstance.value).toBe(0); - dispatchClickEvent(sliderTrackElement, 0.19); + dispatchClickEvent(sliderTrackElement, sliderNativeElement, 0.19); // The expected value is 19 from: percentage * difference of max and min. expect(sliderInstance.value).toBe(19); }); it('should update the value on a slide', () => { expect(sliderInstance.value).toBe(0); - dispatchSlideEvent(sliderTrackElement, sliderNativeElement, 0, 0.89, gestureConfig); + dispatchSlideEventSequence(sliderTrackElement, sliderNativeElement, 0, 0.89, gestureConfig); // The expected value is 89 from: percentage * difference of max and min. expect(sliderInstance.value).toBe(89); }); it('should set the value as min when sliding before the track', () => { expect(sliderInstance.value).toBe(0); - dispatchSlideEvent(sliderTrackElement, sliderNativeElement, 0, -1.33, gestureConfig); + dispatchSlideEventSequence(sliderTrackElement, sliderNativeElement, 0, -1.33, gestureConfig); expect(sliderInstance.value).toBe(0); }); it('should set the value as max when sliding past the track', () => { expect(sliderInstance.value).toBe(0); - dispatchSlideEvent(sliderTrackElement, sliderNativeElement, 0, 1.75, gestureConfig); + dispatchSlideEventSequence(sliderTrackElement, sliderNativeElement, 0, 1.75, gestureConfig); expect(sliderInstance.value).toBe(100); }); it('should update the track fill on click', () => { expect(trackFillDimensions.width).toBe(0); - dispatchClickEvent(sliderTrackElement, 0.39); + dispatchClickEvent(sliderTrackElement, sliderNativeElement, 0.39); trackFillDimensions = trackFillElement.getBoundingClientRect(); thumbDimensions = thumbElement.getBoundingClientRect(); @@ -117,7 +118,7 @@ describe('MdSlider', () => { expect(thumbDimensions.left).toBe(sliderDimensions.left); // 50% is used here because the click event that is dispatched truncates the position and so // a value had to be used that would not be truncated. - dispatchClickEvent(sliderTrackElement, 0.5); + dispatchClickEvent(sliderTrackElement, sliderNativeElement, 0.5); thumbDimensions = thumbElement.getBoundingClientRect(); // The thumb position should be at 50% of the slider's width + the offset of the slider. @@ -127,7 +128,7 @@ describe('MdSlider', () => { it('should update the track fill on slide', () => { expect(trackFillDimensions.width).toBe(0); - dispatchSlideEvent(sliderTrackElement, sliderNativeElement, 0, 0.86, gestureConfig); + dispatchSlideEventSequence(sliderTrackElement, sliderNativeElement, 0, 0.86, gestureConfig); trackFillDimensions = trackFillElement.getBoundingClientRect(); thumbDimensions = thumbElement.getBoundingClientRect(); @@ -143,7 +144,7 @@ describe('MdSlider', () => { expect(thumbDimensions.left).toBe(sliderDimensions.left); // The slide event also truncates the position passed in, so 50% is used here as well to // ensure the ability to calculate the expected position. - dispatchSlideEvent(sliderTrackElement, sliderNativeElement, 0, 0.5, gestureConfig); + dispatchSlideEventSequence(sliderTrackElement, sliderNativeElement, 0, 0.5, gestureConfig); thumbDimensions = thumbElement.getBoundingClientRect(); expect(thumbDimensions.left).toBe(sliderDimensions.width * 0.5 + sliderDimensions.left); @@ -153,7 +154,7 @@ describe('MdSlider', () => { let containerElement = sliderNativeElement.querySelector('.md-slider-container'); expect(containerElement.classList).not.toContain('md-slider-active'); - dispatchClickEvent(sliderNativeElement, 0.23); + dispatchClickEvent(sliderTrackElement, sliderNativeElement, 0.23); fixture.detectChanges(); expect(containerElement.classList).toContain('md-slider-active'); @@ -162,7 +163,7 @@ describe('MdSlider', () => { it('should remove the md-slider-active class on blur', () => { let containerElement = sliderNativeElement.querySelector('.md-slider-container'); - dispatchClickEvent(sliderNativeElement, 0.95); + dispatchClickEvent(sliderTrackElement, sliderNativeElement, 0.95); fixture.detectChanges(); expect(containerElement.classList).toContain('md-slider-active'); @@ -178,12 +179,12 @@ describe('MdSlider', () => { let containerElement = sliderNativeElement.querySelector('.md-slider-container'); expect(containerElement.classList).not.toContain('md-slider-sliding'); - dispatchSlideStartEvent(sliderNativeElement, 0, gestureConfig); + dispatchSlideStartEvent(sliderTrackElement, sliderNativeElement, 0, gestureConfig); fixture.detectChanges(); expect(containerElement.classList).toContain('md-slider-sliding'); - dispatchSlideEndEvent(sliderNativeElement, 0.34, gestureConfig); + dispatchSlideEndEvent(sliderTrackElement, sliderNativeElement, 0.34, gestureConfig); fixture.detectChanges(); expect(containerElement.classList).not.toContain('md-slider-sliding'); @@ -194,6 +195,7 @@ describe('MdSlider', () => { let fixture: ComponentFixture; let sliderDebugElement: DebugElement; let sliderNativeElement: HTMLElement; + let sliderTrackElement: HTMLElement; let sliderInstance: MdSlider; beforeEach(() => { @@ -202,6 +204,7 @@ describe('MdSlider', () => { sliderDebugElement = fixture.debugElement.query(By.directive(MdSlider)); sliderNativeElement = sliderDebugElement.nativeElement; + sliderTrackElement = sliderNativeElement.querySelector('.md-slider-track'); sliderInstance = sliderDebugElement.componentInstance; }); @@ -211,13 +214,13 @@ describe('MdSlider', () => { it('should not change the value on click when disabled', () => { expect(sliderInstance.value).toBe(0); - dispatchClickEvent(sliderNativeElement, 0.63); + dispatchClickEvent(sliderTrackElement, sliderNativeElement, 0.63); expect(sliderInstance.value).toBe(0); }); it('should not change the value on slide when disabled', () => { expect(sliderInstance.value).toBe(0); - dispatchSlideEvent(sliderNativeElement, sliderNativeElement, 0, 0.5, gestureConfig); + dispatchSlideEventSequence(sliderNativeElement, sliderNativeElement, 0, 0.5, gestureConfig); expect(sliderInstance.value).toBe(0); }); @@ -225,7 +228,7 @@ describe('MdSlider', () => { let containerElement = sliderNativeElement.querySelector('.md-slider-container'); expect(containerElement.classList).not.toContain('md-slider-active'); - dispatchClickEvent(sliderNativeElement, 0.43); + dispatchClickEvent(sliderTrackElement, sliderNativeElement, 0.43); fixture.detectChanges(); expect(containerElement.classList).not.toContain('md-slider-active'); @@ -235,7 +238,7 @@ describe('MdSlider', () => { let containerElement = sliderNativeElement.querySelector('.md-slider-container'); expect(containerElement.classList).not.toContain('md-slider-sliding'); - dispatchSlideStartEvent(sliderNativeElement, 0.46, gestureConfig); + dispatchSlideStartEvent(sliderTrackElement, sliderNativeElement, 0.46, gestureConfig); fixture.detectChanges(); expect(containerElement.classList).not.toContain('md-slider-sliding'); @@ -277,7 +280,7 @@ describe('MdSlider', () => { }); it('should set the correct value on click', () => { - dispatchClickEvent(sliderTrackElement, 0.09); + dispatchClickEvent(sliderTrackElement, sliderNativeElement, 0.09); // Computed by multiplying the difference between the min and the max by the percentage from // the click and adding that to the minimum. let value = Math.round(4 + (0.09 * (6 - 4))); @@ -285,7 +288,7 @@ describe('MdSlider', () => { }); it('should set the correct value on slide', () => { - dispatchSlideEvent(sliderTrackElement, sliderNativeElement, 0, 0.62, gestureConfig); + dispatchSlideEventSequence(sliderTrackElement, sliderNativeElement, 0, 0.62, gestureConfig); // Computed by multiplying the difference between the min and the max by the percentage from // the click and adding that to the minimum. let value = Math.round(4 + (0.62 * (6 - 4))); @@ -293,7 +296,7 @@ describe('MdSlider', () => { }); it('should snap the thumb and fill to the nearest value on click', () => { - dispatchClickEvent(sliderTrackElement, 0.68); + dispatchClickEvent(sliderTrackElement, sliderNativeElement, 0.68); fixture.detectChanges(); let trackFillDimensions = trackFillElement.getBoundingClientRect(); @@ -306,10 +309,7 @@ describe('MdSlider', () => { }); it('should snap the thumb and fill to the nearest value on slide', () => { - dispatchSlideEvent(sliderTrackElement, sliderNativeElement, 0, 0.74, gestureConfig); - fixture.detectChanges(); - - dispatchSlideEndEvent(sliderNativeElement, 0.74, gestureConfig); + dispatchSlideEventSequence(sliderTrackElement, sliderNativeElement, 0, 0.74, gestureConfig); fixture.detectChanges(); let trackFillDimensions = trackFillElement.getBoundingClientRect(); @@ -377,14 +377,14 @@ describe('MdSlider', () => { }); it('should set the correct value on click', () => { - dispatchClickEvent(sliderTrackElement, 0.92); + dispatchClickEvent(sliderTrackElement, sliderNativeElement, 0.92); // On a slider with default max and min the value should be approximately equal to the // percentage clicked. This should be the case regardless of what the original set value was. expect(sliderInstance.value).toBe(92); }); it('should set the correct value on slide', () => { - dispatchSlideEvent(sliderTrackElement, sliderNativeElement, 0, 0.32, gestureConfig); + dispatchSlideEventSequence(sliderTrackElement, sliderNativeElement, 0, 0.32, gestureConfig); expect(sliderInstance.value).toBe(32); }); }); @@ -415,14 +415,14 @@ describe('MdSlider', () => { it('should set the correct step value on click', () => { expect(sliderInstance.value).toBe(0); - dispatchClickEvent(sliderTrackElement, 0.13); + dispatchClickEvent(sliderTrackElement, sliderNativeElement, 0.13); fixture.detectChanges(); expect(sliderInstance.value).toBe(25); }); it('should snap the thumb and fill to a step on click', () => { - dispatchClickEvent(sliderNativeElement, 0.66); + dispatchClickEvent(sliderTrackElement, sliderNativeElement, 0.66); fixture.detectChanges(); let trackFillDimensions = trackFillElement.getBoundingClientRect(); @@ -435,17 +435,14 @@ describe('MdSlider', () => { }); it('should set the correct step value on slide', () => { - dispatchSlideEvent(sliderTrackElement, sliderNativeElement, 0, 0.07, gestureConfig); + dispatchSlideEventSequence(sliderTrackElement, sliderNativeElement, 0, 0.07, gestureConfig); fixture.detectChanges(); expect(sliderInstance.value).toBe(0); }); it('should snap the thumb and fill to a step on slide', () => { - dispatchSlideEvent(sliderTrackElement, sliderNativeElement, 0, 0.88, gestureConfig); - fixture.detectChanges(); - - dispatchSlideEndEvent(sliderNativeElement, 0.88, gestureConfig); + dispatchSlideEventSequence(sliderTrackElement, sliderNativeElement, 0, 0.88, gestureConfig); fixture.detectChanges(); let trackFillDimensions = trackFillElement.getBoundingClientRect(); @@ -562,7 +559,7 @@ describe('MdSlider', () => { it('should update the thumb label text on click', () => { expect(thumbLabelTextElement.textContent).toBe('0'); - dispatchClickEvent(sliderTrackElement, 0.13); + dispatchClickEvent(sliderTrackElement, sliderNativeElement, 0.13); fixture.detectChanges(); // The thumb label text is set to the slider's value. These should always be the same. @@ -572,7 +569,7 @@ describe('MdSlider', () => { it('should update the thumb label text on slide', () => { expect(thumbLabelTextElement.textContent).toBe('0'); - dispatchSlideEvent(sliderTrackElement, sliderNativeElement, 0, 0.56, gestureConfig); + dispatchSlideEventSequence(sliderTrackElement, sliderNativeElement, 0, 0.56, gestureConfig); fixture.detectChanges(); // The thumb label text is set to the slider's value. These should always be the same. @@ -583,7 +580,7 @@ describe('MdSlider', () => { expect(sliderContainerElement.classList).not.toContain('md-slider-active'); expect(sliderContainerElement.classList).toContain('md-slider-thumb-label-showing'); - dispatchClickEvent(sliderNativeElement, 0.49); + dispatchClickEvent(sliderTrackElement, sliderNativeElement, 0.49); fixture.detectChanges(); // The thumb label appears when the slider is active and the 'md-slider-thumb-label-showing' @@ -595,7 +592,7 @@ describe('MdSlider', () => { it('should show the thumb label on slide', () => { expect(sliderContainerElement.classList).not.toContain('md-slider-active'); - dispatchSlideEvent(sliderTrackElement, sliderNativeElement, 0, 0.91, gestureConfig); + dispatchSlideEventSequence(sliderTrackElement, sliderNativeElement, 0, 0.91, gestureConfig); fixture.detectChanges(); expect(sliderContainerElement.classList).toContain('md-slider-thumb-label-showing'); @@ -635,7 +632,7 @@ describe('MdSlider', () => { it('should update the control on click', () => { expect(testComponent.control.value).toBe(0); - dispatchClickEvent(sliderTrackElement, 0.76); + dispatchClickEvent(sliderTrackElement, sliderNativeElement, 0.76); fixture.detectChanges(); expect(testComponent.control.value).toBe(76); @@ -644,7 +641,7 @@ describe('MdSlider', () => { it('should update the control on slide', () => { expect(testComponent.control.value).toBe(0); - dispatchSlideEvent(sliderTrackElement, sliderNativeElement, 0, 0.19, gestureConfig); + dispatchSlideEventSequence(sliderTrackElement, sliderNativeElement, 0, 0.19, gestureConfig); fixture.detectChanges(); expect(testComponent.control.value).toBe(19); @@ -803,6 +800,54 @@ describe('MdSlider', () => { expect(thumbDimensions.left).toBe(sliderDimensions.right); }); }); + + describe('slider with change handler', () => { + let fixture: ComponentFixture; + let sliderDebugElement: DebugElement; + let sliderNativeElement: HTMLElement; + let sliderTrackElement: HTMLElement; + let testComponent: SliderWithChangeHandler; + + beforeEach(() => { + fixture = TestBed.createComponent(SliderWithChangeHandler); + fixture.detectChanges(); + + testComponent = fixture.debugElement.componentInstance; + spyOn(testComponent, 'onChange'); + + sliderDebugElement = fixture.debugElement.query(By.directive(MdSlider)); + sliderNativeElement = sliderDebugElement.nativeElement; + sliderTrackElement = sliderNativeElement.querySelector('.md-slider-track'); + }); + + it('should emit change on click', () => { + expect(testComponent.onChange).not.toHaveBeenCalled(); + + dispatchClickEvent(sliderTrackElement, sliderNativeElement, 0.2); + fixture.detectChanges(); + + expect(testComponent.onChange).toHaveBeenCalledTimes(1); + }); + + it('should emit change on slide', () => { + expect(testComponent.onChange).not.toHaveBeenCalled(); + + dispatchSlideEventSequence(sliderTrackElement, sliderNativeElement, 0, 0.4, gestureConfig); + fixture.detectChanges(); + + expect(testComponent.onChange).toHaveBeenCalledTimes(1); + }); + + it('should not emit multiple changes for same value', () => { + expect(testComponent.onChange).not.toHaveBeenCalled(); + + dispatchClickEvent(sliderTrackElement, sliderNativeElement, 0.6); + dispatchSlideEventSequence(sliderTrackElement, sliderNativeElement, 0.6, 0.6, gestureConfig); + fixture.detectChanges(); + + expect(testComponent.onChange).toHaveBeenCalledTimes(1); + }); + }); }); // The transition has to be removed in order to test the updated positions without setTimeout. @@ -884,64 +929,81 @@ class SliderWithValueSmallerThanMin { } }) class SliderWithValueGreaterThanMax { } +@Component({ + template: `` +}) +class SliderWithChangeHandler { + onChange() { } +} + /** * Dispatches a click event from an element. * Note: The mouse event truncates the position for the click. - * @param element The element from which the event will be dispatched. + * @param trackElement The track element from which the event location will be calculated. + * @param containerElement The container element from which the event will be dispatched. * @param percentage The percentage of the slider where the click should occur. Used to find the * physical location of the click. */ -function dispatchClickEvent(element: HTMLElement, percentage: number): void { - let dimensions = element.getBoundingClientRect(); +function dispatchClickEvent(trackElement: HTMLElement, containerElement: HTMLElement, + percentage: number): void { + let dimensions = trackElement.getBoundingClientRect(); let y = dimensions.top; let x = dimensions.left + (dimensions.width * percentage); let event = document.createEvent('MouseEvent'); event.initMouseEvent( 'click', true, true, window, 0, x, y, x, y, false, false, false, false, 0, null); - element.dispatchEvent(event); + containerElement.dispatchEvent(event); } /** - * Dispatches a slide event from an element. + * Dispatches a slide event sequence (consisting of slidestart, slide, slideend) from an element. * @param trackElement The track element from which the event location will be calculated. * @param containerElement The container element from which the event will be dispatched. * @param startPercent The percentage of the slider where the slide will begin. * @param endPercent The percentage of the slider where the slide will end. * @param gestureConfig The gesture config for the test to handle emitting the slide events. */ +function dispatchSlideEventSequence(trackElement: HTMLElement, containerElement: HTMLElement, + startPercent: number, endPercent: number, + gestureConfig: TestGestureConfig): void { + dispatchSlideStartEvent(trackElement, containerElement, startPercent, gestureConfig); + dispatchSlideEvent(trackElement, containerElement, startPercent, gestureConfig); + dispatchSlideEvent(trackElement, containerElement, endPercent, gestureConfig); + dispatchSlideEndEvent(trackElement, containerElement, endPercent, gestureConfig); +} + +/** + * Dispatches a slide event from an element. + * @param trackElement The track element from which the event location will be calculated. + * @param containerElement The container element from which the event will be dispatched. + * @param percent The percentage of the slider where the slide will happen. + * @param gestureConfig The gesture config for the test to handle emitting the slide events. + */ function dispatchSlideEvent(trackElement: HTMLElement, containerElement: HTMLElement, - startPercent: number, endPercent: number, - gestureConfig: TestGestureConfig): void { + percent: number, gestureConfig: TestGestureConfig): void { let dimensions = trackElement.getBoundingClientRect(); - let startX = dimensions.left + (dimensions.width * startPercent); - let endX = dimensions.left + (dimensions.width * endPercent); - - gestureConfig.emitEventForElement('slidestart', containerElement, { - // The actual event has a center with an x value that the slide listener is looking for. - center: { x: startX }, - // The event needs a source event with a prevent default so we fake one. - srcEvent: { preventDefault: jasmine.createSpy('preventDefault') } - }); + let x = dimensions.left + (dimensions.width * percent); gestureConfig.emitEventForElement('slide', containerElement, { - center: { x: endX }, + center: { x: x }, srcEvent: { preventDefault: jasmine.createSpy('preventDefault') } }); } /** * Dispatches a slidestart event from an element. - * @param element The element from which the event will be dispatched. - * @param startPercent The percentage of the slider where the slide will begin. + * @param trackElement The track element from which the event location will be calculated. + * @param containerElement The container element from which the event will be dispatched. + * @param percent The percentage of the slider where the slide will begin. * @param gestureConfig The gesture config for the test to handle emitting the slide events. */ -function dispatchSlideStartEvent(element: HTMLElement, startPercent: number, - gestureConfig: TestGestureConfig): void { - let dimensions = element.getBoundingClientRect(); - let x = dimensions.left + (dimensions.width * startPercent); +function dispatchSlideStartEvent(trackElement: HTMLElement, containerElement: HTMLElement, + percent: number, gestureConfig: TestGestureConfig): void { + let dimensions = trackElement.getBoundingClientRect(); + let x = dimensions.left + (dimensions.width * percent); - gestureConfig.emitEventForElement('slidestart', element, { + gestureConfig.emitEventForElement('slidestart', containerElement, { center: { x: x }, srcEvent: { preventDefault: jasmine.createSpy('preventDefault') } }); @@ -949,16 +1011,17 @@ function dispatchSlideStartEvent(element: HTMLElement, startPercent: number, /** * Dispatches a slideend event from an element. - * @param element The element from which the event will be dispatched. - * @param endPercent The percentage of the slider where the slide will end. + * @param trackElement The track element from which the event location will be calculated. + * @param containerElement The container element from which the event will be dispatched. + * @param percent The percentage of the slider where the slide will end. * @param gestureConfig The gesture config for the test to handle emitting the slide events. */ -function dispatchSlideEndEvent(element: HTMLElement, endPercent: number, - gestureConfig: TestGestureConfig): void { - let dimensions = element.getBoundingClientRect(); - let x = dimensions.left + (dimensions.width * endPercent); +function dispatchSlideEndEvent(trackElement: HTMLElement, containerElement: HTMLElement, + percent: number, gestureConfig: TestGestureConfig): void { + let dimensions = trackElement.getBoundingClientRect(); + let x = dimensions.left + (dimensions.width * percent); - gestureConfig.emitEventForElement('slideend', element, { + gestureConfig.emitEventForElement('slideend', containerElement, { center: { x: x }, srcEvent: { preventDefault: jasmine.createSpy('preventDefault') } }); diff --git a/src/lib/slider/slider.ts b/src/lib/slider/slider.ts index e0cc9290878b..c86bc0b5c654 100644 --- a/src/lib/slider/slider.ts +++ b/src/lib/slider/slider.ts @@ -3,8 +3,10 @@ import { ModuleWithProviders, Component, ElementRef, + EventEmitter, HostBinding, Input, + Output, ViewEncapsulation, AfterContentInit, forwardRef, @@ -30,6 +32,12 @@ export const MD_SLIDER_VALUE_ACCESSOR: any = { multi: true }; +/** A simple change event emitted by the MdSlider component. */ +export class MdSliderChange { + source: MdSlider; + value: number; +} + @Component({ moduleId: module.id, selector: 'md-slider', @@ -80,6 +88,9 @@ export class MdSlider implements AfterContentInit, ControlValueAccessor { private _controlValueAccessorChangeFn: (value: any) => void = (value) => {}; + /** The last value for which a change event was emitted. */ + private _lastEmittedValue: number = null; + /** onTouch function registered via registerOnTouch (ControlValueAccessor). */ onTouched: () => any = () => {}; @@ -161,6 +172,8 @@ export class MdSlider implements AfterContentInit, ControlValueAccessor { this.snapThumbToValue(); } + @Output() change = new EventEmitter(); + constructor(elementRef: ElementRef) { this._renderer = new SliderRenderer(elementRef); } @@ -190,6 +203,7 @@ export class MdSlider implements AfterContentInit, ControlValueAccessor { this._renderer.addFocus(); this.updateValueFromPosition(event.clientX); this.snapThumbToValue(); + this._emitValueIfChanged(); } /** TODO: internal */ @@ -220,6 +234,7 @@ export class MdSlider implements AfterContentInit, ControlValueAccessor { onSlideEnd() { this.isSliding = false; this.snapThumbToValue(); + this._emitValueIfChanged(); } /** TODO: internal */ @@ -276,6 +291,17 @@ export class MdSlider implements AfterContentInit, ControlValueAccessor { } } + /** Emits a change event if the current value is different from the last emitted value. */ + private _emitValueIfChanged() { + if (this.value != this._lastEmittedValue) { + let event = new MdSliderChange(); + event.source = this; + event.value = this.value; + this.change.emit(event); + this._lastEmittedValue = this.value; + } + } + /** * Calculates the separation in pixels of tick marks. If there is no tick interval or the interval * is set to something other than a number or 'auto', nothing happens. @@ -366,10 +392,6 @@ export class MdSlider implements AfterContentInit, ControlValueAccessor { */ writeValue(value: any) { this.value = value; - - if (this._sliderDimensions) { - this.snapThumbToValue(); - } } /** From cca5afe43cb658078981ab2effb2a5abbc7fa202 Mon Sep 17 00:00:00 2001 From: Miles Malerba Date: Tue, 1 Nov 2016 12:03:15 -0700 Subject: [PATCH 2/2] Clean up dispatch event methods. --- src/lib/slider/slider.spec.ts | 127 +++++++++++++++++----------------- 1 file changed, 62 insertions(+), 65 deletions(-) diff --git a/src/lib/slider/slider.spec.ts b/src/lib/slider/slider.spec.ts index 188107e1acc0..32dfe87a398c 100644 --- a/src/lib/slider/slider.spec.ts +++ b/src/lib/slider/slider.spec.ts @@ -76,33 +76,33 @@ describe('MdSlider', () => { it('should update the value on a click', () => { expect(sliderInstance.value).toBe(0); - dispatchClickEvent(sliderTrackElement, sliderNativeElement, 0.19); + dispatchClickEvent(sliderNativeElement, 0.19); // The expected value is 19 from: percentage * difference of max and min. expect(sliderInstance.value).toBe(19); }); it('should update the value on a slide', () => { expect(sliderInstance.value).toBe(0); - dispatchSlideEventSequence(sliderTrackElement, sliderNativeElement, 0, 0.89, gestureConfig); + dispatchSlideEventSequence(sliderNativeElement, 0, 0.89, gestureConfig); // The expected value is 89 from: percentage * difference of max and min. expect(sliderInstance.value).toBe(89); }); it('should set the value as min when sliding before the track', () => { expect(sliderInstance.value).toBe(0); - dispatchSlideEventSequence(sliderTrackElement, sliderNativeElement, 0, -1.33, gestureConfig); + dispatchSlideEventSequence(sliderNativeElement, 0, -1.33, gestureConfig); expect(sliderInstance.value).toBe(0); }); it('should set the value as max when sliding past the track', () => { expect(sliderInstance.value).toBe(0); - dispatchSlideEventSequence(sliderTrackElement, sliderNativeElement, 0, 1.75, gestureConfig); + dispatchSlideEventSequence(sliderNativeElement, 0, 1.75, gestureConfig); expect(sliderInstance.value).toBe(100); }); it('should update the track fill on click', () => { expect(trackFillDimensions.width).toBe(0); - dispatchClickEvent(sliderTrackElement, sliderNativeElement, 0.39); + dispatchClickEvent(sliderNativeElement, 0.39); trackFillDimensions = trackFillElement.getBoundingClientRect(); thumbDimensions = thumbElement.getBoundingClientRect(); @@ -118,7 +118,7 @@ describe('MdSlider', () => { expect(thumbDimensions.left).toBe(sliderDimensions.left); // 50% is used here because the click event that is dispatched truncates the position and so // a value had to be used that would not be truncated. - dispatchClickEvent(sliderTrackElement, sliderNativeElement, 0.5); + dispatchClickEvent(sliderNativeElement, 0.5); thumbDimensions = thumbElement.getBoundingClientRect(); // The thumb position should be at 50% of the slider's width + the offset of the slider. @@ -128,7 +128,7 @@ describe('MdSlider', () => { it('should update the track fill on slide', () => { expect(trackFillDimensions.width).toBe(0); - dispatchSlideEventSequence(sliderTrackElement, sliderNativeElement, 0, 0.86, gestureConfig); + dispatchSlideEventSequence(sliderNativeElement, 0, 0.86, gestureConfig); trackFillDimensions = trackFillElement.getBoundingClientRect(); thumbDimensions = thumbElement.getBoundingClientRect(); @@ -144,7 +144,7 @@ describe('MdSlider', () => { expect(thumbDimensions.left).toBe(sliderDimensions.left); // The slide event also truncates the position passed in, so 50% is used here as well to // ensure the ability to calculate the expected position. - dispatchSlideEventSequence(sliderTrackElement, sliderNativeElement, 0, 0.5, gestureConfig); + dispatchSlideEventSequence(sliderNativeElement, 0, 0.5, gestureConfig); thumbDimensions = thumbElement.getBoundingClientRect(); expect(thumbDimensions.left).toBe(sliderDimensions.width * 0.5 + sliderDimensions.left); @@ -154,7 +154,7 @@ describe('MdSlider', () => { let containerElement = sliderNativeElement.querySelector('.md-slider-container'); expect(containerElement.classList).not.toContain('md-slider-active'); - dispatchClickEvent(sliderTrackElement, sliderNativeElement, 0.23); + dispatchClickEvent(sliderNativeElement, 0.23); fixture.detectChanges(); expect(containerElement.classList).toContain('md-slider-active'); @@ -163,7 +163,7 @@ describe('MdSlider', () => { it('should remove the md-slider-active class on blur', () => { let containerElement = sliderNativeElement.querySelector('.md-slider-container'); - dispatchClickEvent(sliderTrackElement, sliderNativeElement, 0.95); + dispatchClickEvent(sliderNativeElement, 0.95); fixture.detectChanges(); expect(containerElement.classList).toContain('md-slider-active'); @@ -179,12 +179,12 @@ describe('MdSlider', () => { let containerElement = sliderNativeElement.querySelector('.md-slider-container'); expect(containerElement.classList).not.toContain('md-slider-sliding'); - dispatchSlideStartEvent(sliderTrackElement, sliderNativeElement, 0, gestureConfig); + dispatchSlideStartEvent(sliderNativeElement, 0, gestureConfig); fixture.detectChanges(); expect(containerElement.classList).toContain('md-slider-sliding'); - dispatchSlideEndEvent(sliderTrackElement, sliderNativeElement, 0.34, gestureConfig); + dispatchSlideEndEvent(sliderNativeElement, 0.34, gestureConfig); fixture.detectChanges(); expect(containerElement.classList).not.toContain('md-slider-sliding'); @@ -214,13 +214,13 @@ describe('MdSlider', () => { it('should not change the value on click when disabled', () => { expect(sliderInstance.value).toBe(0); - dispatchClickEvent(sliderTrackElement, sliderNativeElement, 0.63); + dispatchClickEvent(sliderNativeElement, 0.63); expect(sliderInstance.value).toBe(0); }); it('should not change the value on slide when disabled', () => { expect(sliderInstance.value).toBe(0); - dispatchSlideEventSequence(sliderNativeElement, sliderNativeElement, 0, 0.5, gestureConfig); + dispatchSlideEventSequence(sliderNativeElement, 0, 0.5, gestureConfig); expect(sliderInstance.value).toBe(0); }); @@ -228,7 +228,7 @@ describe('MdSlider', () => { let containerElement = sliderNativeElement.querySelector('.md-slider-container'); expect(containerElement.classList).not.toContain('md-slider-active'); - dispatchClickEvent(sliderTrackElement, sliderNativeElement, 0.43); + dispatchClickEvent(sliderNativeElement, 0.43); fixture.detectChanges(); expect(containerElement.classList).not.toContain('md-slider-active'); @@ -238,7 +238,7 @@ describe('MdSlider', () => { let containerElement = sliderNativeElement.querySelector('.md-slider-container'); expect(containerElement.classList).not.toContain('md-slider-sliding'); - dispatchSlideStartEvent(sliderTrackElement, sliderNativeElement, 0.46, gestureConfig); + dispatchSlideStartEvent(sliderNativeElement, 0.46, gestureConfig); fixture.detectChanges(); expect(containerElement.classList).not.toContain('md-slider-sliding'); @@ -280,7 +280,7 @@ describe('MdSlider', () => { }); it('should set the correct value on click', () => { - dispatchClickEvent(sliderTrackElement, sliderNativeElement, 0.09); + dispatchClickEvent(sliderNativeElement, 0.09); // Computed by multiplying the difference between the min and the max by the percentage from // the click and adding that to the minimum. let value = Math.round(4 + (0.09 * (6 - 4))); @@ -288,7 +288,7 @@ describe('MdSlider', () => { }); it('should set the correct value on slide', () => { - dispatchSlideEventSequence(sliderTrackElement, sliderNativeElement, 0, 0.62, gestureConfig); + dispatchSlideEventSequence(sliderNativeElement, 0, 0.62, gestureConfig); // Computed by multiplying the difference between the min and the max by the percentage from // the click and adding that to the minimum. let value = Math.round(4 + (0.62 * (6 - 4))); @@ -296,7 +296,7 @@ describe('MdSlider', () => { }); it('should snap the thumb and fill to the nearest value on click', () => { - dispatchClickEvent(sliderTrackElement, sliderNativeElement, 0.68); + dispatchClickEvent(sliderNativeElement, 0.68); fixture.detectChanges(); let trackFillDimensions = trackFillElement.getBoundingClientRect(); @@ -309,7 +309,7 @@ describe('MdSlider', () => { }); it('should snap the thumb and fill to the nearest value on slide', () => { - dispatchSlideEventSequence(sliderTrackElement, sliderNativeElement, 0, 0.74, gestureConfig); + dispatchSlideEventSequence(sliderNativeElement, 0, 0.74, gestureConfig); fixture.detectChanges(); let trackFillDimensions = trackFillElement.getBoundingClientRect(); @@ -377,14 +377,14 @@ describe('MdSlider', () => { }); it('should set the correct value on click', () => { - dispatchClickEvent(sliderTrackElement, sliderNativeElement, 0.92); + dispatchClickEvent(sliderNativeElement, 0.92); // On a slider with default max and min the value should be approximately equal to the // percentage clicked. This should be the case regardless of what the original set value was. expect(sliderInstance.value).toBe(92); }); it('should set the correct value on slide', () => { - dispatchSlideEventSequence(sliderTrackElement, sliderNativeElement, 0, 0.32, gestureConfig); + dispatchSlideEventSequence(sliderNativeElement, 0, 0.32, gestureConfig); expect(sliderInstance.value).toBe(32); }); }); @@ -415,14 +415,14 @@ describe('MdSlider', () => { it('should set the correct step value on click', () => { expect(sliderInstance.value).toBe(0); - dispatchClickEvent(sliderTrackElement, sliderNativeElement, 0.13); + dispatchClickEvent(sliderNativeElement, 0.13); fixture.detectChanges(); expect(sliderInstance.value).toBe(25); }); it('should snap the thumb and fill to a step on click', () => { - dispatchClickEvent(sliderTrackElement, sliderNativeElement, 0.66); + dispatchClickEvent(sliderNativeElement, 0.66); fixture.detectChanges(); let trackFillDimensions = trackFillElement.getBoundingClientRect(); @@ -435,14 +435,14 @@ describe('MdSlider', () => { }); it('should set the correct step value on slide', () => { - dispatchSlideEventSequence(sliderTrackElement, sliderNativeElement, 0, 0.07, gestureConfig); + dispatchSlideEventSequence(sliderNativeElement, 0, 0.07, gestureConfig); fixture.detectChanges(); expect(sliderInstance.value).toBe(0); }); it('should snap the thumb and fill to a step on slide', () => { - dispatchSlideEventSequence(sliderTrackElement, sliderNativeElement, 0, 0.88, gestureConfig); + dispatchSlideEventSequence(sliderNativeElement, 0, 0.88, gestureConfig); fixture.detectChanges(); let trackFillDimensions = trackFillElement.getBoundingClientRect(); @@ -559,7 +559,7 @@ describe('MdSlider', () => { it('should update the thumb label text on click', () => { expect(thumbLabelTextElement.textContent).toBe('0'); - dispatchClickEvent(sliderTrackElement, sliderNativeElement, 0.13); + dispatchClickEvent(sliderNativeElement, 0.13); fixture.detectChanges(); // The thumb label text is set to the slider's value. These should always be the same. @@ -569,7 +569,7 @@ describe('MdSlider', () => { it('should update the thumb label text on slide', () => { expect(thumbLabelTextElement.textContent).toBe('0'); - dispatchSlideEventSequence(sliderTrackElement, sliderNativeElement, 0, 0.56, gestureConfig); + dispatchSlideEventSequence(sliderNativeElement, 0, 0.56, gestureConfig); fixture.detectChanges(); // The thumb label text is set to the slider's value. These should always be the same. @@ -580,7 +580,7 @@ describe('MdSlider', () => { expect(sliderContainerElement.classList).not.toContain('md-slider-active'); expect(sliderContainerElement.classList).toContain('md-slider-thumb-label-showing'); - dispatchClickEvent(sliderTrackElement, sliderNativeElement, 0.49); + dispatchClickEvent(sliderNativeElement, 0.49); fixture.detectChanges(); // The thumb label appears when the slider is active and the 'md-slider-thumb-label-showing' @@ -592,7 +592,7 @@ describe('MdSlider', () => { it('should show the thumb label on slide', () => { expect(sliderContainerElement.classList).not.toContain('md-slider-active'); - dispatchSlideEventSequence(sliderTrackElement, sliderNativeElement, 0, 0.91, gestureConfig); + dispatchSlideEventSequence(sliderNativeElement, 0, 0.91, gestureConfig); fixture.detectChanges(); expect(sliderContainerElement.classList).toContain('md-slider-thumb-label-showing'); @@ -632,7 +632,7 @@ describe('MdSlider', () => { it('should update the control on click', () => { expect(testComponent.control.value).toBe(0); - dispatchClickEvent(sliderTrackElement, sliderNativeElement, 0.76); + dispatchClickEvent(sliderNativeElement, 0.76); fixture.detectChanges(); expect(testComponent.control.value).toBe(76); @@ -641,7 +641,7 @@ describe('MdSlider', () => { it('should update the control on slide', () => { expect(testComponent.control.value).toBe(0); - dispatchSlideEventSequence(sliderTrackElement, sliderNativeElement, 0, 0.19, gestureConfig); + dispatchSlideEventSequence(sliderNativeElement, 0, 0.19, gestureConfig); fixture.detectChanges(); expect(testComponent.control.value).toBe(19); @@ -823,7 +823,7 @@ describe('MdSlider', () => { it('should emit change on click', () => { expect(testComponent.onChange).not.toHaveBeenCalled(); - dispatchClickEvent(sliderTrackElement, sliderNativeElement, 0.2); + dispatchClickEvent(sliderNativeElement, 0.2); fixture.detectChanges(); expect(testComponent.onChange).toHaveBeenCalledTimes(1); @@ -832,7 +832,7 @@ describe('MdSlider', () => { it('should emit change on slide', () => { expect(testComponent.onChange).not.toHaveBeenCalled(); - dispatchSlideEventSequence(sliderTrackElement, sliderNativeElement, 0, 0.4, gestureConfig); + dispatchSlideEventSequence(sliderNativeElement, 0, 0.4, gestureConfig); fixture.detectChanges(); expect(testComponent.onChange).toHaveBeenCalledTimes(1); @@ -841,8 +841,8 @@ describe('MdSlider', () => { it('should not emit multiple changes for same value', () => { expect(testComponent.onChange).not.toHaveBeenCalled(); - dispatchClickEvent(sliderTrackElement, sliderNativeElement, 0.6); - dispatchSlideEventSequence(sliderTrackElement, sliderNativeElement, 0.6, 0.6, gestureConfig); + dispatchClickEvent(sliderNativeElement, 0.6); + dispatchSlideEventSequence(sliderNativeElement, 0.6, 0.6, gestureConfig); fixture.detectChanges(); expect(testComponent.onChange).toHaveBeenCalledTimes(1); @@ -939,13 +939,12 @@ class SliderWithChangeHandler { /** * Dispatches a click event from an element. * Note: The mouse event truncates the position for the click. - * @param trackElement The track element from which the event location will be calculated. - * @param containerElement The container element from which the event will be dispatched. + * @param sliderElement The md-slider element from which the event will be dispatched. * @param percentage The percentage of the slider where the click should occur. Used to find the * physical location of the click. */ -function dispatchClickEvent(trackElement: HTMLElement, containerElement: HTMLElement, - percentage: number): void { +function dispatchClickEvent(sliderElement: HTMLElement, percentage: number): void { + let trackElement = sliderElement.querySelector('.md-slider-track'); let dimensions = trackElement.getBoundingClientRect(); let y = dimensions.top; let x = dimensions.left + (dimensions.width * percentage); @@ -953,39 +952,37 @@ function dispatchClickEvent(trackElement: HTMLElement, containerElement: HTMLEle let event = document.createEvent('MouseEvent'); event.initMouseEvent( 'click', true, true, window, 0, x, y, x, y, false, false, false, false, 0, null); - containerElement.dispatchEvent(event); + sliderElement.dispatchEvent(event); } /** * Dispatches a slide event sequence (consisting of slidestart, slide, slideend) from an element. - * @param trackElement The track element from which the event location will be calculated. - * @param containerElement The container element from which the event will be dispatched. + * @param sliderElement The md-slider element from which the event will be dispatched. * @param startPercent The percentage of the slider where the slide will begin. * @param endPercent The percentage of the slider where the slide will end. * @param gestureConfig The gesture config for the test to handle emitting the slide events. */ -function dispatchSlideEventSequence(trackElement: HTMLElement, containerElement: HTMLElement, - startPercent: number, endPercent: number, - gestureConfig: TestGestureConfig): void { - dispatchSlideStartEvent(trackElement, containerElement, startPercent, gestureConfig); - dispatchSlideEvent(trackElement, containerElement, startPercent, gestureConfig); - dispatchSlideEvent(trackElement, containerElement, endPercent, gestureConfig); - dispatchSlideEndEvent(trackElement, containerElement, endPercent, gestureConfig); +function dispatchSlideEventSequence(sliderElement: HTMLElement, startPercent: number, + endPercent: number, gestureConfig: TestGestureConfig): void { + dispatchSlideStartEvent(sliderElement, startPercent, gestureConfig); + dispatchSlideEvent(sliderElement, startPercent, gestureConfig); + dispatchSlideEvent(sliderElement, endPercent, gestureConfig); + dispatchSlideEndEvent(sliderElement, endPercent, gestureConfig); } /** * Dispatches a slide event from an element. - * @param trackElement The track element from which the event location will be calculated. - * @param containerElement The container element from which the event will be dispatched. + * @param sliderElement The md-slider element from which the event will be dispatched. * @param percent The percentage of the slider where the slide will happen. * @param gestureConfig The gesture config for the test to handle emitting the slide events. */ -function dispatchSlideEvent(trackElement: HTMLElement, containerElement: HTMLElement, - percent: number, gestureConfig: TestGestureConfig): void { +function dispatchSlideEvent(sliderElement: HTMLElement, percent: number, + gestureConfig: TestGestureConfig): void { + let trackElement = sliderElement.querySelector('.md-slider-track'); let dimensions = trackElement.getBoundingClientRect(); let x = dimensions.left + (dimensions.width * percent); - gestureConfig.emitEventForElement('slide', containerElement, { + gestureConfig.emitEventForElement('slide', sliderElement, { center: { x: x }, srcEvent: { preventDefault: jasmine.createSpy('preventDefault') } }); @@ -993,17 +990,17 @@ function dispatchSlideEvent(trackElement: HTMLElement, containerElement: HTMLEle /** * Dispatches a slidestart event from an element. - * @param trackElement The track element from which the event location will be calculated. - * @param containerElement The container element from which the event will be dispatched. + * @param sliderElement The md-slider element from which the event will be dispatched. * @param percent The percentage of the slider where the slide will begin. * @param gestureConfig The gesture config for the test to handle emitting the slide events. */ -function dispatchSlideStartEvent(trackElement: HTMLElement, containerElement: HTMLElement, - percent: number, gestureConfig: TestGestureConfig): void { +function dispatchSlideStartEvent(sliderElement: HTMLElement, percent: number, + gestureConfig: TestGestureConfig): void { + let trackElement = sliderElement.querySelector('.md-slider-track'); let dimensions = trackElement.getBoundingClientRect(); let x = dimensions.left + (dimensions.width * percent); - gestureConfig.emitEventForElement('slidestart', containerElement, { + gestureConfig.emitEventForElement('slidestart', sliderElement, { center: { x: x }, srcEvent: { preventDefault: jasmine.createSpy('preventDefault') } }); @@ -1011,17 +1008,17 @@ function dispatchSlideStartEvent(trackElement: HTMLElement, containerElement: HT /** * Dispatches a slideend event from an element. - * @param trackElement The track element from which the event location will be calculated. - * @param containerElement The container element from which the event will be dispatched. + * @param sliderElement The md-slider element from which the event will be dispatched. * @param percent The percentage of the slider where the slide will end. * @param gestureConfig The gesture config for the test to handle emitting the slide events. */ -function dispatchSlideEndEvent(trackElement: HTMLElement, containerElement: HTMLElement, - percent: number, gestureConfig: TestGestureConfig): void { +function dispatchSlideEndEvent(sliderElement: HTMLElement, percent: number, + gestureConfig: TestGestureConfig): void { + let trackElement = sliderElement.querySelector('.md-slider-track'); let dimensions = trackElement.getBoundingClientRect(); let x = dimensions.left + (dimensions.width * percent); - gestureConfig.emitEventForElement('slideend', containerElement, { + gestureConfig.emitEventForElement('slideend', sliderElement, { center: { x: x }, srcEvent: { preventDefault: jasmine.createSpy('preventDefault') } });