Skip to content

Commit 11dec36

Browse files
crisbetotinayuangao
authored andcommitted
fix(select): unable to set a tabindex (#3479)
* fix(select): unable to set a tabindex Fixes users not being able to override the `tabIndex` on `md-select`. Fixes #3474. * fix: allow use of `tabindex`
1 parent fb75a13 commit 11dec36

File tree

2 files changed

+50
-11
lines changed

2 files changed

+50
-11
lines changed

src/lib/select/select.spec.ts

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ describe('MdSelect', () => {
4242
SelectWithErrorSibling,
4343
ThrowsErrorOnInit,
4444
BasicSelectOnPush,
45-
BasicSelectOnPushPreselected
45+
BasicSelectOnPushPreselected,
46+
SelectWithPlainTabindex
4647
],
4748
providers: [
4849
{provide: OverlayContainer, useFactory: () => {
@@ -1081,10 +1082,28 @@ describe('MdSelect', () => {
10811082
expect(select.getAttribute('aria-label')).toEqual('Food');
10821083
});
10831084

1084-
it('should set the tabindex of the select to 0', () => {
1085+
it('should set the tabindex of the select to 0 by default', () => {
10851086
expect(select.getAttribute('tabindex')).toEqual('0');
10861087
});
10871088

1089+
it('should be able to override the tabindex', () => {
1090+
fixture.componentInstance.tabIndexOverride = 3;
1091+
fixture.detectChanges();
1092+
1093+
expect(select.getAttribute('tabindex')).toBe('3');
1094+
});
1095+
1096+
it('should be able to set the tabindex via the native attribute', () => {
1097+
fixture.destroy();
1098+
1099+
const plainTabindexFixture = TestBed.createComponent(SelectWithPlainTabindex);
1100+
1101+
plainTabindexFixture.detectChanges();
1102+
select = plainTabindexFixture.debugElement.query(By.css('md-select')).nativeElement;
1103+
1104+
expect(select.getAttribute('tabindex')).toBe('5');
1105+
});
1106+
10881107
it('should set aria-required for required selects', () => {
10891108
expect(select.getAttribute('aria-required'))
10901109
.toEqual('false', `Expected aria-required attr to be false for normal selects.`);
@@ -1583,7 +1602,8 @@ describe('MdSelect', () => {
15831602
selector: 'basic-select',
15841603
template: `
15851604
<div [style.height.px]="heightAbove"></div>
1586-
<md-select placeholder="Food" [formControl]="control" [required]="isRequired">
1605+
<md-select placeholder="Food" [formControl]="control" [required]="isRequired"
1606+
[tabIndex]="tabIndexOverride">
15871607
<md-option *ngFor="let food of foods" [value]="food.value" [disabled]="food.disabled">
15881608
{{ food.viewValue }}
15891609
</md-option>
@@ -1606,6 +1626,7 @@ class BasicSelect {
16061626
isRequired: boolean;
16071627
heightAbove = 0;
16081628
heightBelow = 0;
1629+
tabIndexOverride: number;
16091630

16101631
@ViewChild(MdSelect) select: MdSelect;
16111632
@ViewChildren(MdOption) options: QueryList<MdOption>;
@@ -1864,6 +1885,14 @@ class MultiSelect {
18641885
@ViewChildren(MdOption) options: QueryList<MdOption>;
18651886
}
18661887

1888+
@Component({
1889+
selector: 'select-with-plain-tabindex',
1890+
template: `
1891+
<md-select tabindex="5"></md-select>
1892+
`
1893+
})
1894+
class SelectWithPlainTabindex { }
1895+
18671896

18681897
class FakeViewportRuler {
18691898
getViewportRect() {

src/lib/select/select.ts

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
ViewEncapsulation,
1515
ViewChild,
1616
ChangeDetectorRef,
17+
Attribute,
1718
} from '@angular/core';
1819
import {MdOption, MdOptionSelectionChange} from '../core/option/option';
1920
import {ENTER, SPACE} from '../core/keyboard/keycodes';
@@ -99,7 +100,7 @@ export type MdSelectFloatPlaceholderType = 'always' | 'never' | 'auto';
99100
encapsulation: ViewEncapsulation.None,
100101
host: {
101102
'role': 'listbox',
102-
'[attr.tabindex]': '_getTabIndex()',
103+
'[attr.tabindex]': 'tabIndex',
103104
'[attr.aria-label]': 'placeholder',
104105
'[attr.aria-required]': 'required.toString()',
105106
'[attr.aria-disabled]': 'disabled.toString()',
@@ -151,6 +152,9 @@ export class MdSelect implements AfterContentInit, ControlValueAccessor, OnDestr
151152
/** The animation state of the placeholder. */
152153
private _placeholderState = '';
153154

155+
/** Tab index for the element. */
156+
private _tabIndex: number;
157+
154158
/**
155159
* The width of the trigger. Must be saved to set the min width of the overlay panel
156160
* and the width of the selected value.
@@ -266,6 +270,15 @@ export class MdSelect implements AfterContentInit, ControlValueAccessor, OnDestr
266270
}
267271
private _floatPlaceholder: MdSelectFloatPlaceholderType = 'auto';
268272

273+
/** Tab index for the select element. */
274+
@Input()
275+
get tabIndex(): number { return this._disabled ? -1 : this._tabIndex; }
276+
set tabIndex(value: number) {
277+
if (typeof value !== 'undefined') {
278+
this._tabIndex = value;
279+
}
280+
}
281+
269282
/** Combined stream of all of the child options' change events. */
270283
get optionSelectionChanges(): Observable<MdOptionSelectionChange> {
271284
return Observable.merge(...this.options.map(option => option.onSelectionChange));
@@ -282,10 +295,13 @@ export class MdSelect implements AfterContentInit, ControlValueAccessor, OnDestr
282295

283296
constructor(private _element: ElementRef, private _renderer: Renderer,
284297
private _viewportRuler: ViewportRuler, private _changeDetectorRef: ChangeDetectorRef,
285-
@Optional() private _dir: Dir, @Self() @Optional() public _control: NgControl) {
298+
@Optional() private _dir: Dir, @Self() @Optional() public _control: NgControl,
299+
@Attribute('tabindex') tabIndex: string) {
286300
if (this._control) {
287301
this._control.valueAccessor = this;
288302
}
303+
304+
this._tabIndex = parseInt(tabIndex) || 0;
289305
}
290306

291307
ngAfterContentInit() {
@@ -452,12 +468,6 @@ export class MdSelect implements AfterContentInit, ControlValueAccessor, OnDestr
452468
}
453469
}
454470

455-
/** Returns the correct tabindex for the select depending on disabled state. */
456-
_getTabIndex() {
457-
return this.disabled ? '-1' : '0';
458-
}
459-
460-
461471
/**
462472
* Sets the scroll position of the scroll container. This must be called after
463473
* the overlay pane is attached or the scroll container element will not yet be

0 commit comments

Comments
 (0)