Skip to content

Commit

Permalink
fix(material/chips): selectable="false" doesn't work on initial ass…
Browse files Browse the repository at this point in the history
…ignment

Fixes #24903.
  • Loading branch information
volvachev authored and mmalerba committed Aug 9, 2022
1 parent e8e7199 commit 1d19c08
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 5 deletions.
58 changes: 58 additions & 0 deletions src/material/chips/chip-listbox.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,34 @@ describe('MDC-based MatChipListbox', () => {
.withContext('Expect no selected chips')
.toBeUndefined();
});

it('should not select when is not selectable', fakeAsync(() => {
fixture.destroy();
TestBed.resetTestingModule();

const falsyFixture = createComponent(FalsyBasicChipListbox);
falsyFixture.detectChanges();
tick();
falsyFixture.detectChanges();

const chipListboxElement = falsyFixture.debugElement.query(By.directive(MatChipListbox))!;
const _chips = chipListboxElement.componentInstance._chips;
const nativeChips = (
chipListboxElement.nativeElement as HTMLElement
).querySelectorAll<HTMLElement>('.mdc-evolution-chip__action--primary');

expect(_chips.first.selected)
.withContext('Expected first option not to be selected')
.toBe(false);

dispatchKeyboardEvent(nativeChips[0], 'keydown', SPACE);
falsyFixture.detectChanges();
flush();

expect(_chips.first.selected)
.withContext('Expected first option not to be selected.')
.toBe(false);
}));
});

describe('chip list with chip input', () => {
Expand Down Expand Up @@ -874,3 +902,33 @@ class SelectedChipListbox {
];
@ViewChildren(MatChipOption) chips: QueryList<MatChipOption>;
}

@Component({
template: `
<mat-chip-listbox [formControl]="control" [required]="isRequired"
[tabIndex]="tabIndexOverride" [selectable]="selectable">
<mat-chip-option *ngFor="let food of foods" [value]="food.value" [disabled]="food.disabled">
{{ food.viewValue }}
</mat-chip-option>
</mat-chip-listbox>
`,
})
class FalsyBasicChipListbox {
foods: any[] = [
{value: 'steak-0', viewValue: 'Steak'},
{value: 'pizza-1', viewValue: 'Pizza'},
{value: 'tacos-2', viewValue: 'Tacos', disabled: true},
{value: 'sandwich-3', viewValue: 'Sandwich'},
{value: 'chips-4', viewValue: 'Chips'},
{value: 'eggs-5', viewValue: 'Eggs'},
{value: 'pasta-6', viewValue: 'Pasta'},
{value: 'sushi-7', viewValue: 'Sushi'},
];
control = new FormControl<string | null>(null);
isRequired: boolean;
tabIndexOverride: number;
selectable: boolean = false;

@ViewChild(MatChipListbox) chipListbox: MatChipListbox;
@ViewChildren(MatChipOption) chips: QueryList<MatChipOption>;
}
59 changes: 59 additions & 0 deletions src/material/legacy-chips/chip-list.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -748,6 +748,33 @@ describe('MatChipList', () => {
.withContext('Expect no selected chips')
.toBeUndefined();
});

it('should not select when is not selectable', fakeAsync(() => {
fixture.destroy();
TestBed.resetTestingModule();

const falsyFixture = createComponent(FalsyBasicChipList);
falsyFixture.detectChanges();
tick();
falsyFixture.detectChanges();

const chipListElement = falsyFixture.debugElement.query(By.directive(MatLegacyChipList))!;
const currentChips = chipListElement.componentInstance.chips;
const currentNativeChips = falsyFixture.debugElement
.queryAll(By.css('mat-chip'))
.map(chip => chip.nativeElement);

expect(currentChips.first.selected)
.withContext('Expected first option not to be selected')
.toBe(false);

dispatchKeyboardEvent(currentNativeChips[0], 'keydown', SPACE);
falsyFixture.detectChanges();

expect(currentChips.first.selected)
.withContext('Expected first option not to be selected.')
.toBe(false);
}));
});

describe('forms integration', () => {
Expand Down Expand Up @@ -1866,3 +1893,35 @@ class ChipListInsideDynamicFormGroup {
});
}
}

@Component({
selector: 'basic-chip-list',
template: `
<mat-form-field>
<mat-chip-list placeholder="Food" [formControl]="control"
[tabIndex]="tabIndexOverride" [selectable]="selectable">
<mat-chip *ngFor="let food of foods" [value]="food.value" [disabled]="food.disabled">
{{ food.viewValue }}
</mat-chip>
</mat-chip-list>
</mat-form-field>
`,
})
class FalsyBasicChipList {
foods: any[] = [
{value: 'steak-0', viewValue: 'Steak'},
{value: 'pizza-1', viewValue: 'Pizza'},
{value: 'tacos-2', viewValue: 'Tacos', disabled: true},
{value: 'sandwich-3', viewValue: 'Sandwich'},
{value: 'chips-4', viewValue: 'Chips'},
{value: 'eggs-5', viewValue: 'Eggs'},
{value: 'pasta-6', viewValue: 'Pasta'},
{value: 'sushi-7', viewValue: 'Sushi'},
];
control = new FormControl<string | null>(null);
tabIndexOverride: number;
selectable: boolean = false;

@ViewChild(MatLegacyChipList) chipList: MatLegacyChipList;
@ViewChildren(MatLegacyChip) chips: QueryList<MatLegacyChip>;
}
8 changes: 3 additions & 5 deletions src/material/legacy-chips/chip-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -329,10 +329,7 @@ export class MatLegacyChipList
}
set selectable(value: BooleanInput) {
this._selectable = coerceBooleanProperty(value);

if (this.chips) {
this.chips.forEach(chip => (chip.chipListSelectable = this._selectable));
}
this._syncChipsState();
}
protected _selectable: boolean = true;

Expand Down Expand Up @@ -414,7 +411,7 @@ export class MatLegacyChipList

// When the list changes, re-subscribe
this.chips.changes.pipe(startWith(null), takeUntil(this._destroyed)).subscribe(() => {
if (this.disabled) {
if (this.disabled || !this.selectable) {
// Since this happens after the content has been
// checked, we need to defer it to the next tick.
Promise.resolve().then(() => {
Expand Down Expand Up @@ -844,6 +841,7 @@ export class MatLegacyChipList
this.chips.forEach(chip => {
chip._chipListDisabled = this._disabled;
chip._chipListMultiple = this.multiple;
chip.chipListSelectable = this._selectable;
});
}
}
Expand Down

0 comments on commit 1d19c08

Please sign in to comment.