From df0bfdcc7b1fa73b0b2ede5630301c1ef01d5ab2 Mon Sep 17 00:00:00 2001 From: Kyle Erf Date: Thu, 8 Feb 2018 16:41:59 -0500 Subject: [PATCH] feat(radio): add aria-describedby passthrough to radio button input (#9741) Adds a simple "aria-describedby" input in the manner of "aria-labelledby." The ability to set "aria-describedby" is essential for accessible radio buttons, as the attribute allows further description of a radio button to be read after mentioning the button's label and whether or not it is checked. --- src/lib/radio/radio.html | 1 + src/lib/radio/radio.spec.ts | 22 +++++++++++++++++++++- src/lib/radio/radio.ts | 3 +++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/lib/radio/radio.html b/src/lib/radio/radio.html index 533a3a0472f..bbcdccb31f9 100644 --- a/src/lib/radio/radio.html +++ b/src/lib/radio/radio.html @@ -23,6 +23,7 @@ [required]="required" [attr.aria-label]="ariaLabel" [attr.aria-labelledby]="ariaLabelledby" + [attr.aria-describedby]="ariaDescribedby" (change)="_onInputChange($event)" (click)="_onInputClick($event)"> diff --git a/src/lib/radio/radio.spec.ts b/src/lib/radio/radio.spec.ts index e25d65c7cbd..b3f7ecf810a 100644 --- a/src/lib/radio/radio.spec.ts +++ b/src/lib/radio/radio.spec.ts @@ -627,6 +627,23 @@ describe('MatRadio', () => { expect(fruitRadioNativeInputs[0].getAttribute('aria-labelledby')).toBe('uvw'); }); + it('should add aria-describedby attribute to the underlying input element if defined', () => { + expect(fruitRadioNativeInputs[0].getAttribute('aria-describedby')).toBe('abc'); + }); + + it('should not add aria-describedby attribute if not defined', () => { + expect(fruitRadioNativeInputs[1].hasAttribute('aria-describedby')).toBeFalsy(); + }); + + it('should change aria-describedby attribute if property is changed at runtime', () => { + expect(fruitRadioNativeInputs[0].getAttribute('aria-describedby')).toBe('abc'); + + testComponent.ariaDescribedby = 'uvw'; + fixture.detectChanges(); + + expect(fruitRadioNativeInputs[0].getAttribute('aria-describedby')).toBe('uvw'); + }); + it('should focus on underlying input element when focus() is called', () => { for (let i = 0; i < fruitRadioInstances.length; i++) { expect(document.activeElement).not.toBe(fruitRadioNativeInputs[i]); @@ -745,10 +762,12 @@ class RadiosInsideRadioGroup { Autumn Baby Banana + A smaller banana + [aria-labelledby]="ariaLabelledby" + [aria-describedby]="ariaDescribedby"> Raspberry No name @@ -757,6 +776,7 @@ class RadiosInsideRadioGroup { class StandaloneRadioButtons { ariaLabel: string = 'Banana'; ariaLabelledby: string = 'xyz'; + ariaDescribedby: string = 'abc'; } diff --git a/src/lib/radio/radio.ts b/src/lib/radio/radio.ts index b9042adee87..71dc9f16266 100644 --- a/src/lib/radio/radio.ts +++ b/src/lib/radio/radio.ts @@ -366,6 +366,9 @@ export class MatRadioButton extends _MatRadioButtonMixinBase /** The 'aria-labelledby' attribute takes precedence as the element's text alternative. */ @Input('aria-labelledby') ariaLabelledby: string; + /** The 'aria-describedby' attribute is read after the element's label and field type. */ + @Input('aria-describedby') ariaDescribedby: string; + /** Whether this radio button is checked. */ @Input() get checked(): boolean { return this._checked; }