Skip to content

Commit 087ed4a

Browse files
crisbetojelbourn
authored andcommitted
fix(list): don't select disabled options when pressing ctrl + a (#18885)
Fixes disabled options being selected when the user presses ctrl + a. Selecting disabled options is still supported through `selectAll`. (cherry picked from commit 77f65e3)
1 parent 4557d58 commit 087ed4a

File tree

2 files changed

+51
-3
lines changed

2 files changed

+51
-3
lines changed

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

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,7 @@ describe('MatSelectionList without forms', () => {
494494
});
495495

496496
it('should select all items using ctrl + a', () => {
497+
listOptions.forEach(option => option.componentInstance.disabled = false);
497498
const event = createKeyboardEvent('keydown', A, selectionList.nativeElement);
498499
Object.defineProperty(event, 'ctrlKey', {get: () => true});
499500

@@ -505,6 +506,23 @@ describe('MatSelectionList without forms', () => {
505506
expect(listOptions.every(option => option.componentInstance.selected)).toBe(true);
506507
});
507508

509+
it('should not select disabled items when pressing ctrl + a', () => {
510+
const event = createKeyboardEvent('keydown', A, selectionList.nativeElement);
511+
Object.defineProperty(event, 'ctrlKey', {get: () => true});
512+
513+
listOptions.slice(0, 2).forEach(option => option.componentInstance.disabled = true);
514+
fixture.detectChanges();
515+
516+
expect(listOptions.map(option => option.componentInstance.selected))
517+
.toEqual([false, false, false, false, false]);
518+
519+
dispatchEvent(selectionList.nativeElement, event);
520+
fixture.detectChanges();
521+
522+
expect(listOptions.map(option => option.componentInstance.selected))
523+
.toEqual([false, false, true, true, true]);
524+
});
525+
508526
it('should select all items using ctrl + a if some items are selected', () => {
509527
const event = createKeyboardEvent('keydown', A, selectionList.nativeElement);
510528
Object.defineProperty(event, 'ctrlKey', {get: () => true});
@@ -617,6 +635,20 @@ describe('MatSelectionList without forms', () => {
617635
expect(list.options.toArray().every(option => option.selected)).toBe(true);
618636
});
619637

638+
it('should be able to select all options, even if they are disabled', () => {
639+
const list: MatSelectionList = selectionList.componentInstance;
640+
641+
list.options.forEach(option => option.disabled = true);
642+
fixture.detectChanges();
643+
644+
expect(list.options.toArray().every(option => option.selected)).toBe(false);
645+
646+
list.selectAll();
647+
fixture.detectChanges();
648+
649+
expect(list.options.toArray().every(option => option.selected)).toBe(true);
650+
});
651+
620652
it('should be able to deselect all options', () => {
621653
const list: MatSelectionList = selectionList.componentInstance;
622654

@@ -629,6 +661,21 @@ describe('MatSelectionList without forms', () => {
629661
expect(list.options.toArray().every(option => option.selected)).toBe(false);
630662
});
631663

664+
it('should be able to deselect all options, even if they are disabled', () => {
665+
const list: MatSelectionList = selectionList.componentInstance;
666+
667+
list.options.forEach(option => option.toggle());
668+
expect(list.options.toArray().every(option => option.selected)).toBe(true);
669+
670+
list.options.forEach(option => option.disabled = true);
671+
fixture.detectChanges();
672+
673+
list.deselectAll();
674+
fixture.detectChanges();
675+
676+
expect(list.options.toArray().every(option => option.selected)).toBe(false);
677+
});
678+
632679
it('should update the list value when an item is selected programmatically', () => {
633680
const list: MatSelectionList = selectionList.componentInstance;
634681

src/material/list/selection-list.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -550,7 +550,8 @@ export class MatSelectionList extends _MatSelectionListMixinBase implements CanD
550550
// The "A" key gets special treatment, because it's used for the "select all" functionality.
551551
if (keyCode === A && this.multiple && hasModifierKey(event, 'ctrlKey') &&
552552
!manager.isTyping()) {
553-
this.options.find(option => !option.selected) ? this.selectAll() : this.deselectAll();
553+
const shouldSelect = this.options.some(option => !option.disabled && !option.selected);
554+
this._setAllOptionsSelected(shouldSelect, true);
554555
event.preventDefault();
555556
} else {
556557
manager.onKeydown(event);
@@ -663,13 +664,13 @@ export class MatSelectionList extends _MatSelectionListMixinBase implements CanD
663664
* Sets the selected state on all of the options
664665
* and emits an event if anything changed.
665666
*/
666-
private _setAllOptionsSelected(isSelected: boolean) {
667+
private _setAllOptionsSelected(isSelected: boolean, skipDisabled?: boolean) {
667668
// Keep track of whether anything changed, because we only want to
668669
// emit the changed event when something actually changed.
669670
let hasChanged = false;
670671

671672
this.options.forEach(option => {
672-
if (option._setSelected(isSelected)) {
673+
if ((!skipDisabled || !option.disabled) && option._setSelected(isSelected)) {
673674
hasChanged = true;
674675
}
675676
});

0 commit comments

Comments
 (0)