Skip to content

Commit b103d1e

Browse files
committed
fix(chips): incorrectly handling disabled state
* Fixes the form field displaying as disabled, but the user still being able to interact with the chip list. * Fixes the chip list still being focusable while it is disabled. * Fixes the individual chips not being disabled when the list is disabled. * Fixes the chip input not being disabled when the list is disabled. Fixes #11089.
1 parent 8aaca12 commit b103d1e

File tree

4 files changed

+69
-3
lines changed

4 files changed

+69
-3
lines changed

src/lib/chips/chip-input.spec.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@ import {Directionality} from '@angular/cdk/bidi';
22
import {ENTER, COMMA} from '@angular/cdk/keycodes';
33
import {PlatformModule} from '@angular/cdk/platform';
44
import {createKeyboardEvent} from '@angular/cdk/testing';
5-
import {Component, DebugElement} from '@angular/core';
5+
import {Component, DebugElement, ViewChild} from '@angular/core';
66
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
77
import {By} from '@angular/platform-browser';
88
import {NoopAnimationsModule} from '@angular/platform-browser/animations';
99
import {MatFormFieldModule} from '@angular/material/form-field';
1010
import {MatChipInput, MatChipInputEvent} from './chip-input';
1111
import {MatChipsModule} from './index';
1212
import {MAT_CHIPS_DEFAULT_OPTIONS, MatChipsDefaultOptions} from './chip-default-options';
13+
import {MatChipList} from './chip-list';
1314

1415

1516
describe('MatChipInput', () => {
@@ -82,6 +83,17 @@ describe('MatChipInput', () => {
8283
expect(label.textContent).toContain('or don\'t');
8384
});
8485

86+
it('should become disabled if the chip list is disabled', () => {
87+
expect(inputNativeElement.hasAttribute('disabled')).toBe(false);
88+
expect(chipInputDirective.disabled).toBe(false);
89+
90+
fixture.componentInstance.chipListInstance.disabled = true;
91+
fixture.detectChanges();
92+
93+
expect(inputNativeElement.getAttribute('disabled')).toBe('true');
94+
expect(chipInputDirective.disabled).toBe(true);
95+
});
96+
8597
});
8698

8799
describe('[addOnBlur]', () => {
@@ -186,6 +198,7 @@ describe('MatChipInput', () => {
186198
`
187199
})
188200
class TestChipInput {
201+
@ViewChild(MatChipList) chipListInstance: MatChipList;
189202
addOnBlur: boolean = false;
190203
placeholder = '';
191204

src/lib/chips/chip-input.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ let nextUniqueId = 0;
3838
'(focus)': '_focus()',
3939
'(input)': '_onInput()',
4040
'[id]': 'id',
41+
'[attr.disabled]': 'disabled || null',
4142
'[attr.placeholder]': 'placeholder || null',
4243
}
4344
})
@@ -81,6 +82,12 @@ export class MatChipInput implements OnChanges {
8182
/** Unique id for the input. */
8283
@Input() id: string = `mat-chip-list-input-${nextUniqueId++}`;
8384

85+
/** Whether the input is disabled. */
86+
@Input()
87+
get disabled(): boolean { return this._disabled || (this._chipList && this._chipList.disabled); }
88+
set disabled(value: boolean) { this._disabled = coerceBooleanProperty(value); }
89+
private _disabled: boolean = false;
90+
8491
/** Whether the input is empty. */
8592
get empty(): boolean { return !this._inputElement.value; }
8693

src/lib/chips/chip-list.spec.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,20 @@ describe('MatChipList', () => {
5050

5151
expect(chipsValid).toBe(true);
5252
});
53+
54+
it('should toggle the chips disabled state based on whether it is disabled', () => {
55+
expect(chips.toArray().every(chip => chip.disabled)).toBe(false);
56+
57+
chipListInstance.disabled = true;
58+
fixture.detectChanges();
59+
60+
expect(chips.toArray().every(chip => chip.disabled)).toBe(true);
61+
62+
chipListInstance.disabled = false;
63+
fixture.detectChanges();
64+
65+
expect(chips.toArray().every(chip => chip.disabled)).toBe(false);
66+
});
5367
});
5468

5569
describe('with selected chips', () => {
@@ -114,6 +128,27 @@ describe('MatChipList', () => {
114128
expect(manager.activeItemIndex).toBe(lastIndex);
115129
});
116130

131+
it('should be able to become focused when disabled', () => {
132+
expect(chipListInstance.focused).toBe(false, 'Expected list to not be focused.');
133+
134+
chipListInstance.disabled = true;
135+
fixture.detectChanges();
136+
137+
chipListInstance.focus();
138+
fixture.detectChanges();
139+
140+
expect(chipListInstance.focused).toBe(false, 'Expected list to continue not to be focused');
141+
});
142+
143+
it('should remove the tabindex from the list if it is disabled', () => {
144+
expect(chipListNativeElement.getAttribute('tabindex')).toBeTruthy();
145+
146+
chipListInstance.disabled = true;
147+
fixture.detectChanges();
148+
149+
expect(chipListNativeElement.hasAttribute('tabindex')).toBeFalsy();
150+
});
151+
117152
describe('on chip destroy', () => {
118153
it('should focus the next item', () => {
119154
let array = chips.toArray();

src/lib/chips/chip-list.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ export class MatChipListChange {
7272
template: `<div class="mat-chip-list-wrapper"><ng-content></ng-content></div>`,
7373
exportAs: 'matChipList',
7474
host: {
75-
'[attr.tabindex]': '_tabIndex',
75+
'[attr.tabindex]': 'disabled ? null : _tabIndex',
7676
'[attr.aria-describedby]': '_ariaDescribedby || null',
7777
'[attr.aria-required]': 'required.toString()',
7878
'[attr.aria-disabled]': 'disabled.toString()',
@@ -261,7 +261,13 @@ export class MatChipList extends _MatChipListMixinBase implements MatFormFieldCo
261261
*/
262262
@Input()
263263
get disabled(): boolean { return this.ngControl ? !!this.ngControl.disabled : this._disabled; }
264-
set disabled(value: boolean) { this._disabled = coerceBooleanProperty(value); }
264+
set disabled(value: boolean) {
265+
this._disabled = coerceBooleanProperty(value);
266+
267+
if (this.chips) {
268+
this.chips.forEach(chip => chip.disabled = this._disabled);
269+
}
270+
}
265271
protected _disabled: boolean = false;
266272

267273
/** Orientation of the chip list. */
@@ -275,6 +281,7 @@ export class MatChipList extends _MatChipListMixinBase implements MatFormFieldCo
275281
get selectable(): boolean { return this._selectable; }
276282
set selectable(value: boolean) {
277283
this._selectable = coerceBooleanProperty(value);
284+
278285
if (this.chips) {
279286
this.chips.forEach(chip => chip.chipListSelectable = this._selectable);
280287
}
@@ -441,6 +448,10 @@ export class MatChipList extends _MatChipListMixinBase implements MatFormFieldCo
441448
* are no eligible chips.
442449
*/
443450
focus(): void {
451+
if (this.disabled) {
452+
return;
453+
}
454+
444455
// TODO: ARIA says this should focus the first `selected` chip if any are selected.
445456
// Focus on first element if there's no chipInput inside chip-list
446457
if (this._chipInput && this._chipInput.focused) {

0 commit comments

Comments
 (0)