diff --git a/src/dev-app/slider/slider-demo.html b/src/dev-app/slider/slider-demo.html index 8fbaa3ddc8cd..883552c781f8 100644 --- a/src/dev-app/slider/slider-demo.html +++ b/src/dev-app/slider/slider-demo.html @@ -181,6 +181,19 @@ + +

Five-digit value

+ + + + +

Year range

+ + + + +
+
diff --git a/src/material/slider/_m3-slider.scss b/src/material/slider/_m3-slider.scss index c3e4908fb560..094d96a072bd 100644 --- a/src/material/slider/_m3-slider.scss +++ b/src/material/slider/_m3-slider.scss @@ -20,13 +20,13 @@ slider-inactive-track-shape: map.get($system, corner-full), slider-with-tick-marks-container-shape: map.get($system, corner-full), slider-value-indicator-opacity: 1, - slider-value-indicator-padding: 0, - slider-value-indicator-width: $indicator-size, + slider-value-indicator-padding: 0 12px, + slider-value-indicator-width: auto, slider-value-indicator-height: $indicator-size, slider-value-indicator-caret-display: none, - slider-value-indicator-border-radius: 50% 50% 50% 0, - slider-value-indicator-text-transform: rotate(45deg), - slider-value-indicator-container-transform: translateX(-50%) rotate(-45deg), + slider-value-indicator-border-radius: map.get($system, corner-full), + slider-value-indicator-text-transform: none, + slider-value-indicator-container-transform: translateX(-50%), slider-active-track-height: 4px, slider-handle-height: 20px, slider-handle-width: 20px, @@ -35,7 +35,7 @@ slider-with-tick-marks-active-container-opacity: 0.38, slider-with-tick-marks-container-size: 2px, slider-with-tick-marks-inactive-container-opacity: 0.38, - slider-value-indicator-transform-origin: 0 $indicator-size, + slider-value-indicator-transform-origin: bottom, ), color: ( slider-active-track-color: map.get($system, primary), diff --git a/src/material/slider/slider.scss b/src/material/slider/slider.scss index 952f722af0c2..c2b73b52adf1 100644 --- a/src/material/slider/slider.scss +++ b/src/material/slider/slider.scss @@ -160,6 +160,7 @@ $fallbacks: m3-slider.get-tokens(); .mdc-slider__value-indicator-text { text-align: center; + white-space: nowrap; width: token-utils.slot(slider-value-indicator-width, $fallbacks); transform: token-utils.slot(slider-value-indicator-text-transform, $fallbacks); font-family: token-utils.slot(slider-label-label-text-font, $fallbacks); diff --git a/src/material/slider/slider.spec.ts b/src/material/slider/slider.spec.ts index a22860c4ae78..6caeb0964ce1 100644 --- a/src/material/slider/slider.spec.ts +++ b/src/material/slider/slider.spec.ts @@ -1044,6 +1044,78 @@ describe('MatSlider', () => { }); }); + describe('long value indicators', () => { + for (const range of [false, true]) { + for (const direction of ['ltr', 'rtl'] as const) { + it(`should fit and center long labels (range: ${range}, direction: ${direction})`, () => { + const component = range + ? DiscreteRangeSliderWithDisplayWith + : DiscreteSliderWithDisplayWith; + const fixture = createComponent(component, [provideFakeDirectionality(direction)]); + fixture.nativeElement.dir = direction; + fixture.componentInstance.displayWith = () => '1'; + fixture.detectChanges(); + const slider: MatSlider = fixture.debugElement.query( + By.directive(MatSlider), + ).componentInstance; + + const thumbs: HTMLElement[] = Array.from( + fixture.nativeElement.querySelectorAll('.mdc-slider__thumb'), + ); + + for (const thumb of thumbs) { + thumb.classList.add('mdc-slider__thumb--with-indicator'); + // Measure the final layout without waiting for the entrance animation. + thumb.querySelector('.mdc-slider__value-indicator')!.style.transition = + 'none'; + } + + const initialRect = thumbs[0] + .querySelector('.mdc-slider__value-indicator')! + .getBoundingClientRect(); + + for (const label of ['2026', '10000', 'September 2026', '1']) { + fixture.componentInstance.displayWith = () => label; + fixture.changeDetectorRef.markForCheck(); + fixture.detectChanges(); + for (const position of [_MatThumb.START, _MatThumb.END]) { + const input = slider._getInput(position) as MatSliderThumb | undefined; + if (input) { + input.value = input.value === 200 ? 199 : input.value + 1; + } + } + fixture.detectChanges(); + + for (const thumb of thumbs) { + const indicator = thumb.querySelector('.mdc-slider__value-indicator')!; + const text = thumb.querySelector('.mdc-slider__value-indicator-text')!; + const textRange = document.createRange(); + textRange.selectNodeContents(text); + + const indicatorRect = indicator.getBoundingClientRect(); + const textRect = textRange.getBoundingClientRect(); + const thumbRect = thumb.getBoundingClientRect(); + + expect(text.textContent).toBe(label); + expect(textRect.left).withContext(label).toBeGreaterThan(indicatorRect.left); + expect(textRect.right).withContext(label).toBeLessThan(indicatorRect.right); + expect(textRect.height).withContext(label).toBeLessThan(indicatorRect.height); + expect(indicatorRect.height).withContext(label).toBeCloseTo(initialRect.height, 0); + if (label === '1') { + expect(indicatorRect.width).toBeCloseTo(initialRect.width, 0); + } else { + expect(indicatorRect.width).withContext(label).toBeGreaterThan(initialRect.width); + } + expect(indicatorRect.left + indicatorRect.width / 2) + .withContext(label) + .toBeCloseTo(thumbRect.left + thumbRect.width / 2, 0); + } + } + }); + } + } + }); + describe('slider with value property binding', () => { let fixture: ComponentFixture; let input: MatSliderThumb;