diff --git a/src/material-experimental/mdc-list/selection-list.spec.ts b/src/material-experimental/mdc-list/selection-list.spec.ts index d83e0c20795d..9229ce78d0c9 100644 --- a/src/material-experimental/mdc-list/selection-list.spec.ts +++ b/src/material-experimental/mdc-list/selection-list.spec.ts @@ -437,6 +437,87 @@ describe('MDC-based MatSelectionList without forms', () => { ); }); + it('should select all items using command(metaKey) + a', () => { + listOptions.forEach(option => (option.componentInstance.disabled = false)); + fixture.detectChanges(); + + expect(listOptions.some(option => option.componentInstance.selected)).toBe(false); + + listOptions[2].nativeElement.focus(); + dispatchKeyboardEvent(listOptions[2].nativeElement, 'keydown', A, 'A', {meta: true}); + fixture.detectChanges(); + + expect(listOptions.every(option => option.componentInstance.selected)).toBe(true); + }); + + it('should not select disabled items when pressing command(metaKey) + a', () => { + 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, + ]); + + listOptions[3].nativeElement.focus(); + dispatchKeyboardEvent(listOptions[3].nativeElement, 'keydown', A, 'A', {meta: true}); + 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', () => { + listOptions.slice(0, 2).forEach(option => (option.componentInstance.selected = true)); + fixture.detectChanges(); + + expect(listOptions.some(option => option.componentInstance.selected)).toBe(true); + + listOptions[2].nativeElement.focus(); + dispatchKeyboardEvent(listOptions[2].nativeElement, 'keydown', A, 'A', {meta: true}); + fixture.detectChanges(); + + expect(listOptions.every(option => option.componentInstance.selected)).toBe(true); + }); + + it('should deselect all with command(metaKey) + a if all options are selected', () => { + listOptions.forEach(option => (option.componentInstance.selected = true)); + fixture.detectChanges(); + + expect(listOptions.every(option => option.componentInstance.selected)).toBe(true); + + listOptions[2].nativeElement.focus(); + dispatchKeyboardEvent(listOptions[2].nativeElement, 'keydown', A, 'A', {meta: true}); + 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)); + fixture.detectChanges(); + + listOptions[2].nativeElement.focus(); + dispatchKeyboardEvent(listOptions[2].nativeElement, 'keydown', A, 'A', {meta: true}); + 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 firstOption = listOptions[0].nativeElement; 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);