From 0c33c89aca97fd3885993b89a0ef6f2f8f6675f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20M=C3=A4dler?= Date: Thu, 2 Jun 2016 22:55:42 +0200 Subject: [PATCH] feat(radio): support aria-label(ledby) on md-radio (#586) --- src/components/radio/README.md | 5 ++- src/components/radio/radio.html | 2 ++ src/components/radio/radio.spec.ts | 55 ++++++++++++++++++++++++++++++ src/components/radio/radio.ts | 6 ++++ src/demo-app/radio/radio-demo.scss | 2 +- 5 files changed, 68 insertions(+), 2 deletions(-) diff --git a/src/components/radio/README.md b/src/components/radio/README.md index f30a783dcb98..7c30a1f812ac 100644 --- a/src/components/radio/README.md +++ b/src/components/radio/README.md @@ -47,13 +47,16 @@ The `md-radio-group` component has no button initially selected. ## `` ### Properties -| Name | Type | Description | +| Name (attribute) | Type | Description | | --- | --- | --- | | `id` | `string` | The unique ID of this radio button. | | `name` | `string` | Group name, defaults to parent radio group if present. | | `value` | `any` | The value of this radio button. | | `checked` | `boolean` | Whether the radio is checked. | | `disabled` | `boolean` | Whether the radio is disabled. | +| `aria-label` | `string` | Used to set the `aria-label` attribute of the underlying input element. | +| `aria-labelledby` | `string` | Used to set the `aria-labelledby` attribute of the underlying input element. + If provided, this attribute takes precedence as the element's text alternative. | When checked, an event is emitted from the `change` EventEmitter property. diff --git a/src/components/radio/radio.html b/src/components/radio/radio.html index e094b07f4842..249e35f67b43 100644 --- a/src/components/radio/radio.html +++ b/src/components/radio/radio.html @@ -13,6 +13,8 @@ [checked]="checked" [disabled]="disabled" [name]="name" + [attr.aria-label]="ariaLabel" + [attr.aria-labelledby]="ariaLabelledby" (change)="onInputChange($event)" (focus)="onInputFocus()" (blur)="onInputBlur()" /> diff --git a/src/components/radio/radio.spec.ts b/src/components/radio/radio.spec.ts index 6f5a8c1a6de4..565769a2cce6 100644 --- a/src/components/radio/radio.spec.ts +++ b/src/components/radio/radio.spec.ts @@ -345,10 +345,14 @@ describe('MdRadio', () => { let radioDebugElements: DebugElement[]; let seasonRadioInstances: MdRadioButton[]; let weatherRadioInstances: MdRadioButton[]; + let fruitRadioInstances: MdRadioButton[]; + let fruitRadioNativeInputs: HTMLElement[]; let testComponent: StandaloneRadioButtons; beforeEach(async(() => { builder.createAsync(StandaloneRadioButtons).then(f => { + let fruitRadioNativeElements: HTMLElement[]; + fixture = f; fixture.detectChanges(); @@ -361,6 +365,18 @@ describe('MdRadio', () => { weatherRadioInstances = radioDebugElements .filter(debugEl => debugEl.componentInstance.name == 'weather') .map(debugEl => debugEl.componentInstance); + fruitRadioInstances = radioDebugElements + .filter(debugEl => debugEl.componentInstance.name == 'fruit') + .map(debugEl => debugEl.componentInstance); + + fruitRadioNativeElements = radioDebugElements + .filter(debugEl => debugEl.componentInstance.name == 'fruit') + .map(debugEl => debugEl.nativeElement); + + fruitRadioNativeInputs = []; + for (let element of fruitRadioNativeElements) { + fruitRadioNativeInputs.push( element.querySelector('input')); + } }); })); @@ -393,6 +409,40 @@ describe('MdRadio', () => { expect(weatherRadioInstances[1].checked).toBe(false); expect(weatherRadioInstances[2].checked).toBe(true); }); + + it('should add aria-label attribute to the underlying input element if defined', () => { + expect(fruitRadioNativeInputs[0].getAttribute('aria-label')).toBe('Banana'); + }); + + it('should not add aria-label attribute if not defined', () => { + expect(fruitRadioNativeInputs[1].hasAttribute('aria-label')).toBeFalsy(); + }); + + it('should change aria-label attribute if property is changed at runtime', () => { + expect(fruitRadioNativeInputs[0].getAttribute('aria-label')).toBe('Banana'); + + fruitRadioInstances[0].ariaLabel = 'Pineapple'; + fixture.detectChanges(); + + expect(fruitRadioNativeInputs[0].getAttribute('aria-label')).toBe('Pineapple'); + }); + + it('should add aria-labelledby attribute to the underlying input element if defined', () => { + expect(fruitRadioNativeInputs[0].getAttribute('aria-labelledby')).toBe('xyz'); + }); + + it('should not add aria-labelledby attribute if not defined', () => { + expect(fruitRadioNativeInputs[1].hasAttribute('aria-labelledby')).toBeFalsy(); + }); + + it('should change aria-labelledby attribute if property is changed at runtime', () => { + expect(fruitRadioNativeInputs[0].getAttribute('aria-labelledby')).toBe('xyz'); + + fruitRadioInstances[0].ariaLabelledby = 'uvw'; + fixture.detectChanges(); + + expect(fruitRadioNativeInputs[0].getAttribute('aria-labelledby')).toBe('uvw'); + }); }); }); @@ -423,6 +473,11 @@ class RadiosInsideRadioGroup { Spring Summer Autumn + + Baby Banana + + + Raspberry ` }) class StandaloneRadioButtons { } diff --git a/src/components/radio/radio.ts b/src/components/radio/radio.ts index f4028ad8db40..57ca45bfb5bb 100644 --- a/src/components/radio/radio.ts +++ b/src/components/radio/radio.ts @@ -254,6 +254,12 @@ export class MdRadioButton implements OnInit { @Input() name: string; + /** Used to set the 'aria-label' attribute on the underlying input element. */ + @Input('aria-label') ariaLabel: string; + + /** The 'aria-labelledby' attribute takes precedence as the element's text alternative. */ + @Input('aria-labelledby') ariaLabelledby: string; + /** Whether this radio is disabled. */ private _disabled: boolean; diff --git a/src/demo-app/radio/radio-demo.scss b/src/demo-app/radio/radio-demo.scss index 16163c533357..af41a95719cd 100644 --- a/src/demo-app/radio/radio-demo.scss +++ b/src/demo-app/radio/radio-demo.scss @@ -11,4 +11,4 @@ md-radio-button { margin: 8px; } -} \ No newline at end of file +}