From 934be6f3833ba2843a8063735cb5a57c9fa6bd85 Mon Sep 17 00:00:00 2001 From: crisbeto Date: Tue, 7 Mar 2017 18:03:30 +0100 Subject: [PATCH 1/2] fix(select): unable to set a tabindex Fixes users not being able to override the `tabIndex` on `md-select`. Fixes #3474. --- src/lib/select/select.spec.ts | 13 +++++++++++-- src/lib/select/select.ts | 20 +++++++++++++------- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/src/lib/select/select.spec.ts b/src/lib/select/select.spec.ts index aceba1735fbf..f2ada0a7fbf9 100644 --- a/src/lib/select/select.spec.ts +++ b/src/lib/select/select.spec.ts @@ -1081,10 +1081,17 @@ describe('MdSelect', () => { expect(select.getAttribute('aria-label')).toEqual('Food'); }); - it('should set the tabindex of the select to 0', () => { + it('should set the tabindex of the select to 0 by default', () => { expect(select.getAttribute('tabindex')).toEqual('0'); }); + it('should be able to override the tabindex', () => { + fixture.componentInstance.tabIndexOverride = 3; + fixture.detectChanges(); + + expect(select.getAttribute('tabindex')).toBe('3'); + }); + it('should set aria-required for required selects', () => { expect(select.getAttribute('aria-required')) .toEqual('false', `Expected aria-required attr to be false for normal selects.`); @@ -1583,7 +1590,8 @@ describe('MdSelect', () => { selector: 'basic-select', template: `
- + {{ food.viewValue }} @@ -1606,6 +1614,7 @@ class BasicSelect { isRequired: boolean; heightAbove = 0; heightBelow = 0; + tabIndexOverride: number; @ViewChild(MdSelect) select: MdSelect; @ViewChildren(MdOption) options: QueryList; diff --git a/src/lib/select/select.ts b/src/lib/select/select.ts index c0eb143bd086..b861ca65eb1c 100644 --- a/src/lib/select/select.ts +++ b/src/lib/select/select.ts @@ -99,7 +99,7 @@ export type MdSelectFloatPlaceholderType = 'always' | 'never' | 'auto'; encapsulation: ViewEncapsulation.None, host: { 'role': 'listbox', - '[attr.tabindex]': '_getTabIndex()', + '[attr.tabindex]': 'tabIndex', '[attr.aria-label]': 'placeholder', '[attr.aria-required]': 'required.toString()', '[attr.aria-disabled]': 'disabled.toString()', @@ -151,6 +151,9 @@ export class MdSelect implements AfterContentInit, ControlValueAccessor, OnDestr /** The animation state of the placeholder. */ private _placeholderState = ''; + /** Tab index for the element. */ + private _tabIndex: number = 0; + /** * The width of the trigger. Must be saved to set the min width of the overlay panel * and the width of the selected value. @@ -266,6 +269,15 @@ export class MdSelect implements AfterContentInit, ControlValueAccessor, OnDestr } private _floatPlaceholder: MdSelectFloatPlaceholderType = 'auto'; + /** Tab index for the select element. */ + @Input() + get tabIndex(): number { return this._disabled ? -1 : this._tabIndex; } + set tabIndex(value: number) { + if (typeof value !== 'undefined') { + this._tabIndex = value; + } + } + /** Combined stream of all of the child options' change events. */ get optionSelectionChanges(): Observable { return Observable.merge(...this.options.map(option => option.onSelectionChange)); @@ -452,12 +464,6 @@ export class MdSelect implements AfterContentInit, ControlValueAccessor, OnDestr } } - /** Returns the correct tabindex for the select depending on disabled state. */ - _getTabIndex() { - return this.disabled ? '-1' : '0'; - } - - /** * Sets the scroll position of the scroll container. This must be called after * the overlay pane is attached or the scroll container element will not yet be From 94be4677d5baeb8f945947a28d9ee21b693dcbc5 Mon Sep 17 00:00:00 2001 From: crisbeto Date: Wed, 8 Mar 2017 19:49:44 +0100 Subject: [PATCH 2/2] fix: allow use of `tabindex` --- src/lib/select/select.spec.ts | 22 +++++++++++++++++++++- src/lib/select/select.ts | 8 ++++++-- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/lib/select/select.spec.ts b/src/lib/select/select.spec.ts index f2ada0a7fbf9..a428799717e9 100644 --- a/src/lib/select/select.spec.ts +++ b/src/lib/select/select.spec.ts @@ -42,7 +42,8 @@ describe('MdSelect', () => { SelectWithErrorSibling, ThrowsErrorOnInit, BasicSelectOnPush, - BasicSelectOnPushPreselected + BasicSelectOnPushPreselected, + SelectWithPlainTabindex ], providers: [ {provide: OverlayContainer, useFactory: () => { @@ -1092,6 +1093,17 @@ describe('MdSelect', () => { expect(select.getAttribute('tabindex')).toBe('3'); }); + it('should be able to set the tabindex via the native attribute', () => { + fixture.destroy(); + + const plainTabindexFixture = TestBed.createComponent(SelectWithPlainTabindex); + + plainTabindexFixture.detectChanges(); + select = plainTabindexFixture.debugElement.query(By.css('md-select')).nativeElement; + + expect(select.getAttribute('tabindex')).toBe('5'); + }); + it('should set aria-required for required selects', () => { expect(select.getAttribute('aria-required')) .toEqual('false', `Expected aria-required attr to be false for normal selects.`); @@ -1873,6 +1885,14 @@ class MultiSelect { @ViewChildren(MdOption) options: QueryList; } +@Component({ + selector: 'select-with-plain-tabindex', + template: ` + + ` +}) +class SelectWithPlainTabindex { } + class FakeViewportRuler { getViewportRect() { diff --git a/src/lib/select/select.ts b/src/lib/select/select.ts index b861ca65eb1c..32915f501b2c 100644 --- a/src/lib/select/select.ts +++ b/src/lib/select/select.ts @@ -14,6 +14,7 @@ import { ViewEncapsulation, ViewChild, ChangeDetectorRef, + Attribute, } from '@angular/core'; import {MdOption, MdOptionSelectionChange} from '../core/option/option'; import {ENTER, SPACE} from '../core/keyboard/keycodes'; @@ -152,7 +153,7 @@ export class MdSelect implements AfterContentInit, ControlValueAccessor, OnDestr private _placeholderState = ''; /** Tab index for the element. */ - private _tabIndex: number = 0; + private _tabIndex: number; /** * The width of the trigger. Must be saved to set the min width of the overlay panel @@ -294,10 +295,13 @@ export class MdSelect implements AfterContentInit, ControlValueAccessor, OnDestr constructor(private _element: ElementRef, private _renderer: Renderer, private _viewportRuler: ViewportRuler, private _changeDetectorRef: ChangeDetectorRef, - @Optional() private _dir: Dir, @Self() @Optional() public _control: NgControl) { + @Optional() private _dir: Dir, @Self() @Optional() public _control: NgControl, + @Attribute('tabindex') tabIndex: string) { if (this._control) { this._control.valueAccessor = this; } + + this._tabIndex = parseInt(tabIndex) || 0; } ngAfterContentInit() {