From 038a337967c34ad1fe8abda523243f65bb001aea Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Tue, 28 Mar 2017 01:39:21 +0200 Subject: [PATCH] fix(select): allow custom aria-label (#3765) * fix(select): allow custom aria-label * Adds support for custom `aria-label` attributes on the select host element. * Also fixes that the `aria-labelledby` attribute is overwritten by the placeholder `aria-label`. Fixes #3762. * Fix typos --- src/lib/select/select.spec.ts | 19 ++++++++++++++++++- src/lib/select/select.ts | 16 +++++++++++++++- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/lib/select/select.spec.ts b/src/lib/select/select.spec.ts index df1fb46f739a..06b76d43c858 100644 --- a/src/lib/select/select.spec.ts +++ b/src/lib/select/select.spec.ts @@ -1085,6 +1085,21 @@ describe('MdSelect', () => { expect(select.getAttribute('aria-label')).toEqual('Food'); }); + it('should support setting a custom aria-label', () => { + fixture.componentInstance.ariaLabel = 'Custom Label'; + fixture.detectChanges(); + + expect(select.getAttribute('aria-label')).toEqual('Custom Label'); + }); + + it('should not set an aria-label if aria-labelledby is specified', () => { + fixture.componentInstance.ariaLabelledby = 'myLabelId'; + fixture.detectChanges(); + + expect(select.getAttribute('aria-label')).toBeFalsy('Expected no aria-label to be set.'); + expect(select.getAttribute('aria-labelledby')).toBe('myLabelId'); + }); + it('should set the tabindex of the select to 0 by default', () => { expect(select.getAttribute('tabindex')).toEqual('0'); }); @@ -1606,7 +1621,7 @@ describe('MdSelect', () => { template: `
+ [tabIndex]="tabIndexOverride" [aria-label]="ariaLabel" [aria-labelledby]="ariaLabelledby"> {{ food.viewValue }} @@ -1630,6 +1645,8 @@ class BasicSelect { heightAbove = 0; heightBelow = 0; tabIndexOverride: number; + ariaLabel: string; + ariaLabelledby: string; @ViewChild(MdSelect) select: MdSelect; @ViewChildren(MdOption) options: QueryList; diff --git a/src/lib/select/select.ts b/src/lib/select/select.ts index c220082e5db2..4a1afaa0c409 100644 --- a/src/lib/select/select.ts +++ b/src/lib/select/select.ts @@ -101,7 +101,8 @@ export type MdSelectFloatPlaceholderType = 'always' | 'never' | 'auto'; host: { 'role': 'listbox', '[attr.tabindex]': 'tabIndex', - '[attr.aria-label]': 'placeholder', + '[attr.aria-label]': '_ariaLabel', + '[attr.aria-labelledby]': 'ariaLabelledby', '[attr.aria-required]': 'required.toString()', '[attr.aria-disabled]': 'disabled.toString()', '[attr.aria-invalid]': '_control?.invalid || "false"', @@ -279,6 +280,12 @@ export class MdSelect implements AfterContentInit, ControlValueAccessor, OnDestr } } + /** Aria label of the select. If not specified, the placeholder will be used as label. */ + @Input('aria-label') ariaLabel: string = ''; + + /** Input that can be used to specify the `aria-labelledby` attribute. */ + @Input('aria-labelledby') ariaLabelledby: string = ''; + /** Combined stream of all of the child options' change events. */ get optionSelectionChanges(): Observable { return Observable.merge(...this.options.map(option => option.onSelectionChange)); @@ -747,6 +754,13 @@ export class MdSelect implements AfterContentInit, ControlValueAccessor, OnDestr 'visible' : 'hidden'; } + /** Returns the aria-label of the select component. */ + get _ariaLabel(): string { + // If an ariaLabelledby value has been set, the select should not overwrite the + // `aria-labelledby` value by setting the ariaLabel to the placeholder. + return this.ariaLabelledby ? null : this.ariaLabel || this.placeholder; + } + /** * Calculates the y-offset of the select's overlay panel in relation to the * top start corner of the trigger. It has to be adjusted in order for the