-
Notifications
You must be signed in to change notification settings - Fork 6.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(menu): multiple close events for a single close #6961
Conversation
Don't emit a closed event when another event will be emitted. Previously, if one clicked on a menu item, one would get two events: `undefined` and `click` in that order. One would see similar behavior for `keydown` or clicking the backdrop. Unit tests were updated to prevent a regression.
src/lib/menu/menu-directive.ts
Outdated
@@ -139,7 +139,7 @@ export class MdMenu implements AfterContentInit, MdMenuPanel, OnDestroy { | |||
} | |||
|
|||
/** Event emitted when the menu is closed. */ | |||
@Output() close = new EventEmitter<void | 'click' | 'keydown'>(); | |||
@Output() close = new EventEmitter<void | 'click' | 'destroy' | 'keydown'>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The idea with this one was to emit the reason only if it's user-generated. Given that there is no special behavior for destroy
, can you revert it back to void | 'click' | 'keydown'
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. I was also wondering if it's a bug that destroy will emit a close event when the menu is already closed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's there to notify any child menus to close.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But child menus won't be open unless the menu itself is open? Anyway, I'm more than happy to leave it as it is, as it doesn't really affect any use cases that I can think of.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In most cases the children won't be open without a parent, but it's possible for the parent to be destroyed while the children are still open.
src/lib/menu/menu-trigger.ts
Outdated
* Gives the option of setting emitEvent to false to avoid emitting an event | ||
* when an event will already be emitted. | ||
*/ | ||
private _closeMenu(emitEvent: boolean) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rather than having the emitEvent
parameter, you can remove the this.menu.close.emit
call from here and move it only to closeMenu
. Also change the doc string to something along the lines of "Closes the menu and does the necessary cleanup".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. Should I only emit the event when the menu is already open, though?
src/lib/menu/menu.spec.ts
Outdated
@@ -485,6 +485,7 @@ describe('MdMenu', () => { | |||
fixture = TestBed.createComponent(SimpleMenu); | |||
fixture.detectChanges(); | |||
fixture.componentInstance.trigger.openMenu(); | |||
fixture.componentInstance.closeCallback.calls.reset(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this necessary? The callbacks should be reset after each test, because the component is re-compiled.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They were necessary when because Observable.of(null) was triggering a close event. Replaced with Observable.empty()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Observable.of(null)
will emit null
immediately. Observable.of()
won't.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you saying that I should change it to Observable.of() instead of Observable.empty()? To me, Observable.empty() is more readable and makes the intent more clear.
But I don't mind change it to Observable.of() since the behavior would be the same as Observable.empty().
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Observable.empty() is definitely valid in this case. I just want to avoid bringing in an extra symbol from RxJS.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed to Observable.of()
src/lib/menu/menu.spec.ts
Outdated
expect(fixture.componentInstance.closeCallback).toHaveBeenCalledTimes(1); | ||
}); | ||
|
||
it('should emit an event when escaped', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"should emit an event when pressing ESCAPE".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
src/lib/menu/menu.spec.ts
Outdated
@@ -989,11 +1004,18 @@ describe('MdMenu', () => { | |||
|
|||
expect(menus.length).toBe(3, 'Expected three open menus'); | |||
|
|||
instance.rootCloseCallback.calls.reset(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These reset calls shouldn't be necessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They were necessary when because Observable.of(null) was triggering a close event. Replaced with Observable.empty()
src/lib/menu/menu-trigger.ts
Outdated
/** | ||
* Closes the menu and does the necessary cleanup. | ||
*/ | ||
private _closeMenu() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One last nitpick: can you rename this to _destroyMenu
to avoid confusion with closeMenu
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
_closeMenu -> _destroyMenu
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, the two CI failures will be resolved by #6956.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
* Revert "Revert "fix(menu): multiple close events for a single close" (#7036)" This reverts commit dcfe515. * Revert "feat(datepicker): Add Moment.js adapter (#6860)" This reverts commit 9545427. * Revert "fix(menu): multiple close events for a single close (#6961)" This reverts commit 1cccd4b. * Revert "fix(menu): nested trigger staying highlighted after click (#6853)" This reverts commit 04bf3d1. * Revert "feat(viewport-ruler): add common window resize handler (#6680)" This reverts commit 881630f.
* fix(menu): multiple close events for a single close Don't emit a closed event when another event will be emitted. Previously, if one clicked on a menu item, one would get two events: `undefined` and `click` in that order. One would see similar behavior for `keydown` or clicking the backdrop. Unit tests were updated to prevent a regression. * Responding to review comments from @crisbeto * Observable.empty() -> Observable.of() _closeMenu -> _destroyMenu
…#7036) * Revert "feat(datepicker): Add Moment.js adapter (angular#6860)" This reverts commit 9545427. * Revert "fix(menu): multiple close events for a single close (angular#6961)" This reverts commit 1cccd4b.
…lar#7054) * Revert "Revert "fix(menu): multiple close events for a single close" (angular#7036)" This reverts commit dcfe515. * Revert "feat(datepicker): Add Moment.js adapter (angular#6860)" This reverts commit 9545427. * Revert "fix(menu): multiple close events for a single close (angular#6961)" This reverts commit 1cccd4b. * Revert "fix(menu): nested trigger staying highlighted after click (angular#6853)" This reverts commit 04bf3d1. * Revert "feat(viewport-ruler): add common window resize handler (angular#6680)" This reverts commit 881630f.
This issue has been automatically locked due to inactivity. Read more about our automatic conversation locking policy. This action has been performed automatically by a bot. |
Don't emit a closed event when another event will be emitted. Previously, if one clicked on a menu item, one would get two events:
undefined
andclick
in that order. One would see similar behavior forkeydown
or clicking the backdrop. Unit tests were updated to prevent a regression.