From 503e0eb5914c44a07a562552ffd03be942394009 Mon Sep 17 00:00:00 2001 From: Egor Volvachev Date: Fri, 15 Apr 2022 23:25:12 +0300 Subject: [PATCH] fix(material/list): add command+A for MacOs "select all" functionality. Fixes a bug in Angular Material `list` when using MacOs `command`+`a` does not work for 'select all functionality'. Fixes #24781 --- src/material/list/selection-list.spec.ts | 82 ++++++++++++++++++++++++ src/material/list/selection-list.ts | 2 +- 2 files changed, 83 insertions(+), 1 deletion(-) diff --git a/src/material/list/selection-list.spec.ts b/src/material/list/selection-list.spec.ts index 843ccd4e4338..274f6c2e96c6 100644 --- a/src/material/list/selection-list.spec.ts +++ b/src/material/list/selection-list.spec.ts @@ -594,6 +594,88 @@ describe('MatSelectionList without forms', () => { ); }); + it('should select all items using command(metaKey) + a', () => { + listOptions.forEach(option => (option.componentInstance.disabled = false)); + const event = createKeyboardEvent('keydown', A, undefined, {meta: true}); + + expect(listOptions.some(option => option.componentInstance.selected)).toBe(false); + + dispatchEvent(selectionList.nativeElement, event); + fixture.detectChanges(); + + expect(listOptions.every(option => option.componentInstance.selected)).toBe(true); + }); + + it('should not select disabled items when pressing command(metaKey) + a', () => { + const event = createKeyboardEvent('keydown', A, undefined, {meta: true}); + + listOptions.slice(0, 2).forEach(option => (option.componentInstance.disabled = true)); + fixture.detectChanges(); + + expect(listOptions.map(option => option.componentInstance.selected)).toEqual([ + false, + false, + false, + false, + false, + ]); + + dispatchEvent(selectionList.nativeElement, event); + fixture.detectChanges(); + + expect(listOptions.map(option => option.componentInstance.selected)).toEqual([ + false, + false, + true, + true, + true, + ]); + }); + + it('should select all items using command(metaKey) + a if some items are selected', () => { + const event = createKeyboardEvent('keydown', A, undefined, {meta: true}); + + listOptions.slice(0, 2).forEach(option => (option.componentInstance.selected = true)); + fixture.detectChanges(); + + expect(listOptions.some(option => option.componentInstance.selected)).toBe(true); + + dispatchEvent(selectionList.nativeElement, event); + fixture.detectChanges(); + + expect(listOptions.every(option => option.componentInstance.selected)).toBe(true); + }); + + it('should deselect all with command(metaKey) + a if all options are selected', () => { + const event = createKeyboardEvent('keydown', A, undefined, {meta: true}); + + listOptions.forEach(option => (option.componentInstance.selected = true)); + fixture.detectChanges(); + + expect(listOptions.every(option => option.componentInstance.selected)).toBe(true); + + dispatchEvent(selectionList.nativeElement, event); + fixture.detectChanges(); + + expect(listOptions.every(option => option.componentInstance.selected)).toBe(false); + }); + + it('should dispatch the selectionChange event when selecting via command(metaKey) + a', () => { + const spy = spyOn(fixture.componentInstance, 'onSelectionChange'); + listOptions.forEach(option => (option.componentInstance.disabled = false)); + const event = createKeyboardEvent('keydown', A, undefined, {meta: true}); + + dispatchEvent(selectionList.nativeElement, event); + fixture.detectChanges(); + + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith( + jasmine.objectContaining({ + options: listOptions.map(option => option.componentInstance), + }), + ); + }); + it('should be able to jump focus down to an item by typing', fakeAsync(() => { const listEl = selectionList.nativeElement; const manager = selectionList.componentInstance._keyManager; diff --git a/src/material/list/selection-list.ts b/src/material/list/selection-list.ts index e7f5206d8886..60b9b9f6fd07 100644 --- a/src/material/list/selection-list.ts +++ b/src/material/list/selection-list.ts @@ -573,7 +573,7 @@ export class MatSelectionList if ( keyCode === A && this.multiple && - hasModifierKey(event, 'ctrlKey') && + hasModifierKey(event, 'ctrlKey', 'metaKey') && !manager.isTyping() ) { const shouldSelect = this.options.some(option => !option.disabled && !option.selected);