Skip to content

Commit

Permalink
fix(material/slider): some screen readers announcing long decimal val…
Browse files Browse the repository at this point in the history
…ues (#20760)

It looks like some screen readers announce the value of a slider by calculating the
percentage themselves using the `aria-valuemin`, `aria-valuemax` and `aria-valuenow`.
The problem is that they don't round down the decimals so for a slider between 0 and 1
with a step of 0.1, they end up reading out values like 0.20000068. These changes work
around the issue by setting `aria-valuetext` to the same value that we shown in the thumb
which we truncate ourselves.

Fixes #20719.

(cherry picked from commit 19f5f5c)
  • Loading branch information
crisbeto authored and jelbourn committed Oct 20, 2020
1 parent 9ea3d01 commit 2226577
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 2 deletions.
24 changes: 23 additions & 1 deletion src/material/slider/slider.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand Down Expand Up @@ -1494,11 +1515,12 @@ class SliderWithMinAndMax {
class SliderWithValue { }

@Component({
template: `<mat-slider [step]="step"></mat-slider>`,
template: `<mat-slider [step]="step" [aria-valuetext]="ariaValuetext"></mat-slider>`,
styles: [styles],
})
class SliderWithStep {
step = 25;
ariaValuetext: string;
}

@Component({
Expand Down
10 changes: 10 additions & 0 deletions src/material/slider/slider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -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; }
Expand Down
3 changes: 2 additions & 1 deletion tools/public_api_guard/material/slider.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export declare class MatSlider extends _MatSliderMixinBase implements ControlVal
protected _document: Document;
_isActive: boolean;
_isSliding: boolean;
ariaValuetext: string;
readonly change: EventEmitter<MatSliderChange>;
get displayValue(): string | number;
displayWith: (value: number) => string | number;
Expand Down Expand Up @@ -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<MatSlider, "mat-slider", ["matSlider"], { "disabled": "disabled"; "color": "color"; "tabIndex": "tabIndex"; "invert": "invert"; "max": "max"; "min": "min"; "step": "step"; "thumbLabel": "thumbLabel"; "tickInterval": "tickInterval"; "value": "value"; "displayWith": "displayWith"; "vertical": "vertical"; }, { "change": "change"; "input": "input"; "valueChange": "valueChange"; }, never, never>;
static ɵcmp: i0.ɵɵComponentDefWithMeta<MatSlider, "mat-slider", ["matSlider"], { "disabled": "disabled"; "color": "color"; "tabIndex": "tabIndex"; "invert": "invert"; "max": "max"; "min": "min"; "step": "step"; "thumbLabel": "thumbLabel"; "tickInterval": "tickInterval"; "value": "value"; "displayWith": "displayWith"; "ariaValuetext": "aria-valuetext"; "vertical": "vertical"; }, { "change": "change"; "input": "input"; "valueChange": "valueChange"; }, never, never>;
static ɵfac: i0.ɵɵFactoryDef<MatSlider, [null, null, null, { optional: true; }, { attribute: "tabindex"; }, null, null, { optional: true; }]>;
}

Expand Down

0 comments on commit 2226577

Please sign in to comment.