Skip to content

Commit 86b3151

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 3c6f7a2 commit 86b3151

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
@@ -256,6 +256,49 @@ describe('MatSelectionList', () => {
256256
});
257257
});
258258

259+
describe('with tabindex', () => {
260+
261+
beforeEach(async(() => {
262+
TestBed.configureTestingModule({
263+
imports: [MatListModule],
264+
declarations: [
265+
SelectionListWithTabindexAttr,
266+
SelectionListWithTabindexBinding,
267+
]
268+
});
269+
270+
TestBed.compileComponents();
271+
}));
272+
273+
it('should properly handle native tabindex attribute', () => {
274+
const fixture = TestBed.createComponent(SelectionListWithTabindexAttr);
275+
const selectionList = fixture.debugElement.query(By.directive(MatSelectionList));
276+
277+
expect(selectionList.componentInstance.tabIndex)
278+
.toBe(5, 'Expected the selection-list tabindex to be set to the attribute value.');
279+
});
280+
281+
it('should support changing the tabIndex through binding', () => {
282+
const fixture = TestBed.createComponent(SelectionListWithTabindexBinding);
283+
const selectionList = fixture.debugElement.query(By.directive(MatSelectionList));
284+
285+
expect(selectionList.componentInstance.tabIndex)
286+
.toBe(0, 'Expected the tabIndex to be set to "0" by default.');
287+
288+
fixture.componentInstance.tabIndex = 3;
289+
fixture.detectChanges();
290+
291+
expect(selectionList.componentInstance.tabIndex)
292+
.toBe(3, 'Expected the tabIndex to updated through binding.');
293+
294+
fixture.componentInstance.disabled = true;
295+
fixture.detectChanges();
296+
297+
expect(selectionList.componentInstance.tabIndex)
298+
.toBe(-1, 'Expected the tabIndex to be set to "-1" if selection list is disabled.');
299+
});
300+
});
301+
259302
describe('with single option', () => {
260303
let fixture: ComponentFixture<SelectionListWithOnlyOneOption>;
261304
let listOption: DebugElement;
@@ -509,3 +552,16 @@ class SelectionListWithSelectedOption {
509552
</mat-selection-list>`})
510553
class SelectionListWithOnlyOneOption {
511554
}
555+
556+
@Component({
557+
template: `<mat-selection-list tabindex="5"></mat-selection-list>`
558+
})
559+
class SelectionListWithTabindexAttr {}
560+
561+
@Component({
562+
template: `<mat-selection-list [tabIndex]="tabIndex" [disabled]="disabled"></mat-selection-list>`
563+
})
564+
class SelectionListWithTabindexBinding {
565+
tabIndex: number;
566+
disabled: boolean;
567+
}

src/lib/list/selection-list.ts

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {SelectionModel} from '@angular/cdk/collections';
1212
import {SPACE} from '@angular/cdk/keycodes';
1313
import {
1414
AfterContentInit,
15+
Attribute,
1516
ChangeDetectionStrategy,
1617
ChangeDetectorRef,
1718
Component,
@@ -32,16 +33,19 @@ import {
3233
import {
3334
CanDisable,
3435
CanDisableRipple,
36+
HasTabIndex,
3537
MatLine,
3638
MatLineSetter,
3739
mixinDisabled,
3840
mixinDisableRipple,
41+
mixinTabIndex,
3942
} from '@angular/material/core';
4043

4144

4245
/** @docs-private */
4346
export class MatSelectionListBase {}
44-
export const _MatSelectionListMixinBase = mixinDisableRipple(mixinDisabled(MatSelectionListBase));
47+
export const _MatSelectionListMixinBase =
48+
mixinTabIndex(mixinDisableRipple(mixinDisabled(MatSelectionListBase)));
4549

4650
/** @docs-private */
4751
export class MatListOptionBase {}
@@ -178,10 +182,10 @@ export class MatListOption extends _MatListOptionMixinBase
178182
@Component({
179183
moduleId: module.id,
180184
selector: 'mat-selection-list',
181-
inputs: ['disabled', 'disableRipple'],
185+
inputs: ['disabled', 'disableRipple', 'tabIndex'],
182186
host: {
183187
'role': 'listbox',
184-
'[attr.tabindex]': '_tabIndex',
188+
'[tabIndex]': 'tabIndex',
185189
'class': 'mat-selection-list',
186190
'(focus)': 'focus()',
187191
'(keydown)': '_keydown($event)',
@@ -192,11 +196,8 @@ export class MatListOption extends _MatListOptionMixinBase
192196
preserveWhitespaces: false,
193197
changeDetection: ChangeDetectionStrategy.OnPush
194198
})
195-
export class MatSelectionList extends _MatSelectionListMixinBase
196-
implements FocusableOption, CanDisable, CanDisableRipple, AfterContentInit {
197-
198-
/** Tab index for the selection-list. */
199-
_tabIndex = 0;
199+
export class MatSelectionList extends _MatSelectionListMixinBase implements FocusableOption,
200+
CanDisable, CanDisableRipple, HasTabIndex, AfterContentInit {
200201

201202
/** The FocusKeyManager which handles focus. */
202203
_keyManager: FocusKeyManager<MatListOption>;
@@ -207,16 +208,14 @@ export class MatSelectionList extends _MatSelectionListMixinBase
207208
/** The currently selected options. */
208209
selectedOptions: SelectionModel<MatListOption> = new SelectionModel<MatListOption>(true);
209210

210-
constructor(private _element: ElementRef) {
211+
constructor(private _element: ElementRef, @Attribute('tabindex') tabIndex: string) {
211212
super();
213+
214+
this.tabIndex = parseInt(tabIndex) || 0;
212215
}
213216

214217
ngAfterContentInit(): void {
215218
this._keyManager = new FocusKeyManager<MatListOption>(this.options).withWrap();
216-
217-
if (this.disabled) {
218-
this._tabIndex = -1;
219-
}
220219
}
221220

222221
/** Focus the selection-list. */

0 commit comments

Comments
 (0)