diff --git a/src/material/slider/slider.spec.ts b/src/material/slider/slider.spec.ts index a9acb3be6232..86c3e5ee299b 100644 --- a/src/material/slider/slider.spec.ts +++ b/src/material/slider/slider.spec.ts @@ -962,6 +962,7 @@ describe('MatSlider', () => { let sliderNativeElement: HTMLElement; let testComponent: SliderWithChangeHandler; let sliderInstance: MatSlider; + let trackFillElement: HTMLElement; beforeEach(() => { fixture = createComponent(SliderWithChangeHandler); @@ -974,6 +975,7 @@ describe('MatSlider', () => { sliderDebugElement = fixture.debugElement.query(By.directive(MatSlider))!; sliderNativeElement = sliderDebugElement.nativeElement; sliderInstance = sliderDebugElement.injector.get(MatSlider); + trackFillElement = sliderNativeElement.querySelector('.mat-slider-track-fill') as HTMLElement; }); it('should increment slider by 1 on up arrow pressed', () => { @@ -1028,6 +1030,21 @@ describe('MatSlider', () => { expect(sliderInstance.value).toBe(99); }); + it('should decrement from max when interacting after out-of-bounds value is assigned', () => { + sliderInstance.max = 100; + sliderInstance.value = 200; + fixture.detectChanges(); + + expect(sliderInstance.value).toBe(200); + expect(trackFillElement.style.transform).toContain('scale3d(1, 1, 1)'); + + dispatchKeyboardEvent(sliderNativeElement, 'keydown', LEFT_ARROW); + fixture.detectChanges(); + + expect(sliderInstance.value).toBe(99); + expect(trackFillElement.style.transform).toContain('scale3d(0.99, 1, 1)'); + }); + it('should increment slider by 10 on page up pressed', () => { expect(testComponent.onChange).not.toHaveBeenCalled(); diff --git a/src/material/slider/slider.ts b/src/material/slider/slider.ts index 4fb7650df091..f808ffc88b35 100644 --- a/src/material/slider/slider.ts +++ b/src/material/slider/slider.ts @@ -793,7 +793,10 @@ export class MatSlider /** Increments the slider by the given number of steps (negative number decrements). */ private _increment(numSteps: number) { - this.value = this._clamp((this.value || 0) + this.step * numSteps, this.min, this.max); + // Pre-clamp the current value since it's allowed to be + // out of bounds when assigned programmatically. + const clampedValue = this._clamp(this.value || 0, this.min, this.max); + this.value = this._clamp(clampedValue + this.step * numSteps, this.min, this.max); } /** Calculate the new value from the new physical location. The value will always be snapped. */