From c4a4f84fb7d4819a30c2ffa2152f09260c490cd3 Mon Sep 17 00:00:00 2001 From: crisbeto Date: Tue, 5 Sep 2017 19:53:55 +0200 Subject: [PATCH 1/2] fix(menu): nested trigger staying highlighted after click Prevents the sub-menu trigger from staying highlighted if the user clicks on it and moves away to another menu item. Fixes #6838. --- src/cdk/testing/event-objects.ts | 6 +++++- src/lib/menu/menu-trigger.ts | 7 +++++++ src/lib/menu/menu.spec.ts | 10 ++++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/cdk/testing/event-objects.ts b/src/cdk/testing/event-objects.ts index 1d88d7996df2..3ff72deca6d4 100644 --- a/src/cdk/testing/event-objects.ts +++ b/src/cdk/testing/event-objects.ts @@ -57,7 +57,11 @@ export function createKeyboardEvent(type: string, keyCode: number, target?: Elem /** Creates a fake event object with any desired event type. */ export function createFakeEvent(type: string) { - let event = document.createEvent('Event'); + const event = document.createEvent('Event'); event.initEvent(type, true, true); + + // IE won't set `defaultPrevented` on synthetic events so we need to do it manually. + event.preventDefault = () => Object.defineProperty(event, 'defaultPrevented', {get: () => true}); + return event; } diff --git a/src/lib/menu/menu-trigger.ts b/src/lib/menu/menu-trigger.ts index 045b872296e6..9bceb8282e80 100644 --- a/src/lib/menu/menu-trigger.ts +++ b/src/lib/menu/menu-trigger.ts @@ -413,6 +413,13 @@ export class MdMenuTrigger implements AfterViewInit, OnDestroy { _handleMousedown(event: MouseEvent): void { if (!isFakeMousedownFromScreenReader(event)) { this._openedByMouse = true; + + // Since clicking on the trigger won't close the menu if it opens a sub-menu, + // we should prevent focus from moving onto it via click to avoid the + // highlight from lingering on the menu item. + if (this.triggersSubmenu) { + event.preventDefault(); + } } } diff --git a/src/lib/menu/menu.spec.ts b/src/lib/menu/menu.spec.ts index 6006dcab4f6b..81c40adecf0a 100644 --- a/src/lib/menu/menu.spec.ts +++ b/src/lib/menu/menu.spec.ts @@ -29,6 +29,7 @@ import { dispatchMouseEvent, dispatchEvent, createKeyboardEvent, + dispatchFakeEvent, } from '@angular/cdk/testing'; @@ -1014,6 +1015,15 @@ describe('MdMenu', () => { expect(overlay.querySelectorAll('.mat-menu-panel').length).toBe(2, 'Expected two open menus'); })); + it('should prevent the default mousedown action if the menu item opens a sub-menu', () => { + compileTestComponent(); + instance.rootTrigger.openMenu(); + fixture.detectChanges(); + + const event = dispatchFakeEvent(overlay.querySelector('[md-menu-item]')!, 'mousedown'); + expect(event.defaultPrevented).toBe(true); + }); + }); }); From 0ce13d0e20f3098a5947646846ca18171022fbcb Mon Sep 17 00:00:00 2001 From: crisbeto Date: Tue, 12 Sep 2017 20:44:26 +0200 Subject: [PATCH 2/2] fix: firefox tests crashing --- src/cdk/testing/dispatch-events.ts | 5 +++-- src/cdk/testing/event-objects.ts | 4 ---- src/lib/menu/menu.spec.ts | 11 ++++++++--- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/cdk/testing/dispatch-events.ts b/src/cdk/testing/dispatch-events.ts index 6e7739c22454..f815fce1a4b9 100644 --- a/src/cdk/testing/dispatch-events.ts +++ b/src/cdk/testing/dispatch-events.ts @@ -25,6 +25,7 @@ export function dispatchKeyboardEvent(node: Node, type: string, keyCode: number) } /** Shorthand to dispatch a mouse event on the specified coordinates. */ -export function dispatchMouseEvent(node: Node, type: string, x = 0, y = 0): MouseEvent { - return dispatchEvent(node, createMouseEvent(type, x, y)) as MouseEvent; +export function dispatchMouseEvent(node: Node, type: string, x = 0, y = 0, + event = createMouseEvent(type, x, y)): MouseEvent { + return dispatchEvent(node, event) as MouseEvent; } diff --git a/src/cdk/testing/event-objects.ts b/src/cdk/testing/event-objects.ts index 3ff72deca6d4..856aad2f09f4 100644 --- a/src/cdk/testing/event-objects.ts +++ b/src/cdk/testing/event-objects.ts @@ -59,9 +59,5 @@ export function createKeyboardEvent(type: string, keyCode: number, target?: Elem export function createFakeEvent(type: string) { const event = document.createEvent('Event'); event.initEvent(type, true, true); - - // IE won't set `defaultPrevented` on synthetic events so we need to do it manually. - event.preventDefault = () => Object.defineProperty(event, 'defaultPrevented', {get: () => true}); - return event; } diff --git a/src/lib/menu/menu.spec.ts b/src/lib/menu/menu.spec.ts index 81c40adecf0a..a9e8086aaa5f 100644 --- a/src/lib/menu/menu.spec.ts +++ b/src/lib/menu/menu.spec.ts @@ -29,7 +29,7 @@ import { dispatchMouseEvent, dispatchEvent, createKeyboardEvent, - dispatchFakeEvent, + createMouseEvent, } from '@angular/cdk/testing'; @@ -1020,8 +1020,13 @@ describe('MdMenu', () => { instance.rootTrigger.openMenu(); fixture.detectChanges(); - const event = dispatchFakeEvent(overlay.querySelector('[md-menu-item]')!, 'mousedown'); - expect(event.defaultPrevented).toBe(true); + const event = createMouseEvent('mousedown'); + + Object.defineProperty(event, 'buttons', {get: () => 1}); + event.preventDefault = jasmine.createSpy('preventDefault spy'); + + dispatchMouseEvent(overlay.querySelector('[md-menu-item]')!, 'mousedown', 0, 0, event); + expect(event.preventDefault).toHaveBeenCalled(); }); });