1885 lines
66 KiB
TypeScript
1885 lines
66 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright Google LLC All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
* found in the LICENSE file at https://angular.dev/license
|
|
*/
|
|
|
|
import {BidiModule, Directionality} from '@angular/cdk/bidi';
|
|
import {Platform} from '@angular/cdk/platform';
|
|
import {dispatchEvent, dispatchFakeEvent, dispatchPointerEvent} from '@angular/cdk/testing/private';
|
|
import {Component, Provider, QueryList, Type, ViewChild, ViewChildren} from '@angular/core';
|
|
import {
|
|
ComponentFixture,
|
|
TestBed,
|
|
fakeAsync,
|
|
flush,
|
|
tick,
|
|
waitForAsync,
|
|
} from '@angular/core/testing';
|
|
import {FormControl, FormsModule, ReactiveFormsModule} from '@angular/forms';
|
|
import {By} from '@angular/platform-browser';
|
|
import {of} from 'rxjs';
|
|
import {MatSliderModule} from './module';
|
|
import {MatSlider} from './slider';
|
|
import {MatSliderRangeThumb, MatSliderThumb} from './slider-input';
|
|
import {_MatThumb} from './slider-interface';
|
|
import {MatSliderVisualThumb} from './slider-thumb';
|
|
|
|
interface Point {
|
|
x: number;
|
|
y: number;
|
|
}
|
|
|
|
describe('MatSlider', () => {
|
|
let platform: Platform;
|
|
|
|
function createComponent<T>(component: Type<T>, providers: Provider[] = []): ComponentFixture<T> {
|
|
TestBed.configureTestingModule({
|
|
imports: [FormsModule, MatSliderModule, ReactiveFormsModule, BidiModule],
|
|
providers: [...providers],
|
|
declarations: [component],
|
|
});
|
|
platform = TestBed.inject(Platform);
|
|
return TestBed.createComponent<T>(component);
|
|
}
|
|
|
|
function checkInput(
|
|
input: MatSliderThumb,
|
|
{
|
|
min,
|
|
max,
|
|
value,
|
|
translateX,
|
|
width,
|
|
step,
|
|
}: {min: number; max: number; value: number; translateX: number; width?: number; step?: number},
|
|
): void {
|
|
expect(input.min).withContext('min').toBe(min);
|
|
expect(input.max).withContext('max').toBe(max);
|
|
expect(input.value).withContext('value').toBe(value);
|
|
|
|
// The discrepancy between the "ideal" and "actual" translateX comes from
|
|
// the 3px offset from the start & end of the slider track to the first
|
|
// and last tick marks.
|
|
//
|
|
// The "actual" translateX is calculated based on a slider that is 6px
|
|
// smaller than the width of the slider. Using this "actual" translateX in
|
|
// tests would make it even more difficult than it already is to tell if
|
|
// the translateX is off, so we abstract things in here so tests can be
|
|
// more intuitive.
|
|
//
|
|
// The most clear way to compare the two tx's is to just turn them into
|
|
// percentages by dividing by their (total height) / 100.
|
|
const idealTXPercentage = Math.round(translateX / 3);
|
|
const actualTXPercentage = Math.round((input.translateX - 3) / 2.94);
|
|
expect(actualTXPercentage)
|
|
.withContext(`translateX: ${input.translateX} should be close to ${translateX}`)
|
|
.toBe(idealTXPercentage);
|
|
if (step !== undefined) {
|
|
expect(input.step).withContext('step').toBe(step);
|
|
}
|
|
if (width !== undefined) {
|
|
const realWidth = parseInt(
|
|
(input as MatSliderRangeThumb)._hostElement.style.width.match(/\d+/)![0],
|
|
10,
|
|
);
|
|
expect(realWidth)
|
|
.withContext('width')
|
|
.toBeCloseTo((300 * width) / 100 + 16, 0);
|
|
}
|
|
}
|
|
|
|
// Note that this test is outside of the other `describe` blocks, because they all run
|
|
// `detectChanges` in the `beforeEach` and we're testing specifically what happens if it
|
|
// is destroyed before change detection has run.
|
|
it('should not throw if a slider is destroyed before the first change detection run', () => {
|
|
expect(() => {
|
|
const fixture = createComponent(StandardSlider);
|
|
fixture.destroy();
|
|
}).not.toThrow();
|
|
});
|
|
|
|
describe('standard slider', () => {
|
|
let fixture: ComponentFixture<StandardSlider>;
|
|
let slider: MatSlider;
|
|
let input: MatSliderThumb;
|
|
|
|
beforeEach(waitForAsync(() => {
|
|
fixture = createComponent(StandardSlider);
|
|
fixture.detectChanges();
|
|
const sliderDebugElement = fixture.debugElement.query(By.directive(MatSlider));
|
|
slider = sliderDebugElement.componentInstance;
|
|
input = slider._getInput(_MatThumb.END) as MatSliderThumb;
|
|
}));
|
|
|
|
it('should set the default values', () => {
|
|
expect(slider.min).toBe(0);
|
|
expect(slider.max).toBe(100);
|
|
expect(slider.step).toBe(1);
|
|
checkInput(input, {min: 0, max: 100, value: 0, step: 1, translateX: 0});
|
|
});
|
|
|
|
it('should update by click', fakeAsync(() => {
|
|
setValueByClick(slider, input, 25);
|
|
checkInput(input, {min: 0, max: 100, value: 25, step: 1, translateX: 75});
|
|
|
|
setValueByClick(slider, input, 50);
|
|
checkInput(input, {min: 0, max: 100, value: 50, step: 1, translateX: 150});
|
|
|
|
setValueByClick(slider, input, 75);
|
|
checkInput(input, {min: 0, max: 100, value: 75, step: 1, translateX: 225});
|
|
|
|
setValueByClick(slider, input, 100);
|
|
checkInput(input, {min: 0, max: 100, value: 100, step: 1, translateX: 300});
|
|
}));
|
|
|
|
it('should update by slide', fakeAsync(() => {
|
|
slideToValue(slider, input, 25);
|
|
checkInput(input, {min: 0, max: 100, value: 25, step: 1, translateX: 75});
|
|
|
|
slideToValue(slider, input, 50);
|
|
checkInput(input, {min: 0, max: 100, value: 50, step: 1, translateX: 150});
|
|
|
|
slideToValue(slider, input, 75);
|
|
checkInput(input, {min: 0, max: 100, value: 75, step: 1, translateX: 225});
|
|
|
|
slideToValue(slider, input, 100);
|
|
checkInput(input, {min: 0, max: 100, value: 100, step: 1, translateX: 300});
|
|
}));
|
|
|
|
it('should not slide before the track', fakeAsync(() => {
|
|
slideToValue(slider, input, -10);
|
|
expect(input.value).toBe(0);
|
|
checkInput(input, {min: 0, max: 100, value: 0, step: 1, translateX: 0});
|
|
}));
|
|
|
|
it('should not slide past the track', fakeAsync(() => {
|
|
slideToValue(slider, input, 110);
|
|
expect(input.value).toBe(100);
|
|
checkInput(input, {min: 0, max: 100, value: 100, step: 1, translateX: 300});
|
|
}));
|
|
|
|
// TODO(wagnermaciel): Fix this test case (behavior works as intended in browser).
|
|
// it('should not break when the page layout changes', fakeAsync(async () => {
|
|
// slider._elementRef.nativeElement.style.marginLeft = '100px';
|
|
// tick(200);
|
|
// fixture.detectChanges();
|
|
// setValueByClick(slider, input, 25);
|
|
// checkInput(input, {min: 0, max: 100, value: 25, step: 1, translateX: 75});
|
|
// slider._elementRef.nativeElement.style.marginLeft = 'initial';
|
|
// }));
|
|
});
|
|
|
|
describe('standard range slider', () => {
|
|
let fixture: ComponentFixture<StandardRangeSlider>;
|
|
let slider: MatSlider;
|
|
let endInput: MatSliderRangeThumb;
|
|
let startInput: MatSliderRangeThumb;
|
|
|
|
beforeEach(waitForAsync(() => {
|
|
fixture = createComponent(StandardRangeSlider);
|
|
fixture.detectChanges();
|
|
const sliderDebugElement = fixture.debugElement.query(By.directive(MatSlider));
|
|
slider = sliderDebugElement.componentInstance;
|
|
endInput = slider._getInput(_MatThumb.END) as MatSliderRangeThumb;
|
|
startInput = slider._getInput(_MatThumb.START) as MatSliderRangeThumb;
|
|
}));
|
|
|
|
it('should set the default values', () => {
|
|
checkInput(startInput, {min: 0, max: 100, value: 0, step: 1, translateX: 0});
|
|
checkInput(endInput, {min: 0, max: 100, value: 100, step: 1, translateX: 300});
|
|
|
|
expect(slider.min).toBe(0);
|
|
expect(slider.max).toBe(100);
|
|
expect(slider.step).toBe(1);
|
|
});
|
|
|
|
it('should update by start input click', fakeAsync(() => {
|
|
setValueByClick(slider, startInput, 25);
|
|
checkInput(startInput, {min: 0, max: 100, value: 25, translateX: 75});
|
|
checkInput(endInput, {min: 25, max: 100, value: 100, translateX: 300});
|
|
}));
|
|
|
|
it('should update by end input click', fakeAsync(() => {
|
|
setValueByClick(slider, endInput, 75);
|
|
checkInput(startInput, {min: 0, max: 75, value: 0, translateX: 0});
|
|
checkInput(endInput, {min: 0, max: 100, value: 75, translateX: 225});
|
|
}));
|
|
|
|
it('should update by start thumb slide', fakeAsync(() => {
|
|
slideToValue(slider, startInput, 75);
|
|
checkInput(startInput, {min: 0, max: 100, value: 75, translateX: 225});
|
|
checkInput(endInput, {min: 75, max: 100, value: 100, translateX: 300});
|
|
}));
|
|
|
|
it('should update by end thumb slide', fakeAsync(() => {
|
|
slideToValue(slider, endInput, 25);
|
|
checkInput(startInput, {min: 0, max: 25, value: 0, translateX: 0});
|
|
checkInput(endInput, {min: 0, max: 100, value: 25, translateX: 75});
|
|
}));
|
|
|
|
it('should not allow start thumb to slide before the track', fakeAsync(() => {
|
|
slideToValue(slider, startInput, -10);
|
|
checkInput(startInput, {min: 0, max: 100, value: 0, translateX: 0});
|
|
checkInput(endInput, {min: 0, max: 100, value: 100, translateX: 300});
|
|
}));
|
|
|
|
it('should not allow end thumb to slide past the track', fakeAsync(() => {
|
|
slideToValue(slider, endInput, 110);
|
|
checkInput(startInput, {min: 0, max: 100, value: 0, translateX: 0});
|
|
checkInput(endInput, {min: 0, max: 100, value: 100, translateX: 300});
|
|
}));
|
|
|
|
it('should not allow start thumb to slide past the end thumb', fakeAsync(() => {
|
|
slideToValue(slider, endInput, 50);
|
|
slideToValue(slider, startInput, 55);
|
|
checkInput(startInput, {min: 0, max: 50, value: 50, translateX: 150});
|
|
checkInput(endInput, {min: 50, max: 100, value: 50, translateX: 150});
|
|
}));
|
|
|
|
it('should not allow end thumb to slide past the start thumb', fakeAsync(() => {
|
|
slideToValue(slider, startInput, 50);
|
|
slideToValue(slider, endInput, 45);
|
|
checkInput(startInput, {min: 0, max: 50, value: 50, translateX: 150});
|
|
checkInput(endInput, {min: 50, max: 100, value: 50, translateX: 150});
|
|
}));
|
|
|
|
// TODO(wagnermaciel): Fix this test case (behavior works as intended in browser).
|
|
// it('should not break when the page layout changes', fakeAsync(() => {
|
|
// slider._elementRef.nativeElement.style.marginLeft = '100px';
|
|
// setValueByClick(slider, startInput, 25);
|
|
// checkInput(startInput, {min: 0, max: 100, value: 25, translateX: 75});
|
|
// checkInput(endInput, {min: 25, max: 100, value: 100, translateX: 300});
|
|
|
|
// setValueByClick(slider, endInput, 75);
|
|
// checkInput(startInput, {min: 0, max: 75, value: 25, translateX: 75});
|
|
// checkInput(endInput, {min: 25, max: 100, value: 75, translateX: 225});
|
|
// slider._elementRef.nativeElement.style.marginLeft = 'initial';
|
|
// }));
|
|
});
|
|
|
|
describe('slider with min/max bindings', () => {
|
|
let fixture: ComponentFixture<SliderWithMinAndMax>;
|
|
let slider: MatSlider;
|
|
let input: MatSliderThumb;
|
|
|
|
beforeEach(waitForAsync(() => {
|
|
fixture = createComponent(SliderWithMinAndMax);
|
|
fixture.detectChanges();
|
|
const sliderDebugElement = fixture.debugElement.query(By.directive(MatSlider));
|
|
slider = sliderDebugElement.componentInstance;
|
|
input = slider._getInput(_MatThumb.END) as MatSliderThumb;
|
|
}));
|
|
|
|
it('should have the correct initial values', () => {
|
|
checkInput(input, {min: 25, max: 75, value: 25, translateX: 0});
|
|
});
|
|
|
|
it('should update the min when the bound value changes', () => {
|
|
fixture.componentInstance.min = 0;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
checkInput(input, {min: 0, max: 75, value: 25, translateX: 100});
|
|
});
|
|
|
|
it('should update the max when the bound value changes', () => {
|
|
fixture.componentInstance.max = 90;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
checkInput(input, {min: 25, max: 90, value: 25, translateX: 0});
|
|
});
|
|
|
|
it('should update the value if the min increases past it', () => {
|
|
fixture.componentInstance.min = 50;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
checkInput(input, {min: 50, max: 75, value: 50, translateX: 0});
|
|
});
|
|
|
|
it('should update the value if the max decreases below it', () => {
|
|
input.value = 75;
|
|
fixture.componentInstance.max = 50;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
checkInput(input, {min: 25, max: 50, value: 50, translateX: 300});
|
|
});
|
|
|
|
it('should allow the min increase above the max', () => {
|
|
fixture.componentInstance.min = 80;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
checkInput(input, {min: 80, max: 75, value: 80, translateX: 0});
|
|
});
|
|
|
|
it('should allow the max to decrease below the min', () => {
|
|
fixture.componentInstance.max = -10;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
checkInput(input, {min: 25, max: -10, value: 25, translateX: 0});
|
|
});
|
|
|
|
it('should update the thumb translateX when the min changes', () => {
|
|
checkInput(input, {min: 25, max: 75, value: 25, translateX: 0});
|
|
fixture.componentInstance.min = -25;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
checkInput(input, {min: -25, max: 75, value: 25, translateX: 150});
|
|
});
|
|
|
|
it('should update the thumb translateX when the max changes', fakeAsync(() => {
|
|
setValueByClick(slider, input, 50);
|
|
checkInput(input, {min: 25, max: 75, value: 50, translateX: 150});
|
|
fixture.componentInstance.max = 125;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
checkInput(input, {min: 25, max: 125, value: 50, translateX: 75});
|
|
}));
|
|
});
|
|
|
|
describe('range slider with min/max bindings', () => {
|
|
let fixture: ComponentFixture<RangeSliderWithMinAndMax>;
|
|
let slider: MatSlider;
|
|
let endInput: MatSliderRangeThumb;
|
|
let startInput: MatSliderRangeThumb;
|
|
|
|
beforeEach(waitForAsync(() => {
|
|
fixture = createComponent(RangeSliderWithMinAndMax);
|
|
fixture.detectChanges();
|
|
const sliderDebugElement = fixture.debugElement.query(By.directive(MatSlider));
|
|
slider = sliderDebugElement.componentInstance;
|
|
endInput = slider._getInput(_MatThumb.END) as MatSliderRangeThumb;
|
|
startInput = slider._getInput(_MatThumb.START) as MatSliderRangeThumb;
|
|
}));
|
|
|
|
it('should have the correct initial values', () => {
|
|
checkInput(startInput, {min: 25, max: 75, value: 25, translateX: 0});
|
|
checkInput(endInput, {min: 25, max: 75, value: 75, translateX: 300});
|
|
});
|
|
|
|
describe('should handle min changes', () => {
|
|
it('that do not affect values', () => {
|
|
checkInput(startInput, {min: 25, max: 75, value: 25, translateX: 0});
|
|
checkInput(endInput, {min: 25, max: 75, value: 75, translateX: 300});
|
|
|
|
fixture.componentInstance.min = -25;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
|
|
checkInput(startInput, {min: -25, max: 75, value: 25, translateX: 150});
|
|
checkInput(endInput, {min: 25, max: 75, value: 75, translateX: 300});
|
|
});
|
|
|
|
it('that affect the start value', () => {
|
|
fixture.componentInstance.min = 50;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
checkInput(startInput, {min: 50, max: 75, value: 50, translateX: 0});
|
|
checkInput(endInput, {min: 50, max: 75, value: 75, translateX: 300});
|
|
});
|
|
|
|
it('that affect both values', () => {
|
|
endInput.value = 50;
|
|
fixture.componentInstance.min = 60;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
checkInput(startInput, {min: 60, max: 60, value: 60, translateX: 0});
|
|
checkInput(endInput, {min: 60, max: 75, value: 60, translateX: 0});
|
|
});
|
|
|
|
it('where the new start tx is greater than the old end tx', fakeAsync(() => {
|
|
fixture.componentInstance.min = 0;
|
|
fixture.componentInstance.max = 100;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
|
|
slideToValue(slider, startInput, 10);
|
|
slideToValue(slider, endInput, 20);
|
|
|
|
checkInput(startInput, {min: 0, max: 20, value: 10, translateX: 30});
|
|
checkInput(endInput, {min: 10, max: 100, value: 20, translateX: 60});
|
|
|
|
fixture.componentInstance.min = -1000;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
|
|
checkInput(startInput, {min: -1000, max: 20, value: 10, translateX: 275.5});
|
|
checkInput(endInput, {min: 10, max: 100, value: 20, translateX: 278});
|
|
}));
|
|
|
|
it('where the new end tx is less than the old start tx', fakeAsync(() => {
|
|
fixture.componentInstance.min = 0;
|
|
fixture.componentInstance.max = 100;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
|
|
slideToValue(slider, endInput, 92);
|
|
slideToValue(slider, startInput, 91);
|
|
|
|
checkInput(startInput, {min: 0, max: 92, value: 91, translateX: 273});
|
|
checkInput(endInput, {min: 91, max: 100, value: 92, translateX: 276});
|
|
|
|
fixture.componentInstance.min = 90;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
|
|
checkInput(startInput, {min: 90, max: 92, value: 91, translateX: 30});
|
|
checkInput(endInput, {min: 91, max: 100, value: 92, translateX: 60});
|
|
}));
|
|
|
|
it('that make min and max equal', () => {
|
|
fixture.componentInstance.min = 75;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
|
|
checkInput(startInput, {min: 75, max: 75, value: 75, translateX: 0});
|
|
checkInput(endInput, {min: 75, max: 75, value: 75, translateX: 0});
|
|
});
|
|
|
|
it('that increase above the max', () => {
|
|
fixture.componentInstance.min = 80;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
|
|
checkInput(startInput, {min: 80, max: 75, value: 80, translateX: 0});
|
|
checkInput(endInput, {min: 80, max: 75, value: 80, translateX: 0});
|
|
});
|
|
});
|
|
|
|
describe('should handle max changes', () => {
|
|
it('that do not affect values', () => {
|
|
checkInput(startInput, {min: 25, max: 75, value: 25, translateX: 0});
|
|
checkInput(endInput, {min: 25, max: 75, value: 75, translateX: 300});
|
|
|
|
fixture.componentInstance.max = 125;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
|
|
checkInput(startInput, {min: 25, max: 75, value: 25, translateX: 0});
|
|
checkInput(endInput, {min: 25, max: 125, value: 75, translateX: 150});
|
|
});
|
|
|
|
it('that affect the end value', () => {
|
|
fixture.componentInstance.max = 50;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
checkInput(endInput, {min: 25, max: 50, value: 50, translateX: 300});
|
|
checkInput(startInput, {min: 25, max: 50, value: 25, translateX: 0});
|
|
});
|
|
|
|
it('that affect both values', () => {
|
|
startInput.value = 60;
|
|
fixture.componentInstance.max = 50;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
checkInput(endInput, {min: 50, max: 50, value: 50, translateX: 300});
|
|
checkInput(startInput, {min: 25, max: 50, value: 50, translateX: 300});
|
|
});
|
|
|
|
it('where the new start tx is greater than the old end tx', fakeAsync(() => {
|
|
fixture.componentInstance.min = 0;
|
|
fixture.componentInstance.max = 100;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
|
|
slideToValue(slider, startInput, 1);
|
|
slideToValue(slider, endInput, 2);
|
|
|
|
checkInput(startInput, {min: 0, max: 2, value: 1, translateX: 3});
|
|
checkInput(endInput, {min: 1, max: 100, value: 2, translateX: 6});
|
|
|
|
fixture.componentInstance.max = 10;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
|
|
checkInput(startInput, {min: 0, max: 2, value: 1, translateX: 30});
|
|
checkInput(endInput, {min: 1, max: 10, value: 2, translateX: 60});
|
|
}));
|
|
|
|
it('where the new end tx is less than the old start tx', fakeAsync(() => {
|
|
fixture.componentInstance.min = 0;
|
|
fixture.componentInstance.max = 100;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
|
|
slideToValue(slider, endInput, 95);
|
|
slideToValue(slider, startInput, 90);
|
|
|
|
checkInput(startInput, {min: 0, max: 95, value: 90, translateX: 270});
|
|
checkInput(endInput, {min: 90, max: 100, value: 95, translateX: 285});
|
|
|
|
fixture.componentInstance.max = 1000;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
|
|
checkInput(startInput, {min: 0, max: 95, value: 90, translateX: 27});
|
|
checkInput(endInput, {min: 90, max: 1000, value: 95, translateX: 28.5});
|
|
}));
|
|
|
|
it('that make min and max equal', () => {
|
|
fixture.componentInstance.max = 25;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
|
|
checkInput(startInput, {min: 25, max: 25, value: 25, translateX: 0});
|
|
checkInput(endInput, {min: 25, max: 25, value: 25, translateX: 0});
|
|
});
|
|
|
|
it('that decrease below the min', () => {
|
|
fixture.componentInstance.max = 0;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
|
|
// For some reason there was a bug with Safari 15.3.
|
|
// Manually testing on version 16.0 shows that this issue no longer exists.
|
|
if (!platform.SAFARI) {
|
|
checkInput(startInput, {min: 25, max: 0, value: 25, translateX: 0});
|
|
checkInput(endInput, {min: 25, max: 0, value: 25, translateX: 0});
|
|
}
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('disabled slider', () => {
|
|
let slider: MatSlider;
|
|
let input: MatSliderThumb;
|
|
|
|
beforeEach(waitForAsync(() => {
|
|
const fixture = createComponent(DisabledSlider);
|
|
fixture.detectChanges();
|
|
const sliderDebugElement = fixture.debugElement.query(By.directive(MatSlider));
|
|
slider = sliderDebugElement.componentInstance;
|
|
input = slider._getInput(_MatThumb.END) as MatSliderThumb;
|
|
}));
|
|
|
|
it('should be disabled', () => {
|
|
expect(slider.disabled).toBeTrue();
|
|
});
|
|
|
|
it('should have the disabled class on the root element', () => {
|
|
expect(slider._elementRef.nativeElement.classList).toContain('mdc-slider--disabled');
|
|
});
|
|
|
|
it('should set the disabled attribute on the input element', () => {
|
|
expect(input._hostElement.disabled).toBeTrue();
|
|
});
|
|
});
|
|
|
|
describe('disabled range slider', () => {
|
|
let slider: MatSlider;
|
|
let startInput: MatSliderThumb;
|
|
let endInput: MatSliderThumb;
|
|
|
|
beforeEach(waitForAsync(() => {
|
|
const fixture = createComponent(DisabledRangeSlider);
|
|
fixture.detectChanges();
|
|
const sliderDebugElement = fixture.debugElement.query(By.directive(MatSlider));
|
|
slider = sliderDebugElement.componentInstance;
|
|
startInput = slider._getInput(_MatThumb.START) as MatSliderRangeThumb;
|
|
endInput = slider._getInput(_MatThumb.END) as MatSliderRangeThumb;
|
|
}));
|
|
|
|
it('should be disabled', () => {
|
|
expect(slider.disabled).toBeTrue();
|
|
});
|
|
|
|
it('should have the disabled class on the root element', () => {
|
|
expect(slider._elementRef.nativeElement.classList).toContain('mdc-slider--disabled');
|
|
});
|
|
|
|
it('should set the disabled attribute on the input elements', () => {
|
|
expect(startInput._hostElement.disabled).toBeTrue();
|
|
expect(endInput._hostElement.disabled).toBeTrue();
|
|
});
|
|
});
|
|
|
|
describe('ripple states', () => {
|
|
let input: MatSliderThumb;
|
|
let thumbInstance: MatSliderVisualThumb;
|
|
let thumbElement: HTMLElement;
|
|
let thumbX: number;
|
|
let thumbY: number;
|
|
|
|
beforeEach(waitForAsync(() => {
|
|
const fixture = createComponent(StandardSlider);
|
|
fixture.detectChanges();
|
|
const sliderDebugElement = fixture.debugElement.query(By.directive(MatSlider));
|
|
const slider = sliderDebugElement.componentInstance;
|
|
input = slider._getInput(_MatThumb.END) as MatSliderThumb;
|
|
thumbInstance = slider._getThumb(_MatThumb.END);
|
|
thumbElement = thumbInstance._hostElement;
|
|
const thumbDimensions = thumbElement.getBoundingClientRect();
|
|
thumbX = thumbDimensions.left + thumbDimensions.width / 2;
|
|
thumbY = thumbDimensions.top + thumbDimensions.height / 2;
|
|
}));
|
|
|
|
function isRippleVisible(selector: string) {
|
|
flushRippleTransitions();
|
|
return thumbElement.querySelector(`.mat-mdc-slider-${selector}-ripple`) !== null;
|
|
}
|
|
|
|
function flushRippleTransitions() {
|
|
thumbElement.querySelectorAll('.mat-ripple-element').forEach(el => {
|
|
dispatchFakeEvent(el, 'transitionend');
|
|
});
|
|
}
|
|
|
|
function blur() {
|
|
input._hostElement.blur();
|
|
}
|
|
|
|
function pointerenter() {
|
|
dispatchPointerEvent(input._hostElement, 'pointermove', thumbX, thumbY);
|
|
}
|
|
|
|
function pointerleave() {
|
|
dispatchPointerEvent(input._hostElement, 'pointermove', thumbX + 1000, thumbY);
|
|
}
|
|
|
|
function pointerdown() {
|
|
dispatchPointerEvent(input._hostElement, 'pointerdown', thumbX, thumbY);
|
|
input.focus();
|
|
}
|
|
|
|
function pointerup() {
|
|
dispatchPointerEvent(input._hostElement, 'pointerup', thumbX, thumbY);
|
|
}
|
|
|
|
it('should show the hover ripple on pointerenter', fakeAsync(() => {
|
|
// Doesn't make sense to test for pointerenter events on touch devices.
|
|
expect(isRippleVisible('hover')).toBeFalse();
|
|
pointerenter();
|
|
expect(isRippleVisible('hover')).toBeTrue();
|
|
}));
|
|
|
|
it('should hide the hover ripple on pointerleave', fakeAsync(() => {
|
|
// Doesn't make sense to test for pointerleave events on touch devices.
|
|
pointerenter();
|
|
pointerleave();
|
|
expect(isRippleVisible('hover')).toBeFalse();
|
|
}));
|
|
|
|
it('should show the focus ripple on pointerdown', fakeAsync(() => {
|
|
expect(isRippleVisible('focus')).toBeFalse();
|
|
pointerdown();
|
|
flush();
|
|
expect(isRippleVisible('focus')).toBeTrue();
|
|
}));
|
|
|
|
it('should continue to show the focus ripple on pointerup', fakeAsync(() => {
|
|
pointerdown();
|
|
pointerup();
|
|
flush();
|
|
|
|
// The slider immediately loses focus on pointerup for Safari.
|
|
if (platform.SAFARI) {
|
|
expect(isRippleVisible('hover')).toBeTrue();
|
|
} else {
|
|
expect(isRippleVisible('focus')).toBeTrue();
|
|
}
|
|
}));
|
|
|
|
it('should hide the focus ripple on blur', fakeAsync(() => {
|
|
pointerdown();
|
|
pointerup();
|
|
blur();
|
|
flush();
|
|
expect(isRippleVisible('focus')).toBeFalse();
|
|
}));
|
|
|
|
it('should show the active ripple on pointerdown', fakeAsync(() => {
|
|
expect(isRippleVisible('active')).toBeFalse();
|
|
pointerdown();
|
|
expect(isRippleVisible('active')).toBeTrue();
|
|
flush();
|
|
}));
|
|
|
|
it('should hide the active ripple on pointerup', fakeAsync(() => {
|
|
pointerdown();
|
|
pointerup();
|
|
flush();
|
|
expect(isRippleVisible('active')).toBeFalse();
|
|
}));
|
|
|
|
// Edge cases.
|
|
|
|
it('should not show the hover ripple if the thumb is already focused', fakeAsync(() => {
|
|
pointerdown();
|
|
pointerenter();
|
|
flush();
|
|
expect(isRippleVisible('hover')).toBeFalse();
|
|
}));
|
|
|
|
it('should hide the hover ripple if the thumb is focused', fakeAsync(() => {
|
|
pointerenter();
|
|
pointerdown();
|
|
flush();
|
|
expect(isRippleVisible('hover')).toBeFalse();
|
|
}));
|
|
|
|
it('should not hide the focus ripple if the thumb is pressed', fakeAsync(() => {
|
|
pointerdown();
|
|
blur();
|
|
flush();
|
|
expect(isRippleVisible('focus')).toBeTrue();
|
|
}));
|
|
|
|
it('should not hide the hover ripple on blur if the thumb is hovered', fakeAsync(() => {
|
|
pointerenter();
|
|
pointerdown();
|
|
pointerup();
|
|
blur();
|
|
flush();
|
|
expect(isRippleVisible('hover')).toBeTrue();
|
|
}));
|
|
|
|
it('should hide the focus ripple on drag end if the thumb already lost focus', fakeAsync(() => {
|
|
pointerdown();
|
|
blur();
|
|
pointerup();
|
|
flush();
|
|
expect(isRippleVisible('focus')).toBeFalse();
|
|
}));
|
|
});
|
|
|
|
describe('slider with set value', () => {
|
|
let slider: MatSlider;
|
|
let input: MatSliderThumb;
|
|
|
|
beforeEach(waitForAsync(() => {
|
|
const fixture = createComponent(SliderWithValue);
|
|
fixture.detectChanges();
|
|
const sliderDebugElement = fixture.debugElement.query(By.directive(MatSlider));
|
|
slider = sliderDebugElement.componentInstance;
|
|
input = slider._getInput(_MatThumb.END) as MatSliderThumb;
|
|
}));
|
|
|
|
it('should set the default value from the attribute', () => {
|
|
checkInput(input, {min: 0, max: 100, value: 50, translateX: 150});
|
|
});
|
|
|
|
it('should update the value', fakeAsync(() => {
|
|
slideToValue(slider, input, 75);
|
|
checkInput(input, {min: 0, max: 100, value: 75, translateX: 225});
|
|
}));
|
|
});
|
|
|
|
describe('range slider with set value', () => {
|
|
let slider: MatSlider;
|
|
let startInput: MatSliderThumb;
|
|
let endInput: MatSliderThumb;
|
|
|
|
beforeEach(waitForAsync(() => {
|
|
const fixture = createComponent(RangeSliderWithValue);
|
|
fixture.detectChanges();
|
|
const sliderDebugElement = fixture.debugElement.query(By.directive(MatSlider));
|
|
slider = sliderDebugElement.componentInstance;
|
|
startInput = slider._getInput(_MatThumb.START) as MatSliderRangeThumb;
|
|
endInput = slider._getInput(_MatThumb.END) as MatSliderRangeThumb;
|
|
}));
|
|
|
|
it('should set the correct initial values', fakeAsync(() => {
|
|
checkInput(startInput, {min: 0, max: 75, value: 25, translateX: 75});
|
|
checkInput(endInput, {min: 25, max: 100, value: 75, translateX: 225});
|
|
}));
|
|
|
|
it('should update the start value', fakeAsync(() => {
|
|
checkInput(startInput, {min: 0, max: 75, value: 25, translateX: 75});
|
|
checkInput(endInput, {min: 25, max: 100, value: 75, translateX: 225});
|
|
slideToValue(slider, startInput, 30);
|
|
checkInput(startInput, {min: 0, max: 75, value: 30, translateX: 90});
|
|
checkInput(endInput, {min: 30, max: 100, value: 75, translateX: 225});
|
|
}));
|
|
|
|
it('should update the end value', fakeAsync(() => {
|
|
slideToValue(slider, endInput, 77);
|
|
checkInput(startInput, {min: 0, max: 77, value: 25, translateX: 75});
|
|
checkInput(endInput, {min: 25, max: 100, value: 77, translateX: 231});
|
|
}));
|
|
});
|
|
|
|
describe('slider with set step', () => {
|
|
let fixture: ComponentFixture<SliderWithStep>;
|
|
let slider: MatSlider;
|
|
let input: MatSliderThumb;
|
|
|
|
beforeEach(waitForAsync(() => {
|
|
fixture = createComponent(SliderWithStep);
|
|
fixture.detectChanges();
|
|
const sliderDebugElement = fixture.debugElement.query(By.directive(MatSlider));
|
|
slider = sliderDebugElement.componentInstance;
|
|
input = slider._getInput(_MatThumb.END) as MatSliderThumb;
|
|
}));
|
|
|
|
it('should update to the value based on the step', fakeAsync(() => {
|
|
slideToValue(slider, input, 30);
|
|
expect(input.value).toBe(25);
|
|
}));
|
|
|
|
it('should not add decimals to the value if it is a whole number', fakeAsync(() => {
|
|
fixture.componentInstance.step = 0.1;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
slideToValue(slider, input, 11);
|
|
expect(input.value).toBe(11);
|
|
}));
|
|
|
|
it('should truncate long decimal values when using a decimal step', fakeAsync(() => {
|
|
fixture.componentInstance.step = 0.5;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
slideToValue(slider, input, 55.555);
|
|
expect(input.value).toBe(55.5);
|
|
}));
|
|
|
|
it('should update the value on step change', fakeAsync(() => {
|
|
slideToValue(slider, input, 30);
|
|
fixture.componentInstance.step = 50;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
expect(input.value).toBe(50);
|
|
}));
|
|
});
|
|
|
|
describe('range slider with set step', () => {
|
|
let fixture: ComponentFixture<RangeSliderWithStep>;
|
|
let slider: MatSlider;
|
|
let startInput: MatSliderThumb;
|
|
let endInput: MatSliderThumb;
|
|
|
|
beforeEach(waitForAsync(() => {
|
|
fixture = createComponent(RangeSliderWithStep);
|
|
fixture.detectChanges();
|
|
const sliderDebugElement = fixture.debugElement.query(By.directive(MatSlider));
|
|
slider = sliderDebugElement.componentInstance;
|
|
startInput = slider._getInput(_MatThumb.START) as MatSliderRangeThumb;
|
|
endInput = slider._getInput(_MatThumb.END) as MatSliderRangeThumb;
|
|
}));
|
|
|
|
it('should set the correct start value on slide', fakeAsync(() => {
|
|
slideToValue(slider, startInput, 30);
|
|
expect(startInput.value).toBe(25);
|
|
}));
|
|
|
|
it('should set the correct end value on slide', fakeAsync(() => {
|
|
slideToValue(slider, endInput, 45);
|
|
expect(endInput.value).toBe(50);
|
|
}));
|
|
|
|
it('should not add decimals to the end value if it is a whole number', fakeAsync(() => {
|
|
fixture.componentInstance.step = 0.1;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
slideToValue(slider, endInput, 11);
|
|
expect(endInput.value).toBe(11);
|
|
}));
|
|
|
|
it('should not add decimals to the start value if it is a whole number', fakeAsync(() => {
|
|
fixture.componentInstance.step = 0.1;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
slideToValue(slider, startInput, 11);
|
|
expect(startInput.value).toBe(11);
|
|
}));
|
|
|
|
it('should truncate long decimal start values when using a decimal step', fakeAsync(() => {
|
|
fixture.componentInstance.step = 0.1;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
slideToValue(slider, startInput, 33.666);
|
|
expect(startInput.value).toBe(33.7);
|
|
}));
|
|
|
|
it('should truncate long decimal end values when using a decimal step', fakeAsync(() => {
|
|
fixture.componentInstance.step = 0.1;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
slideToValue(slider, endInput, 33.6666);
|
|
expect(endInput.value).toBe(33.7);
|
|
}));
|
|
|
|
describe('should handle step changes', () => {
|
|
it('where the new start tx is greater than the old end tx', fakeAsync(() => {
|
|
fixture.componentInstance.step = 0;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
|
|
slideToValue(slider, startInput, 45);
|
|
slideToValue(slider, endInput, 46);
|
|
|
|
checkInput(startInput, {min: 0, max: 46, value: 45, translateX: 135});
|
|
checkInput(endInput, {min: 45, max: 100, value: 46, translateX: 138});
|
|
|
|
fixture.componentInstance.step = 50;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
|
|
checkInput(startInput, {min: 0, max: 50, value: 50, translateX: 150});
|
|
checkInput(endInput, {min: 50, max: 100, value: 50, translateX: 150});
|
|
}));
|
|
|
|
it('where the new end tx is less than the old start tx', fakeAsync(() => {
|
|
fixture.componentInstance.step = 0;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
|
|
slideToValue(slider, startInput, 21);
|
|
slideToValue(slider, endInput, 22);
|
|
|
|
checkInput(startInput, {min: 0, max: 22, value: 21, translateX: 63});
|
|
checkInput(endInput, {min: 21, max: 100, value: 22, translateX: 66});
|
|
|
|
fixture.componentInstance.step = 50;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
|
|
checkInput(startInput, {min: 0, max: 0, value: 0, translateX: 0});
|
|
checkInput(endInput, {min: 0, max: 100, value: 0, translateX: 0});
|
|
}));
|
|
});
|
|
});
|
|
|
|
describe('slider with custom thumb label formatting', () => {
|
|
let fixture: ComponentFixture<DiscreteSliderWithDisplayWith>;
|
|
let slider: MatSlider;
|
|
let input: MatSliderThumb;
|
|
let valueIndicatorTextElement: Element;
|
|
|
|
beforeEach(() => {
|
|
fixture = createComponent(DiscreteSliderWithDisplayWith);
|
|
fixture.detectChanges();
|
|
const sliderDebugElement = fixture.debugElement.query(By.directive(MatSlider))!;
|
|
const sliderNativeElement = sliderDebugElement.nativeElement;
|
|
slider = sliderDebugElement.componentInstance;
|
|
valueIndicatorTextElement = sliderNativeElement.querySelector(
|
|
'.mdc-slider__value-indicator-text',
|
|
)!;
|
|
input = slider._getInput(_MatThumb.END) as MatSliderThumb;
|
|
});
|
|
|
|
it('should set the aria-valuetext attribute with the given `displayWith` function', fakeAsync(() => {
|
|
expect(input._hostElement.getAttribute('aria-valuetext')).toBe('$1');
|
|
setValueByClick(slider, input, 199);
|
|
fixture.detectChanges();
|
|
flush();
|
|
expect(input._hostElement.getAttribute('aria-valuetext')).toBe('$199');
|
|
}));
|
|
|
|
it('should invoke the passed-in `displayWith` function with the value', fakeAsync(() => {
|
|
spyOn(slider, 'displayWith').and.callThrough();
|
|
setValueByClick(slider, input, 199);
|
|
expect(slider.displayWith).toHaveBeenCalledWith(199);
|
|
}));
|
|
|
|
it('should format the thumb label based on the passed-in `displayWith` function', fakeAsync(() => {
|
|
setValueByClick(slider, input, 149);
|
|
fixture.detectChanges();
|
|
expect(valueIndicatorTextElement.textContent).toBe('$149');
|
|
}));
|
|
});
|
|
|
|
describe('range slider with custom thumb label formatting', () => {
|
|
let fixture: ComponentFixture<DiscreteRangeSliderWithDisplayWith>;
|
|
let slider: MatSlider;
|
|
let startValueIndicatorTextElement: Element;
|
|
let endValueIndicatorTextElement: Element;
|
|
let startInput: MatSliderThumb;
|
|
let endInput: MatSliderThumb;
|
|
|
|
beforeEach(() => {
|
|
fixture = createComponent(DiscreteRangeSliderWithDisplayWith);
|
|
fixture.detectChanges();
|
|
const sliderDebugElement = fixture.debugElement.query(By.directive(MatSlider))!;
|
|
slider = sliderDebugElement.componentInstance;
|
|
startInput = slider._getInput(_MatThumb.START) as MatSliderRangeThumb;
|
|
endInput = slider._getInput(_MatThumb.END) as MatSliderRangeThumb;
|
|
|
|
const startThumbElement = slider._getThumb(_MatThumb.START)._hostElement;
|
|
const endThumbElement = slider._getThumb(_MatThumb.END)._hostElement;
|
|
startValueIndicatorTextElement = startThumbElement.querySelector(
|
|
'.mdc-slider__value-indicator-text',
|
|
)!;
|
|
endValueIndicatorTextElement = endThumbElement.querySelector(
|
|
'.mdc-slider__value-indicator-text',
|
|
)!;
|
|
});
|
|
|
|
it('should set the aria-valuetext attribute with the given `displayWith` function', fakeAsync(() => {
|
|
expect(startInput._hostElement.getAttribute('aria-valuetext')).toBe('$1');
|
|
expect(endInput._hostElement.getAttribute('aria-valuetext')).toBe('$200');
|
|
setValueByClick(slider, startInput, 25);
|
|
setValueByClick(slider, endInput, 81);
|
|
expect(startInput._hostElement.getAttribute('aria-valuetext')).toBe('$25');
|
|
expect(endInput._hostElement.getAttribute('aria-valuetext')).toBe('$81');
|
|
}));
|
|
|
|
it('should invoke the passed-in `displayWith` function with the start value', fakeAsync(() => {
|
|
spyOn(slider, 'displayWith').and.callThrough();
|
|
setValueByClick(slider, startInput, 197);
|
|
expect(slider.displayWith).toHaveBeenCalledWith(197);
|
|
}));
|
|
|
|
it('should invoke the passed-in `displayWith` function with the end value', fakeAsync(() => {
|
|
spyOn(slider, 'displayWith').and.callThrough();
|
|
setValueByClick(slider, endInput, 72);
|
|
expect(slider.displayWith).toHaveBeenCalledWith(72);
|
|
}));
|
|
|
|
it('should format the start thumb label based on the passed-in `displayWith` function', fakeAsync(() => {
|
|
setValueByClick(slider, startInput, 120);
|
|
fixture.detectChanges();
|
|
expect(startValueIndicatorTextElement.textContent).toBe('$120');
|
|
}));
|
|
|
|
it('should format the end thumb label based on the passed-in `displayWith` function', fakeAsync(() => {
|
|
setValueByClick(slider, endInput, 70);
|
|
fixture.detectChanges();
|
|
expect(endValueIndicatorTextElement.textContent).toBe('$70');
|
|
}));
|
|
});
|
|
|
|
describe('slider with value property binding', () => {
|
|
let fixture: ComponentFixture<SliderWithOneWayBinding>;
|
|
let input: MatSliderThumb;
|
|
|
|
beforeEach(waitForAsync(() => {
|
|
fixture = createComponent(SliderWithOneWayBinding);
|
|
fixture.detectChanges();
|
|
const sliderDebugElement = fixture.debugElement.query(By.directive(MatSlider));
|
|
const slider = sliderDebugElement.componentInstance;
|
|
input = slider._getInput(_MatThumb.END) as MatSliderThumb;
|
|
}));
|
|
|
|
it('should update when bound value changes', () => {
|
|
fixture.componentInstance.value = 75;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
expect(input.value).toBe(75);
|
|
});
|
|
});
|
|
|
|
describe('range slider with value property binding', () => {
|
|
let fixture: ComponentFixture<RangeSliderWithOneWayBinding>;
|
|
let startInput: MatSliderThumb;
|
|
let endInput: MatSliderThumb;
|
|
|
|
beforeEach(waitForAsync(() => {
|
|
fixture = createComponent(RangeSliderWithOneWayBinding);
|
|
fixture.detectChanges();
|
|
const sliderDebugElement = fixture.debugElement.query(By.directive(MatSlider));
|
|
const slider = sliderDebugElement.componentInstance;
|
|
startInput = slider._getInput(_MatThumb.START) as MatSliderRangeThumb;
|
|
endInput = slider._getInput(_MatThumb.END) as MatSliderRangeThumb;
|
|
}));
|
|
|
|
it('should update when bound start value changes', () => {
|
|
fixture.componentInstance.startValue = 30;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
expect(startInput.value).toBe(30);
|
|
});
|
|
|
|
it('should update when bound end value changes', () => {
|
|
fixture.componentInstance.endValue = 70;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
expect(endInput.value).toBe(70);
|
|
});
|
|
|
|
it('should update the input width when the start value changes', () => {
|
|
const startInputEl = startInput._elementRef.nativeElement;
|
|
const endInputEl = endInput._elementRef.nativeElement;
|
|
const startInputWidthBefore = startInputEl.getBoundingClientRect().width;
|
|
const endInputWidthBefore = endInputEl.getBoundingClientRect().width;
|
|
fixture.componentInstance.startValue = 10;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
const startInputWidthAfter = startInputEl.getBoundingClientRect().width;
|
|
const endInputWidthAfter = endInputEl.getBoundingClientRect().width;
|
|
expect(startInputWidthBefore).not.toBe(startInputWidthAfter);
|
|
expect(endInputWidthBefore).not.toBe(endInputWidthAfter);
|
|
});
|
|
|
|
it('should update the input width when the end value changes', () => {
|
|
const startInputEl = startInput._elementRef.nativeElement;
|
|
const endInputEl = endInput._elementRef.nativeElement;
|
|
const startInputWidthBefore = startInputEl.getBoundingClientRect().width;
|
|
const endInputWidthBefore = endInputEl.getBoundingClientRect().width;
|
|
fixture.componentInstance.endValue = 90;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
const startInputWidthAfter = startInputEl.getBoundingClientRect().width;
|
|
const endInputWidthAfter = endInputEl.getBoundingClientRect().width;
|
|
expect(startInputWidthBefore).not.toBe(startInputWidthAfter);
|
|
expect(endInputWidthBefore).not.toBe(endInputWidthAfter);
|
|
});
|
|
});
|
|
|
|
describe('slider with direction', () => {
|
|
let slider: MatSlider;
|
|
let input: MatSliderThumb;
|
|
|
|
beforeEach(waitForAsync(() => {
|
|
const fixture = createComponent(StandardSlider, [
|
|
{
|
|
provide: Directionality,
|
|
useValue: {value: 'rtl', change: of()},
|
|
},
|
|
]);
|
|
fixture.detectChanges();
|
|
const sliderDebugElement = fixture.debugElement.query(By.directive(MatSlider));
|
|
slider = sliderDebugElement.componentInstance;
|
|
input = slider._getInput(_MatThumb.END) as MatSliderThumb;
|
|
}));
|
|
|
|
it('works in RTL languages', fakeAsync(() => {
|
|
setValueByClick(slider, input, 25, true);
|
|
checkInput(input, {min: 0, max: 100, value: 75, translateX: 75});
|
|
}));
|
|
});
|
|
|
|
describe('range slider with direction', () => {
|
|
let slider: MatSlider;
|
|
let startInput: MatSliderThumb;
|
|
let endInput: MatSliderThumb;
|
|
|
|
beforeEach(waitForAsync(() => {
|
|
const fixture = createComponent(StandardRangeSlider, [
|
|
{
|
|
provide: Directionality,
|
|
useValue: {value: 'rtl', change: of()},
|
|
},
|
|
]);
|
|
fixture.detectChanges();
|
|
const sliderDebugElement = fixture.debugElement.query(By.directive(MatSlider));
|
|
slider = sliderDebugElement.componentInstance;
|
|
startInput = slider._getInput(_MatThumb.START) as MatSliderRangeThumb;
|
|
endInput = slider._getInput(_MatThumb.END) as MatSliderRangeThumb;
|
|
}));
|
|
|
|
it('works in RTL languages', fakeAsync(() => {
|
|
setValueByClick(slider, startInput, 90, true);
|
|
checkInput(startInput, {min: 0, max: 100, value: 10, translateX: 270});
|
|
|
|
setValueByClick(slider, endInput, 10, true);
|
|
checkInput(endInput, {min: 10, max: 100, value: 90, translateX: 30});
|
|
}));
|
|
});
|
|
|
|
describe('slider with ngModel', () => {
|
|
let fixture: ComponentFixture<SliderWithNgModel>;
|
|
let slider: MatSlider;
|
|
let input: MatSliderThumb;
|
|
|
|
beforeEach(waitForAsync(() => {
|
|
fixture = createComponent(SliderWithNgModel);
|
|
fixture.detectChanges();
|
|
const sliderDebugElement = fixture.debugElement.query(By.directive(MatSlider));
|
|
slider = sliderDebugElement.componentInstance;
|
|
input = slider._getInput(_MatThumb.END) as MatSliderThumb;
|
|
}));
|
|
|
|
it('should update the model', fakeAsync(() => {
|
|
slideToValue(slider, input, 19);
|
|
fixture.detectChanges();
|
|
expect(fixture.componentInstance.val).toBe(19);
|
|
checkInput(input, {min: 0, max: 100, value: 19, translateX: 57});
|
|
}));
|
|
|
|
it('should update the slider', fakeAsync(() => {
|
|
fixture.componentInstance.val = 20;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
flush();
|
|
checkInput(input, {min: 0, max: 100, value: 20, translateX: 60});
|
|
}));
|
|
|
|
it('should be able to reset a slider by setting the model back to undefined', fakeAsync(() => {
|
|
fixture.componentInstance.val = 5;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
flush();
|
|
checkInput(input, {min: 0, max: 100, value: 5, translateX: 15});
|
|
|
|
fixture.componentInstance.val = undefined;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
flush();
|
|
checkInput(input, {min: 0, max: 100, value: 0, translateX: 0});
|
|
}));
|
|
});
|
|
|
|
describe('range slider with ngModel', () => {
|
|
let slider: MatSlider;
|
|
let fixture: ComponentFixture<RangeSliderWithNgModel>;
|
|
let startInput: MatSliderThumb;
|
|
let endInput: MatSliderThumb;
|
|
|
|
beforeEach(waitForAsync(() => {
|
|
fixture = createComponent(RangeSliderWithNgModel);
|
|
fixture.detectChanges();
|
|
const sliderDebugElement = fixture.debugElement.query(By.directive(MatSlider));
|
|
slider = sliderDebugElement.componentInstance;
|
|
startInput = slider._getInput(_MatThumb.START) as MatSliderRangeThumb;
|
|
endInput = slider._getInput(_MatThumb.END) as MatSliderRangeThumb;
|
|
}));
|
|
|
|
it('should update the models on input value changes', fakeAsync(() => {
|
|
slideToValue(slider, startInput, 25);
|
|
fixture.detectChanges();
|
|
flush();
|
|
checkInput(startInput, {min: 0, max: 100, value: 25, translateX: 75});
|
|
|
|
slideToValue(slider, endInput, 75);
|
|
fixture.detectChanges();
|
|
flush();
|
|
checkInput(endInput, {min: 25, max: 100, value: 75, translateX: 225});
|
|
}));
|
|
|
|
it('should update the thumbs on ngModel value change', fakeAsync(() => {
|
|
fixture.componentInstance.startVal = 50;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
flush();
|
|
checkInput(startInput, {min: 0, max: 100, value: 50, translateX: 150});
|
|
|
|
fixture.componentInstance.endVal = 75;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
flush();
|
|
checkInput(endInput, {min: 50, max: 100, value: 75, translateX: 225});
|
|
}));
|
|
|
|
it('should be able to reset a start input', fakeAsync(() => {
|
|
fixture.componentInstance.startVal = 5;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
flush();
|
|
checkInput(startInput, {min: 0, max: 100, value: 5, translateX: 15});
|
|
|
|
fixture.componentInstance.startVal = undefined;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
flush();
|
|
checkInput(startInput, {min: 0, max: 100, value: 0, translateX: 0});
|
|
}));
|
|
|
|
it('should be able to reset an end input', fakeAsync(() => {
|
|
fixture.componentInstance.endVal = 99;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
flush();
|
|
checkInput(endInput, {min: 0, max: 100, value: 99, translateX: 297});
|
|
|
|
fixture.componentInstance.endVal = undefined;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
flush();
|
|
checkInput(endInput, {min: 0, max: 100, value: 0, translateX: 0});
|
|
}));
|
|
});
|
|
|
|
describe('range slider w/ NgModel edge case', () => {
|
|
it('should initialize correctly despite NgModel `null` bug', fakeAsync(() => {
|
|
const fixture = createComponent(RangeSliderWithNgModelEdgeCase);
|
|
fixture.detectChanges();
|
|
const sliderDebugElement = fixture.debugElement.query(By.directive(MatSlider));
|
|
const slider = sliderDebugElement.componentInstance;
|
|
const startInput = slider._getInput(_MatThumb.START) as MatSliderRangeThumb;
|
|
const endInput = slider._getInput(_MatThumb.END) as MatSliderRangeThumb;
|
|
flush();
|
|
checkInput(startInput, {min: -1, max: -0.3, value: -0.7, translateX: 90});
|
|
checkInput(endInput, {min: -0.7, max: 0, value: -0.3, translateX: 210});
|
|
}));
|
|
});
|
|
|
|
describe('slider as a custom form control', () => {
|
|
let fixture: ComponentFixture<SliderWithFormControl>;
|
|
let slider: MatSlider;
|
|
let input: MatSliderThumb;
|
|
|
|
beforeEach(waitForAsync(() => {
|
|
fixture = createComponent(SliderWithFormControl);
|
|
fixture.detectChanges();
|
|
const sliderDebugElement = fixture.debugElement.query(By.directive(MatSlider));
|
|
slider = sliderDebugElement.componentInstance;
|
|
input = slider._getInput(_MatThumb.END) as MatSliderThumb;
|
|
}));
|
|
|
|
it('should update the control on slide', fakeAsync(() => {
|
|
expect(fixture.componentInstance.control.value).toBe(0);
|
|
slideToValue(slider, input, 19);
|
|
expect(fixture.componentInstance.control.value).toBe(19);
|
|
}));
|
|
|
|
it('should update the value when the control is set', () => {
|
|
expect(input.value).toBe(0);
|
|
fixture.componentInstance.control.setValue(7);
|
|
checkInput(input, {min: 0, max: 100, value: 7, translateX: 21});
|
|
});
|
|
|
|
it('should update the disabled state when control is disabled', () => {
|
|
expect(slider.disabled).toBe(false);
|
|
fixture.componentInstance.control.disable();
|
|
expect(slider.disabled).toBe(true);
|
|
});
|
|
|
|
it('should update the disabled state when the control is enabled', () => {
|
|
slider.disabled = true;
|
|
fixture.componentInstance.control.enable();
|
|
expect(slider.disabled).toBe(false);
|
|
});
|
|
|
|
it('should have the correct control state initially and after interaction', fakeAsync(() => {
|
|
let sliderControl = fixture.componentInstance.control;
|
|
|
|
// The control should start off valid, pristine, and untouched.
|
|
expect(sliderControl.valid).toBe(true);
|
|
expect(sliderControl.pristine).toBe(true);
|
|
expect(sliderControl.touched).toBe(false);
|
|
|
|
// After changing the value, the control should become dirty (not pristine),
|
|
// but remain untouched.
|
|
setValueByClick(slider, input, 50);
|
|
|
|
expect(sliderControl.valid).toBe(true);
|
|
expect(sliderControl.pristine).toBe(false);
|
|
expect(sliderControl.touched).toBe(false);
|
|
|
|
// If the control has been visited due to interaction, the control should remain
|
|
// dirty and now also be touched.
|
|
input.blur();
|
|
fixture.detectChanges();
|
|
|
|
expect(sliderControl.valid).toBe(true);
|
|
expect(sliderControl.pristine).toBe(false);
|
|
expect(sliderControl.touched).toBe(true);
|
|
}));
|
|
});
|
|
|
|
describe('slider as a custom form control', () => {
|
|
let fixture: ComponentFixture<RangeSliderWithFormControl>;
|
|
let slider: MatSlider;
|
|
let startInput: MatSliderThumb;
|
|
let endInput: MatSliderThumb;
|
|
|
|
beforeEach(waitForAsync(() => {
|
|
fixture = createComponent(RangeSliderWithFormControl);
|
|
fixture.detectChanges();
|
|
const sliderDebugElement = fixture.debugElement.query(By.directive(MatSlider));
|
|
slider = sliderDebugElement.componentInstance;
|
|
startInput = slider._getInput(_MatThumb.START) as MatSliderRangeThumb;
|
|
endInput = slider._getInput(_MatThumb.END) as MatSliderRangeThumb;
|
|
}));
|
|
|
|
it('should update the start input control on slide', fakeAsync(() => {
|
|
expect(fixture.componentInstance.startInputControl.value).toBe(0);
|
|
slideToValue(slider, startInput, 20);
|
|
expect(fixture.componentInstance.startInputControl.value).toBe(20);
|
|
}));
|
|
|
|
it('should update the end input control on slide', fakeAsync(() => {
|
|
expect(fixture.componentInstance.endInputControl.value).toBe(100);
|
|
slideToValue(slider, endInput, 80);
|
|
expect(fixture.componentInstance.endInputControl.value).toBe(80);
|
|
}));
|
|
|
|
it('should update the start input value when the start input control is set', () => {
|
|
expect(startInput.value).toBe(0);
|
|
fixture.componentInstance.startInputControl.setValue(10);
|
|
checkInput(startInput, {min: 0, max: 100, value: 10, translateX: 30});
|
|
});
|
|
|
|
it('should update the end input value when the end input control is set', () => {
|
|
expect(endInput.value).toBe(100);
|
|
fixture.componentInstance.endInputControl.setValue(90);
|
|
checkInput(endInput, {min: 0, max: 100, value: 90, translateX: 270});
|
|
});
|
|
|
|
it('should update the disabled state if the start input control is disabled', () => {
|
|
expect(slider.disabled).toBe(false);
|
|
fixture.componentInstance.startInputControl.disable();
|
|
expect(slider.disabled).toBe(true);
|
|
});
|
|
|
|
it('should update the disabled state if the end input control is disabled', () => {
|
|
expect(slider.disabled).toBe(false);
|
|
fixture.componentInstance.endInputControl.disable();
|
|
expect(slider.disabled).toBe(true);
|
|
});
|
|
|
|
it('should update the disabled state when both input controls are enabled', () => {
|
|
slider.disabled = true;
|
|
fixture.componentInstance.startInputControl.enable();
|
|
expect(slider.disabled).toBe(false);
|
|
fixture.componentInstance.endInputControl.enable();
|
|
expect(slider.disabled).toBe(false);
|
|
});
|
|
|
|
it('should have the correct start input control state initially and after interaction', fakeAsync(() => {
|
|
let sliderControl = fixture.componentInstance.startInputControl;
|
|
|
|
// The control should start off valid, pristine, and untouched.
|
|
expect(sliderControl.valid).toBe(true);
|
|
expect(sliderControl.pristine).toBe(true);
|
|
expect(sliderControl.touched).toBe(false);
|
|
|
|
// After changing the value, the control should become dirty (not pristine),
|
|
// but remain untouched.
|
|
setValueByClick(slider, startInput, 25);
|
|
|
|
expect(sliderControl.valid).toBe(true);
|
|
expect(sliderControl.pristine).toBe(false);
|
|
expect(sliderControl.touched).toBe(false);
|
|
|
|
// If the control has been visited due to interaction, the control should remain
|
|
// dirty and now also be touched.
|
|
startInput.blur();
|
|
fixture.detectChanges();
|
|
|
|
expect(sliderControl.valid).toBe(true);
|
|
expect(sliderControl.pristine).toBe(false);
|
|
expect(sliderControl.touched).toBe(true);
|
|
}));
|
|
|
|
it('should have the correct start input control state initially and after interaction', fakeAsync(() => {
|
|
let sliderControl = fixture.componentInstance.endInputControl;
|
|
|
|
// The control should start off valid, pristine, and untouched.
|
|
expect(sliderControl.valid).toBe(true);
|
|
expect(sliderControl.pristine).toBe(true);
|
|
expect(sliderControl.touched).toBe(false);
|
|
|
|
// After changing the value, the control should become dirty (not pristine),
|
|
// but remain untouched.
|
|
setValueByClick(slider, endInput, 75);
|
|
|
|
expect(sliderControl.valid).toBe(true);
|
|
expect(sliderControl.pristine).toBe(false);
|
|
expect(sliderControl.touched).toBe(false);
|
|
|
|
// If the control has been visited due to interaction, the control should remain
|
|
// dirty and now also be touched.
|
|
endInput.blur();
|
|
fixture.detectChanges();
|
|
|
|
expect(sliderControl.valid).toBe(true);
|
|
expect(sliderControl.pristine).toBe(false);
|
|
expect(sliderControl.touched).toBe(true);
|
|
}));
|
|
});
|
|
|
|
describe('slider with a two-way binding', () => {
|
|
let input: MatSliderThumb;
|
|
let slider: MatSlider;
|
|
let fixture: ComponentFixture<SliderWithTwoWayBinding>;
|
|
|
|
beforeEach(() => {
|
|
fixture = createComponent(SliderWithTwoWayBinding);
|
|
fixture.detectChanges();
|
|
const sliderDebugElement = fixture.debugElement.query(By.directive(MatSlider));
|
|
slider = sliderDebugElement.componentInstance;
|
|
input = slider._getInput(_MatThumb.END) as MatSliderThumb;
|
|
});
|
|
|
|
it('should sync the value binding in both directions', fakeAsync(() => {
|
|
checkInput(input, {min: 0, max: 100, value: 0, step: 1, translateX: 0});
|
|
|
|
slideToValue(slider, input, 10);
|
|
expect(fixture.componentInstance.value).toBe(10);
|
|
checkInput(input, {min: 0, max: 100, value: 10, step: 1, translateX: 30});
|
|
|
|
fixture.componentInstance.value = 20;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
expect(fixture.componentInstance.value).toBe(20);
|
|
checkInput(input, {min: 0, max: 100, value: 20, step: 1, translateX: 60});
|
|
}));
|
|
});
|
|
|
|
describe('range slider with a two-way binding', () => {
|
|
let slider: MatSlider;
|
|
let startInput: MatSliderRangeThumb;
|
|
let endInput: MatSliderRangeThumb;
|
|
let fixture: ComponentFixture<RangeSliderWithTwoWayBinding>;
|
|
|
|
beforeEach(waitForAsync(() => {
|
|
fixture = createComponent(RangeSliderWithTwoWayBinding);
|
|
fixture.detectChanges();
|
|
const sliderDebugElement = fixture.debugElement.query(By.directive(MatSlider));
|
|
slider = sliderDebugElement.componentInstance;
|
|
endInput = slider._getInput(_MatThumb.END) as MatSliderRangeThumb;
|
|
startInput = slider._getInput(_MatThumb.START) as MatSliderRangeThumb;
|
|
}));
|
|
|
|
it('should sync the start value binding in both directions', fakeAsync(() => {
|
|
expect(fixture.componentInstance.startValue).toBe(0);
|
|
expect(startInput.value).toBe(0);
|
|
|
|
slideToValue(slider, startInput, 10);
|
|
|
|
expect(fixture.componentInstance.startValue).toBe(10);
|
|
expect(startInput.value).toBe(10);
|
|
|
|
fixture.componentInstance.startValue = 20;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
expect(fixture.componentInstance.startValue).toBe(20);
|
|
expect(startInput.value).toBe(20);
|
|
}));
|
|
|
|
it('should sync the end value binding in both directions', fakeAsync(() => {
|
|
expect(fixture.componentInstance.endValue).toBe(100);
|
|
expect(endInput.value).toBe(100);
|
|
|
|
slideToValue(slider, endInput, 90);
|
|
expect(fixture.componentInstance.endValue).toBe(90);
|
|
expect(endInput.value).toBe(90);
|
|
|
|
fixture.componentInstance.endValue = 80;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
expect(fixture.componentInstance.endValue).toBe(80);
|
|
expect(endInput.value).toBe(80);
|
|
}));
|
|
});
|
|
});
|
|
|
|
const SLIDER_STYLES = ['.mat-mdc-slider { width: 300px; }'];
|
|
|
|
@Component({
|
|
template: `
|
|
<mat-slider>
|
|
<input matSliderThumb>
|
|
</mat-slider>
|
|
`,
|
|
styles: SLIDER_STYLES,
|
|
standalone: false,
|
|
})
|
|
class StandardSlider {}
|
|
|
|
@Component({
|
|
template: `
|
|
<mat-slider>
|
|
<input matSliderStartThumb>
|
|
<input matSliderEndThumb>
|
|
</mat-slider>
|
|
`,
|
|
styles: SLIDER_STYLES,
|
|
standalone: false,
|
|
})
|
|
class StandardRangeSlider {}
|
|
|
|
@Component({
|
|
template: `
|
|
<mat-slider disabled>
|
|
<input matSliderThumb>
|
|
</mat-slider>
|
|
`,
|
|
styles: SLIDER_STYLES,
|
|
standalone: false,
|
|
})
|
|
class DisabledSlider {}
|
|
|
|
@Component({
|
|
template: `
|
|
<mat-slider disabled>
|
|
<input matSliderStartThumb>
|
|
<input matSliderEndThumb>
|
|
</mat-slider>
|
|
`,
|
|
styles: SLIDER_STYLES,
|
|
standalone: false,
|
|
})
|
|
class DisabledRangeSlider {}
|
|
|
|
@Component({
|
|
template: `
|
|
<mat-slider [min]="min" [max]="max">
|
|
<input matSliderThumb>
|
|
</mat-slider>
|
|
`,
|
|
styles: SLIDER_STYLES,
|
|
standalone: false,
|
|
})
|
|
class SliderWithMinAndMax {
|
|
min = 25;
|
|
max = 75;
|
|
}
|
|
|
|
@Component({
|
|
template: `
|
|
<mat-slider [min]="min" [max]="max">
|
|
<input matSliderStartThumb>
|
|
<input matSliderEndThumb>
|
|
</mat-slider>
|
|
`,
|
|
styles: SLIDER_STYLES,
|
|
standalone: false,
|
|
})
|
|
class RangeSliderWithMinAndMax {
|
|
min = 25;
|
|
max = 75;
|
|
}
|
|
|
|
@Component({
|
|
template: `
|
|
<mat-slider>
|
|
<input value="50" matSliderThumb>
|
|
</mat-slider>
|
|
`,
|
|
styles: SLIDER_STYLES,
|
|
standalone: false,
|
|
})
|
|
class SliderWithValue {}
|
|
|
|
@Component({
|
|
template: `
|
|
<mat-slider>
|
|
<input value="25" matSliderStartThumb>
|
|
<input value="75" matSliderEndThumb>
|
|
</mat-slider>
|
|
`,
|
|
styles: SLIDER_STYLES,
|
|
standalone: false,
|
|
})
|
|
class RangeSliderWithValue {}
|
|
|
|
@Component({
|
|
template: `
|
|
<mat-slider [step]="step">
|
|
<input matSliderThumb>
|
|
</mat-slider>
|
|
`,
|
|
styles: SLIDER_STYLES,
|
|
standalone: false,
|
|
})
|
|
class SliderWithStep {
|
|
step = 25;
|
|
}
|
|
|
|
@Component({
|
|
template: `
|
|
<mat-slider [step]="step">
|
|
<input matSliderStartThumb>
|
|
<input matSliderEndThumb>
|
|
</mat-slider>
|
|
`,
|
|
styles: SLIDER_STYLES,
|
|
standalone: false,
|
|
})
|
|
class RangeSliderWithStep {
|
|
step = 25;
|
|
}
|
|
|
|
@Component({
|
|
template: `
|
|
<mat-slider [displayWith]="displayWith" min="1" max="200" discrete>
|
|
<input matSliderThumb>
|
|
</mat-slider>
|
|
`,
|
|
styles: SLIDER_STYLES,
|
|
standalone: false,
|
|
})
|
|
class DiscreteSliderWithDisplayWith {
|
|
displayWith(v: number) {
|
|
return `$${v}`;
|
|
}
|
|
}
|
|
|
|
@Component({
|
|
template: `
|
|
<mat-slider [displayWith]="displayWith" min="1" max="200" discrete>
|
|
<input matSliderStartThumb>
|
|
<input matSliderEndThumb>
|
|
</mat-slider>
|
|
`,
|
|
styles: SLIDER_STYLES,
|
|
standalone: false,
|
|
})
|
|
class DiscreteRangeSliderWithDisplayWith {
|
|
displayWith(v: number) {
|
|
return `$${v}`;
|
|
}
|
|
}
|
|
|
|
@Component({
|
|
template: `
|
|
<mat-slider>
|
|
<input [value]="value" matSliderThumb>
|
|
</mat-slider>
|
|
`,
|
|
styles: SLIDER_STYLES,
|
|
standalone: false,
|
|
})
|
|
class SliderWithOneWayBinding {
|
|
value = 50;
|
|
}
|
|
|
|
@Component({
|
|
template: `
|
|
<mat-slider>
|
|
<input [value]="startValue" matSliderStartThumb>
|
|
<input [value]="endValue" matSliderEndThumb>
|
|
</mat-slider>
|
|
`,
|
|
styles: SLIDER_STYLES,
|
|
standalone: false,
|
|
})
|
|
class RangeSliderWithOneWayBinding {
|
|
startValue = 25;
|
|
endValue = 75;
|
|
}
|
|
|
|
@Component({
|
|
template: `
|
|
<mat-slider>
|
|
<input [(ngModel)]="val" matSliderThumb>
|
|
</mat-slider>
|
|
`,
|
|
styles: SLIDER_STYLES,
|
|
standalone: false,
|
|
})
|
|
class SliderWithNgModel {
|
|
@ViewChild(MatSlider) slider: MatSlider;
|
|
val: number | undefined = 0;
|
|
}
|
|
|
|
@Component({
|
|
template: `
|
|
<mat-slider>
|
|
<input [(ngModel)]="startVal" matSliderStartThumb>
|
|
<input [(ngModel)]="endVal" matSliderEndThumb>
|
|
</mat-slider>
|
|
`,
|
|
styles: SLIDER_STYLES,
|
|
standalone: false,
|
|
})
|
|
class RangeSliderWithNgModel {
|
|
@ViewChild(MatSlider) slider: MatSlider;
|
|
startVal: number | undefined = 0;
|
|
endVal: number | undefined = 100;
|
|
}
|
|
|
|
@Component({
|
|
template: `
|
|
<mat-slider min="-1" max="0" step="0.1">
|
|
<input [(ngModel)]="startValue" matSliderStartThumb />
|
|
<input [(ngModel)]="endValue" matSliderEndThumb />
|
|
</mat-slider>
|
|
|
|
`,
|
|
styles: SLIDER_STYLES,
|
|
standalone: false,
|
|
})
|
|
class RangeSliderWithNgModelEdgeCase {
|
|
@ViewChild(MatSlider) slider: MatSlider;
|
|
startValue: number = -0.7;
|
|
endValue: number = -0.3;
|
|
}
|
|
|
|
@Component({
|
|
template: `
|
|
<mat-slider>
|
|
<input [formControl]="control" matSliderThumb>
|
|
</mat-slider>`,
|
|
styles: SLIDER_STYLES,
|
|
standalone: false,
|
|
})
|
|
class SliderWithFormControl {
|
|
control = new FormControl(0);
|
|
}
|
|
|
|
@Component({
|
|
template: `
|
|
<mat-slider>
|
|
<input [formControl]="startInputControl" matSliderStartThumb>
|
|
<input [formControl]="endInputControl" matSliderEndThumb>
|
|
</mat-slider>`,
|
|
styles: SLIDER_STYLES,
|
|
standalone: false,
|
|
})
|
|
class RangeSliderWithFormControl {
|
|
startInputControl = new FormControl(0);
|
|
endInputControl = new FormControl(100);
|
|
}
|
|
|
|
@Component({
|
|
template: `
|
|
<mat-slider>
|
|
<input [(value)]="value" matSliderThumb>
|
|
</mat-slider>
|
|
`,
|
|
styles: SLIDER_STYLES,
|
|
standalone: false,
|
|
})
|
|
class SliderWithTwoWayBinding {
|
|
value = 0;
|
|
}
|
|
|
|
@Component({
|
|
template: `
|
|
<mat-slider>
|
|
<input [(value)]="startValue" matSliderStartThumb>
|
|
<input [(value)]="endValue" matSliderEndThumb>
|
|
</mat-slider>
|
|
`,
|
|
styles: SLIDER_STYLES,
|
|
standalone: false,
|
|
})
|
|
class RangeSliderWithTwoWayBinding {
|
|
@ViewChild(MatSlider) slider: MatSlider;
|
|
@ViewChildren(MatSliderThumb) sliderInputs: QueryList<MatSliderThumb>;
|
|
startValue = 0;
|
|
endValue = 100;
|
|
}
|
|
|
|
/** Clicks on the MatSlider at the coordinates corresponding to the given value. */
|
|
function setValueByClick(
|
|
slider: MatSlider,
|
|
input: MatSliderThumb,
|
|
value: number,
|
|
isRtl: boolean = false,
|
|
) {
|
|
const inputElement = input._elementRef.nativeElement;
|
|
const val = isRtl ? slider.max - value : value;
|
|
const {x, y} = getCoordsForValue(slider, value);
|
|
|
|
dispatchPointerEvent(inputElement, 'pointerdown', x, y);
|
|
input.value = val;
|
|
dispatchEvent(input._hostElement, new Event('input'));
|
|
input.focus();
|
|
dispatchPointerEvent(inputElement, 'pointerup', x, y);
|
|
dispatchEvent(input._hostElement, new Event('change'));
|
|
flush();
|
|
}
|
|
|
|
/** Slides the MatSlider's thumb to the given value. */
|
|
function slideToValue(slider: MatSlider, input: MatSliderThumb, value: number) {
|
|
const sliderElement = slider._elementRef.nativeElement;
|
|
const {x: startX, y: startY} = getCoordsForValue(slider, input.value);
|
|
const {x: endX, y: endY} = getCoordsForValue(slider, value);
|
|
|
|
dispatchPointerEvent(sliderElement, 'pointerdown', startX, startY);
|
|
input.focus();
|
|
dispatchPointerEvent(sliderElement, 'pointermove', endX, endY);
|
|
input._hostElement.value = `${value}`;
|
|
dispatchEvent(input._hostElement, new Event('input'));
|
|
dispatchPointerEvent(sliderElement, 'pointerup', endX, endY);
|
|
dispatchEvent(input._hostElement, new Event('change'));
|
|
tick(10);
|
|
}
|
|
|
|
/** Returns the x and y coordinates for the given slider value. */
|
|
function getCoordsForValue(slider: MatSlider, value: number): Point {
|
|
const {min, max} = slider;
|
|
const percent = (value - min) / (max - min);
|
|
|
|
const {top, left, width, height} = slider._elementRef.nativeElement.getBoundingClientRect();
|
|
const x = width * percent + left;
|
|
const y = top + height / 2;
|
|
|
|
return {x, y};
|
|
}
|