diff --git a/src/material/slider/slider.spec.ts b/src/material/slider/slider.spec.ts
index cb064373776b..f0e93d2aac66 100644
--- a/src/material/slider/slider.spec.ts
+++ b/src/material/slider/slider.spec.ts
@@ -507,6 +507,27 @@ describe('MatSlider', () => {
expect(sliderInstance.value).toBe(0.3);
});
+ it('should set the truncated value to the aria-valuetext', () => {
+ fixture.componentInstance.step = 0.1;
+ fixture.detectChanges();
+
+ dispatchSlideEventSequence(sliderNativeElement, 0, 0.333333);
+ fixture.detectChanges();
+
+ expect(sliderNativeElement.getAttribute('aria-valuetext')).toBe('33');
+ });
+
+ it('should be able to override the aria-valuetext', () => {
+ fixture.componentInstance.step = 0.1;
+ fixture.componentInstance.ariaValuetext = 'custom';
+ fixture.detectChanges();
+
+ dispatchSlideEventSequence(sliderNativeElement, 0, 0.333333);
+ fixture.detectChanges();
+
+ expect(sliderNativeElement.getAttribute('aria-valuetext')).toBe('custom');
+ });
+
});
describe('slider with auto ticks', () => {
@@ -1494,11 +1515,12 @@ class SliderWithMinAndMax {
class SliderWithValue { }
@Component({
- template: ``,
+ template: ``,
styles: [styles],
})
class SliderWithStep {
step = 25;
+ ariaValuetext: string;
}
@Component({
diff --git a/src/material/slider/slider.ts b/src/material/slider/slider.ts
index d06c9b77241c..3501864aff1a 100644
--- a/src/material/slider/slider.ts
+++ b/src/material/slider/slider.ts
@@ -134,6 +134,13 @@ const _MatSliderMixinBase:
'[attr.aria-valuemax]': 'max',
'[attr.aria-valuemin]': 'min',
'[attr.aria-valuenow]': 'value',
+
+ // NVDA and Jaws appear to announce the `aria-valuenow` by calculating its percentage based
+ // on its value between `aria-valuemin` and `aria-valuemax`. Due to how decimals are handled,
+ // it can cause the slider to read out a very long value like 0.20000068 if the current value
+ // is 0.2 with a min of 0 and max of 1. We work around the issue by setting `aria-valuetext`
+ // to the same value that we set on the slider's thumb which will be truncated.
+ '[attr.aria-valuetext]': 'ariaValuetext || displayValue',
'[attr.aria-orientation]': 'vertical ? "vertical" : "horizontal"',
'[class.mat-slider-disabled]': 'disabled',
'[class.mat-slider-has-ticks]': 'tickInterval',
@@ -268,6 +275,9 @@ export class MatSlider extends _MatSliderMixinBase
*/
@Input() displayWith: (value: number) => string | number;
+ /** Text corresponding to the slider's value. */
+ @Input('aria-valuetext') ariaValuetext: string;
+
/** Whether the slider is vertical. */
@Input()
get vertical(): boolean { return this._vertical; }
diff --git a/tools/public_api_guard/material/slider.d.ts b/tools/public_api_guard/material/slider.d.ts
index a5beb01f501f..2a895d4a2288 100644
--- a/tools/public_api_guard/material/slider.d.ts
+++ b/tools/public_api_guard/material/slider.d.ts
@@ -5,6 +5,7 @@ export declare class MatSlider extends _MatSliderMixinBase implements ControlVal
protected _document: Document;
_isActive: boolean;
_isSliding: boolean;
+ ariaValuetext: string;
readonly change: EventEmitter;
get displayValue(): string | number;
displayWith: (value: number) => string | number;
@@ -71,7 +72,7 @@ export declare class MatSlider extends _MatSliderMixinBase implements ControlVal
static ngAcceptInputType_tickInterval: NumberInput;
static ngAcceptInputType_value: NumberInput;
static ngAcceptInputType_vertical: BooleanInput;
- static ɵcmp: i0.ɵɵComponentDefWithMeta;
+ static ɵcmp: i0.ɵɵComponentDefWithMeta;
static ɵfac: i0.ɵɵFactoryDef;
}