Skip to content

Commit bbb92a7

Browse files
authored
fix(chips): unable to set custom tabindex on chip (#17699)
I noticed this as I was going to recommend to someone on GitHub to set a tabindex on a `mat-chip`. As things are setup right now, the `mat-chip` will override whatever `tabindex` is set on it. Note that this isn't an issue on the MDC chip. These changes fix the current `mat-chip` and add tests to the MDC based one on so it doesn't regress.
1 parent ea17135 commit bbb92a7

File tree

4 files changed

+110
-29
lines changed

4 files changed

+110
-29
lines changed

src/material-experimental/mdc-chips/chip.spec.ts

Lines changed: 50 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,12 @@ describe('MDC-based MatChip', () => {
2121
globalRippleOptions = {};
2222
TestBed.configureTestingModule({
2323
imports: [MatChipsModule],
24-
declarations: [BasicChip, SingleChip],
24+
declarations: [
25+
BasicChip,
26+
SingleChip,
27+
BasicChipWithStaticTabindex,
28+
BasicChipWithBoundTabindex,
29+
],
2530
providers: [
2631
{provide: MAT_RIPPLE_GLOBAL_OPTIONS, useFactory: () => globalRippleOptions},
2732
{provide: Directionality, useFactory: () => ({
@@ -35,18 +40,41 @@ describe('MDC-based MatChip', () => {
3540
}));
3641

3742
describe('MatBasicChip', () => {
38-
39-
beforeEach(() => {
43+
it('adds the `mat-mdc-basic-chip` class', () => {
4044
fixture = TestBed.createComponent(BasicChip);
4145
fixture.detectChanges();
4246

43-
chipDebugElement = fixture.debugElement.query(By.directive(MatChip))!;
44-
chipNativeElement = chipDebugElement.nativeElement;
45-
chipInstance = chipDebugElement.injector.get<MatChip>(MatChip);
47+
const chip = fixture.nativeElement.querySelector('mat-basic-chip');
48+
expect(chip.classList).toContain('mat-mdc-basic-chip');
4649
});
4750

48-
it('adds the `mat-mdc-basic-chip` class', () => {
49-
expect(chipNativeElement.classList).toContain('mat-mdc-basic-chip');
51+
it('should be able to set a static tabindex', () => {
52+
fixture = TestBed.createComponent(BasicChipWithStaticTabindex);
53+
fixture.detectChanges();
54+
55+
const chip = fixture.nativeElement.querySelector('mat-basic-chip');
56+
expect(chip.getAttribute('tabindex')).toBe('3');
57+
});
58+
59+
it('should be able to set a static tabindex', () => {
60+
fixture = TestBed.createComponent(BasicChipWithStaticTabindex);
61+
fixture.detectChanges();
62+
63+
const chip = fixture.nativeElement.querySelector('mat-basic-chip');
64+
expect(chip.getAttribute('tabindex')).toBe('3');
65+
});
66+
67+
it('should be able to set a dynamic tabindex', () => {
68+
fixture = TestBed.createComponent(BasicChipWithBoundTabindex);
69+
fixture.detectChanges();
70+
71+
const chip = fixture.nativeElement.querySelector('mat-basic-chip');
72+
expect(chip.getAttribute('tabindex')).toBe('12');
73+
74+
fixture.componentInstance.tabindex = 15;
75+
fixture.detectChanges();
76+
77+
expect(chip.getAttribute('tabindex')).toBe('15');
5078
});
5179
});
5280

@@ -184,7 +212,20 @@ class SingleChip {
184212
}
185213

186214
@Component({
187-
template: `<mat-basic-chip>{{name}}</mat-basic-chip>`
215+
template: `<mat-basic-chip>Hello</mat-basic-chip>`
188216
})
189217
class BasicChip {
190218
}
219+
220+
@Component({
221+
template: `<mat-basic-chip tabindex="3">Hello</mat-basic-chip>`
222+
})
223+
class BasicChipWithStaticTabindex {
224+
}
225+
226+
@Component({
227+
template: `<mat-basic-chip [tabIndex]="tabindex">Hello</mat-basic-chip>`
228+
})
229+
class BasicChipWithBoundTabindex {
230+
tabindex = 12;
231+
}

src/material/chips/chip.spec.ts

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,18 @@ describe('MatChip', () => {
1515
let chipNativeElement: HTMLElement;
1616
let chipInstance: MatChip;
1717
let globalRippleOptions: RippleGlobalOptions;
18-
1918
let dir = 'ltr';
2019

2120
beforeEach(async(() => {
2221
globalRippleOptions = {};
2322
TestBed.configureTestingModule({
2423
imports: [MatChipsModule],
25-
declarations: [BasicChip, SingleChip],
24+
declarations: [
25+
BasicChip,
26+
SingleChip,
27+
BasicChipWithStaticTabindex,
28+
BasicChipWithBoundTabindex,
29+
],
2630
providers: [
2731
{provide: MAT_RIPPLE_GLOBAL_OPTIONS, useFactory: () => globalRippleOptions},
2832
{provide: Directionality, useFactory: () => ({
@@ -36,22 +40,38 @@ describe('MatChip', () => {
3640
}));
3741

3842
describe('MatBasicChip', () => {
39-
40-
beforeEach(() => {
43+
it('adds the `mat-basic-chip` class', () => {
4144
fixture = TestBed.createComponent(BasicChip);
4245
fixture.detectChanges();
4346

44-
chipDebugElement = fixture.debugElement.query(By.directive(MatChip))!;
45-
chipNativeElement = chipDebugElement.nativeElement;
46-
chipInstance = chipDebugElement.injector.get<MatChip>(MatChip);
47+
const chip = fixture.nativeElement.querySelector('mat-basic-chip');
48+
expect(chip.classList).toContain('mat-chip');
49+
expect(chip.classList).toContain('mat-basic-chip');
4750
});
4851

49-
it('adds the `mat-basic-chip` class', () => {
50-
expect(chipNativeElement.classList).toContain('mat-chip');
51-
expect(chipNativeElement.classList).toContain('mat-basic-chip');
52+
it('should be able to set a static tabindex', () => {
53+
fixture = TestBed.createComponent(BasicChipWithStaticTabindex);
54+
fixture.detectChanges();
55+
56+
const chip = fixture.nativeElement.querySelector('mat-basic-chip');
57+
expect(chip.getAttribute('tabindex')).toBe('3');
58+
});
59+
60+
it('should be able to set a dynamic tabindex', () => {
61+
fixture = TestBed.createComponent(BasicChipWithBoundTabindex);
62+
fixture.detectChanges();
63+
64+
const chip = fixture.nativeElement.querySelector('mat-basic-chip');
65+
expect(chip.getAttribute('tabindex')).toBe('12');
66+
67+
fixture.componentInstance.tabindex = 15;
68+
fixture.detectChanges();
69+
70+
expect(chip.getAttribute('tabindex')).toBe('15');
5271
});
5372
});
5473

74+
5575
describe('MatChip', () => {
5676
let testComponent: SingleChip;
5777

@@ -426,7 +446,20 @@ class SingleChip {
426446
}
427447

428448
@Component({
429-
template: `<mat-basic-chip>{{name}}</mat-basic-chip>`
449+
template: `<mat-basic-chip>Hello</mat-basic-chip>`
430450
})
431451
class BasicChip {
432452
}
453+
454+
@Component({
455+
template: `<mat-basic-chip tabindex="3">Hello</mat-basic-chip>`
456+
})
457+
class BasicChipWithStaticTabindex {
458+
}
459+
460+
@Component({
461+
template: `<mat-basic-chip [tabIndex]="tabindex">Hello</mat-basic-chip>`
462+
})
463+
class BasicChipWithBoundTabindex {
464+
tabindex = 12;
465+
}

src/material/chips/chip.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
Optional,
2424
Output,
2525
ChangeDetectorRef,
26+
Attribute,
2627
} from '@angular/core';
2728
import {
2829
CanColor,
@@ -31,6 +32,9 @@ import {
3132
CanDisableCtor,
3233
CanDisableRipple,
3334
CanDisableRippleCtor,
35+
HasTabIndex,
36+
HasTabIndexCtor,
37+
mixinTabIndex,
3438
MAT_RIPPLE_GLOBAL_OPTIONS,
3539
mixinColor,
3640
mixinDisabled,
@@ -69,8 +73,9 @@ class MatChipBase {
6973
constructor(public _elementRef: ElementRef) {}
7074
}
7175

72-
const _MatChipMixinBase: CanColorCtor & CanDisableRippleCtor & CanDisableCtor & typeof MatChipBase =
73-
mixinColor(mixinDisableRipple(mixinDisabled(MatChipBase)), 'primary');
76+
const _MatChipMixinBase: CanColorCtor & CanDisableRippleCtor & CanDisableCtor &
77+
HasTabIndexCtor & typeof MatChipBase =
78+
mixinTabIndex(mixinColor(mixinDisableRipple(mixinDisabled(MatChipBase)), 'primary'), -1);
7479

7580
/**
7681
* Dummy directive to add CSS class to chip avatar.
@@ -97,11 +102,11 @@ export class MatChipTrailingIcon {}
97102
*/
98103
@Directive({
99104
selector: `mat-basic-chip, [mat-basic-chip], mat-chip, [mat-chip]`,
100-
inputs: ['color', 'disabled', 'disableRipple'],
105+
inputs: ['color', 'disabled', 'disableRipple', 'tabIndex'],
101106
exportAs: 'matChip',
102107
host: {
103108
'class': 'mat-chip',
104-
'[attr.tabindex]': 'disabled ? null : -1',
109+
'[attr.tabindex]': 'disabled ? null : tabIndex',
105110
'role': 'option',
106111
'[class.mat-chip-selected]': 'selected',
107112
'[class.mat-chip-with-avatar]': 'avatar',
@@ -118,7 +123,7 @@ export class MatChipTrailingIcon {}
118123
},
119124
})
120125
export class MatChip extends _MatChipMixinBase implements FocusableOption, OnDestroy, CanColor,
121-
CanDisable, CanDisableRipple, RippleTarget {
126+
CanDisable, CanDisableRipple, RippleTarget, HasTabIndex {
122127

123128
/** Reference to the RippleRenderer for the chip. */
124129
private _chipRipple: RippleRenderer;
@@ -238,7 +243,8 @@ export class MatChip extends _MatChipMixinBase implements FocusableOption, OnDes
238243
// @breaking-change 8.0.0 `animationMode` parameter to become required.
239244
@Optional() @Inject(ANIMATION_MODULE_TYPE) animationMode?: string,
240245
// @breaking-change 9.0.0 `_changeDetectorRef` parameter to become required.
241-
private _changeDetectorRef?: ChangeDetectorRef) {
246+
private _changeDetectorRef?: ChangeDetectorRef,
247+
@Attribute('tabindex') tabIndex?: string) {
242248
super(_elementRef);
243249

244250
this._addHostClassName();
@@ -247,6 +253,7 @@ export class MatChip extends _MatChipMixinBase implements FocusableOption, OnDes
247253
this._chipRipple.setupTriggerEvents(_elementRef);
248254
this.rippleConfig = globalRippleOptions || {};
249255
this._animationsDisabled = animationMode === 'NoopAnimations';
256+
this.tabIndex = tabIndex != null ? (parseInt(tabIndex) || -1) : -1;
250257
}
251258

252259
_addHostClassName() {

tools/public_api_guard/material/chips.d.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
export declare const MAT_CHIPS_DEFAULT_OPTIONS: InjectionToken<MatChipsDefaultOptions>;
22

3-
export declare class MatChip extends _MatChipMixinBase implements FocusableOption, OnDestroy, CanColor, CanDisable, CanDisableRipple, RippleTarget {
3+
export declare class MatChip extends _MatChipMixinBase implements FocusableOption, OnDestroy, CanColor, CanDisable, CanDisableRipple, RippleTarget, HasTabIndex {
44
_animationsDisabled: boolean;
55
_chipListMultiple: boolean;
66
_elementRef: ElementRef<HTMLElement>;
@@ -29,7 +29,7 @@ export declare class MatChip extends _MatChipMixinBase implements FocusableOptio
2929
trailingIcon: MatChipTrailingIcon;
3030
get value(): any;
3131
set value(value: any);
32-
constructor(_elementRef: ElementRef<HTMLElement>, _ngZone: NgZone, platform: Platform, globalRippleOptions: RippleGlobalOptions | null, animationMode?: string, _changeDetectorRef?: ChangeDetectorRef | undefined);
32+
constructor(_elementRef: ElementRef<HTMLElement>, _ngZone: NgZone, platform: Platform, globalRippleOptions: RippleGlobalOptions | null, animationMode?: string, _changeDetectorRef?: ChangeDetectorRef | undefined, tabIndex?: string);
3333
_addHostClassName(): void;
3434
_blur(): void;
3535
_handleClick(event: Event): void;
@@ -46,7 +46,7 @@ export declare class MatChip extends _MatChipMixinBase implements FocusableOptio
4646
static ngAcceptInputType_removable: BooleanInput;
4747
static ngAcceptInputType_selectable: BooleanInput;
4848
static ngAcceptInputType_selected: BooleanInput;
49-
static ɵdir: i0.ɵɵDirectiveDefWithMeta<MatChip, "mat-basic-chip, [mat-basic-chip], mat-chip, [mat-chip]", ["matChip"], { "color": "color"; "disabled": "disabled"; "disableRipple": "disableRipple"; "selected": "selected"; "value": "value"; "selectable": "selectable"; "removable": "removable"; }, { "selectionChange": "selectionChange"; "destroyed": "destroyed"; "removed": "removed"; }, ["avatar", "trailingIcon", "removeIcon"]>;
49+
static ɵdir: i0.ɵɵDirectiveDefWithMeta<MatChip, "mat-basic-chip, [mat-basic-chip], mat-chip, [mat-chip]", ["matChip"], { "color": "color"; "disabled": "disabled"; "disableRipple": "disableRipple"; "tabIndex": "tabIndex"; "selected": "selected"; "value": "value"; "selectable": "selectable"; "removable": "removable"; }, { "selectionChange": "selectionChange"; "destroyed": "destroyed"; "removed": "removed"; }, ["avatar", "trailingIcon", "removeIcon"]>;
5050
static ɵfac: i0.ɵɵFactoryDef<MatChip>;
5151
}
5252

0 commit comments

Comments
 (0)