Skip to content
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

feat(cdk-experimental/menu): add context menu trigger directive #20144

Merged
merged 1 commit into from Jul 31, 2020

Conversation

andy9775
Copy link
Contributor

Add a directive which opens an attached context menu when a user right clicks within the
triggering element. The context menu trigger also considers nested context menu triggers within
an element opting to open the lowest level non-disabled context menu.

@andy9775 andy9775 requested a review from jelbourn as a code owner July 30, 2020 17:26
@googlebot googlebot added the cla: yes PR author has agreed to Google's Contributor License Agreement label Jul 30, 2020
@andy9775
Copy link
Contributor Author

This pr implements the basic context menu logic. A follow on PR will implement keyboard triggering

expect(getContextMenu()).not.toBeDefined();
});

it('should re-open the menu when right clicking twice in the context', () => {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test description leaves it ambiguous whether one or two menus are expected. The context string below does make it clearer, but maybe say 're-open the same menu' for extra clarity.

*/
function isWithinMenuElement(target: Element | null) {
while (target instanceof Element) {
if (target.className.indexOf('cdk-menu') !== -1) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can break on something like class="cdk-menu-close". You should use target.classList.contains('cdk-menu') instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All cdk menu classes are in the form of cdk-menu-* so we want to check if we've hit any directive not just the cdk-menu directive. However I could see users placing cdk-menu-foo on their own elements but not sure how likely that is. @jelbourn thoughts?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My stance is that we own the cdk- prefix and users shouldn't be making up their own cdk classes. If this breaks as a result of that, it's outside the scope of our purview.

this.opened.next();

if (this._overlayRef) {
this._overlayRef!.updatePositionStrategy(this._getOverlayPositionStrategy(coordinates));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of recreating the entire position strategy on each open, you should be able to only update the coordinates through FlexibleConnectedPositionStrategy.setOrigin.

/** Configuration options passed to the context menu. */
export type ContextMenuOptions = {
/** The opened menus X coordinate offset from the triggering position. */
menuOffsetX: number;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think just offsetX and offsetY would be good since "menu" is implied

};

/** Injection token for the ContextMenu options object. */
export const CDK_CONTEXT_MENU_OPTIONS = new InjectionToken('cdk-context-menu-options');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
export const CDK_CONTEXT_MENU_OPTIONS = new InjectionToken('cdk-context-menu-options');
export const CDK_CONTEXT_MENU_OPTIONS =
new InjectionToken<ContextMenuOptions >('cdk-context-menu-options');

};

/** Injection token for the ContextMenu options object. */
export const CDK_CONTEXT_MENU_OPTIONS = new InjectionToken('cdk-context-menu-options');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we generally name providers like this like CDK_CONTENT_MENU_DEFAULT_OPTIONS

* Open the attached menu at the specified location.
* @param coordinates where to open the context menu
*/
openMenu(coordinates: ContextMenuCoordinates) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
openMenu(coordinates: ContextMenuCoordinates) {
open(coordinates: ContextMenuCoordinates) {

src/cdk-experimental/menu/context-menu.ts Outdated Show resolved Hide resolved
}

/** Whether the attached menu is open. */
isMenuOpen() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
isMenuOpen() {
isOpen() {

src/cdk-experimental/menu/context-menu.ts Outdated Show resolved Hide resolved
src/cdk-experimental/menu/context-menu.ts Show resolved Hide resolved
* Get the portal to be attached to the overlay which contains the menu. Allows for the menu
* content to change dynamically and be reflected in the application.
*/
private _getPortal() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
private _getPortal() {
private _getMenuContent(): Portal<unknown> {

Comment on lines 277 to 282
// stop the default context menu from appearing if user right-clicked somewhere outside of
// any context menu directive or if a user right-clicked inside of the opened menu and just
// close it.
if (event.type === 'contextmenu') {
event.preventDefault();
this.closeMenu();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure this bit is necessary- I tested a few other Google apps (gmail, docs, calendar) and they don't seem to prevent native context menus from opening when you click somewhere without an open one (while you have an open custom menu)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to prevent the default context menu from opening when right clicked inside a custom context menu?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, interesting- gmail completely ignores right-clicks inside of a custom context menu while Google Docs and Calendar both just treat them as normal clicks. I would probably do what gmail does here since it seems the most predictable.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed.

So I've implemented it as:

  • if regular click occurs outside of the open context menu, close the open menu
  • if right click occurs inside the context menu, do nothing
  • if right click occurs outside of the context menu (and outside the context) close the menu and either open the new context menu or the regular context menu

@andy9775
Copy link
Contributor Author

@teflonwaffles @crisbeto @jelbourn feedback should be addressed

Add a directive which opens an attached context menu when a user right clicks within the
triggering element. The context menu trigger also considers nested context menu triggers within
an element opting to open the lowest level non-disabled context menu.
Copy link
Member

@jelbourn jelbourn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@jelbourn jelbourn added action: merge The PR is ready for merge by the caretaker merge safe target: minor This PR is targeted for the next minor release labels Jul 31, 2020
@jelbourn jelbourn merged commit d945b27 into angular:master Jul 31, 2020
@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Aug 31, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
action: merge The PR is ready for merge by the caretaker cla: yes PR author has agreed to Google's Contributor License Agreement target: minor This PR is targeted for the next minor release
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants