Skip to content

Commit c3d9672

Browse files
committed
fix(selection-list): tabIndex should respect disabled state
* Currently if the selection-list is disabled, the tabIndex may be still set to a valid value that allows tabbing to the element. The `mixinTabIndex` respects the disabled state of the selection list.
1 parent 53c42a4 commit c3d9672

File tree

2 files changed

+68
-13
lines changed

2 files changed

+68
-13
lines changed

src/lib/list/selection-list.spec.ts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,49 @@ describe('MatSelectionList', () => {
219219
});
220220
});
221221

222+
describe('with tabindex', () => {
223+
224+
beforeEach(async(() => {
225+
TestBed.configureTestingModule({
226+
imports: [MatListModule],
227+
declarations: [
228+
SelectionListWithTabindexAttr,
229+
SelectionListWithTabindexBinding,
230+
]
231+
});
232+
233+
TestBed.compileComponents();
234+
}));
235+
236+
it('should properly handle native tabindex attribute', () => {
237+
const fixture = TestBed.createComponent(SelectionListWithTabindexAttr);
238+
const selectionList = fixture.debugElement.query(By.directive(MatSelectionList));
239+
240+
expect(selectionList.componentInstance.tabIndex)
241+
.toBe(5, 'Expected the selection-list tabindex to be set to the attribute value.');
242+
});
243+
244+
it('should support changing the tabIndex through binding', () => {
245+
const fixture = TestBed.createComponent(SelectionListWithTabindexBinding);
246+
const selectionList = fixture.debugElement.query(By.directive(MatSelectionList));
247+
248+
expect(selectionList.componentInstance.tabIndex)
249+
.toBe(0, 'Expected the tabIndex to be set to "0" by default.');
250+
251+
fixture.componentInstance.tabIndex = 3;
252+
fixture.detectChanges();
253+
254+
expect(selectionList.componentInstance.tabIndex)
255+
.toBe(3, 'Expected the tabIndex to updated through binding.');
256+
257+
fixture.componentInstance.disabled = true;
258+
fixture.detectChanges();
259+
260+
expect(selectionList.componentInstance.tabIndex)
261+
.toBe(-1, 'Expected the tabIndex to be set to "-1" if selection list is disabled.');
262+
});
263+
});
264+
222265
describe('with single option', () => {
223266
let fixture: ComponentFixture<SelectionListWithOnlyOneOption>;
224267
let listOption: DebugElement;
@@ -464,3 +507,16 @@ class SelectionListWithDisabledOption {
464507
</mat-selection-list>`})
465508
class SelectionListWithOnlyOneOption {
466509
}
510+
511+
@Component({
512+
template: `<mat-selection-list tabindex="5"></mat-selection-list>`
513+
})
514+
class SelectionListWithTabindexAttr {}
515+
516+
@Component({
517+
template: `<mat-selection-list [tabIndex]="tabIndex" [disabled]="disabled"></mat-selection-list>`
518+
})
519+
class SelectionListWithTabindexBinding {
520+
tabIndex: number;
521+
disabled: boolean;
522+
}

src/lib/list/selection-list.ts

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -28,22 +28,26 @@ import {
2828
QueryList,
2929
Renderer2,
3030
ViewEncapsulation,
31+
Attribute,
3132
} from '@angular/core';
3233
import {
3334
CanDisable,
3435
CanDisableRipple,
3536
MatLine,
3637
MatLineSetter,
38+
HasTabIndex,
3739
mixinDisabled,
3840
mixinDisableRipple,
41+
mixinTabIndex,
3942
} from '@angular/material/core';
4043
import {merge} from 'rxjs/observable/merge';
4144
import {Subscription} from 'rxjs/Subscription';
4245

4346

4447
/** @docs-private */
4548
export class MatSelectionListBase {}
46-
export const _MatSelectionListMixinBase = mixinDisableRipple(mixinDisabled(MatSelectionListBase));
49+
export const _MatSelectionListMixinBase =
50+
mixinTabIndex(mixinDisableRipple(mixinDisabled(MatSelectionListBase)));
4751

4852
/** @docs-private */
4953
export class MatListOptionBase {}
@@ -187,10 +191,10 @@ export class MatListOption extends _MatListOptionMixinBase
187191
@Component({
188192
moduleId: module.id,
189193
selector: 'mat-selection-list',
190-
inputs: ['disabled', 'disableRipple'],
194+
inputs: ['disabled', 'disableRipple', 'tabIndex'],
191195
host: {
192196
'role': 'listbox',
193-
'[attr.tabindex]': '_tabIndex',
197+
'[tabIndex]': 'tabIndex',
194198
'class': 'mat-selection-list',
195199
'(focus)': 'focus()',
196200
'(keydown)': '_keydown($event)',
@@ -201,11 +205,8 @@ export class MatListOption extends _MatListOptionMixinBase
201205
preserveWhitespaces: false,
202206
changeDetection: ChangeDetectionStrategy.OnPush
203207
})
204-
export class MatSelectionList extends _MatSelectionListMixinBase
205-
implements FocusableOption, CanDisable, CanDisableRipple, AfterContentInit, OnDestroy {
206-
207-
/** Tab index for the selection-list. */
208-
_tabIndex = 0;
208+
export class MatSelectionList extends _MatSelectionListMixinBase implements FocusableOption,
209+
CanDisable, CanDisableRipple, HasTabIndex, AfterContentInit, OnDestroy {
209210

210211
/** Subscription to all list options' onFocus events */
211212
private _optionFocusSubscription = Subscription.EMPTY;
@@ -222,17 +223,15 @@ export class MatSelectionList extends _MatSelectionListMixinBase
222223
/** The currently selected options. */
223224
selectedOptions: SelectionModel<MatListOption> = new SelectionModel<MatListOption>(true);
224225

225-
constructor(private _element: ElementRef) {
226+
constructor(private _element: ElementRef, @Attribute('tabindex') tabIndex: string) {
226227
super();
228+
229+
this.tabIndex = parseInt(tabIndex) || 0;
227230
}
228231

229232
ngAfterContentInit(): void {
230233
this._keyManager = new FocusKeyManager<MatListOption>(this.options).withWrap();
231234

232-
if (this.disabled) {
233-
this._tabIndex = -1;
234-
}
235-
236235
this._optionFocusSubscription = this._onFocusSubscription();
237236
this._optionDestroyStream = this._onDestroySubscription();
238237
}

0 commit comments

Comments
 (0)