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(menu): Added ability to show the menu overlay around the menu trigger #1771

Merged
merged 1 commit into from
Jan 20, 2017

Conversation

trshafer
Copy link
Contributor

@trshafer trshafer commented Nov 8, 2016

Currently the menu is fixed to show over the menu trigger.

This adds a (rather blunt) boolean to decide if the menu should be shown above or below the trigger. I thought about changing the possibilities to overlay-position: default | above | below | left | right; But that felt like a bit too much flexibility. I think some version of this feature is really important for those designing their own menu. Let me know what you think.

Thanks,
Thomas

Here is the menu below the trigger:
Menu Below

Here is the menu above the trigger:
Menu Above

@googlebot googlebot added the cla: yes PR author has agreed to Google's Contributor License Agreement label Nov 8, 2016
@trshafer trshafer force-pushed the menu-positioning branch 2 times, most recently from 56de2a8 to 72979c7 Compare November 11, 2016 17:14
using the `x-position` (`before | after`) and `y-position` (`above | below`) attributes.
By default, the menu will display after and below its trigger. You can change this display position
using the `x-position` (`before | after`) and `y-position` (`above | below`) attributes. The menu
can be positioned over the menu button or outside using `overlay-trigger` (`true | false`).
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 a fan of this name ("overlay-trigger")

@kara thoughts?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I totally agree. It's also not very flexible. Happy to move it to something other than a boolean, perhaps "cover", "horizontal", and "vertical"? I dunno, those are pretty bad too.

Copy link
Contributor

Choose a reason for hiding this comment

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

How about "overlap-trigger"? "Overlay-trigger" might sound like the trigger itself is an overlay.

@jelbourn
Copy link
Member

R: @kara

Copy link
Contributor

@kara kara left a comment

Choose a reason for hiding this comment

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

Can you add tests for this?


@ViewChild(TemplateRef) templateRef: TemplateRef<any>;
@ContentChildren(MdMenuItem) items: QueryList<MdMenuItem>;

constructor(@Attribute('x-position') posX: MenuPositionX,
@Attribute('y-position') posY: MenuPositionY) {
@Attribute('y-position') posY: MenuPositionY,
@Attribute('overlay-trigger') trigger = true) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Have you considered using the public keyword instead? That way you don't have to explicitly set it again on line 60.

@Attribute('overlap-trigger') public overlapTrigger = true

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the suggestion.

@@ -148,7 +149,8 @@ also adds `aria-hasPopup="true"` to the trigger element.
| --- | --- | --- |
| `x-position` | `before | after` | The horizontal position of the menu in relation to the trigger. Defaults to `after`. |
| `y-position` | `above | below` | The vertical position of the menu in relation to the trigger. Defaults to `below`. |

| `overlay-trigger` | `true | false` | Whether to have the menu show on top of the menu trigger or outside. Defaults to `true`. |
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we rename to overlapTrigger?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

let originY = overlayY;
if (this.menu.overlayTrigger) {
originY = overlayY === 'top' ? 'bottom' : 'top';
}
Copy link
Contributor

Choose a reason for hiding this comment

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

If you want to overlap the trigger, shouldn't the originY and the overlayY be the same? e.g. the top of the trigger should connect to the top of the overlay or the bottom of the trigger should connect to the bottom of the overlay. Let me know if I'm missing something.

let originY: VerticalConnectionPos;
if (this.menu.overlapTrigger) {
   originY = overlayY;
} else {
   originY = overlayY === 'top' ? 'bottom' : 'top';
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

One would think so. However I believe the current implementation produces the desired effect. Are you able to clone the pr and run the demo-app and verify? I have done so and I believe the functionality is accurate.

@trshafer
Copy link
Contributor Author

trshafer commented Jan 4, 2017

Not sure why it says the checks have passed, the tests are failing on my local machine. I'm working on it.

@trshafer
Copy link
Contributor Author

trshafer commented Jan 4, 2017

Ah, looks like there's some test bleedover. If I just focus on the fallback positions describe block using fdescribe, those tests will fail. If I focus on the whole menu test then the fallback positions test passes. Tested this on master.

@trshafer
Copy link
Contributor Author

trshafer commented Jan 5, 2017

I have logged the issue: #2530.

@trshafer
Copy link
Contributor Author

trshafer commented Jan 5, 2017

Hey @kara, I'm happy to write tests for the updated change. However since the tests are currently failing I don't feel confident in updating the tests or making new ones without potentially causing issues. Are you able to update the tests? I could attempt to fix the tests, however since I don't have the background knowledge in those tests, I thought it would be better to defer to the original author. Looks like they were introduced here: 7572e34

@kara
Copy link
Contributor

kara commented Jan 5, 2017

@trshafer We won't be able to merge this PR without tests; it's standard practice for this repo to provide tests with your code. Go ahead and give it a shot. I can take a look at the other issue you found in the meantime.

@trshafer
Copy link
Contributor Author

trshafer commented Jan 5, 2017

Totally. Happy to write tests. I should have red, green, refactored my way to this PR. Starting from a red test is harder for me to confidently ensure the test and change I have written is correct. It would be great if the menu test suite was green before I start.

I've also rebased in the recent commit.

@kara
Copy link
Contributor

kara commented Jan 5, 2017

Yep, that makes sense. I'll ping you once I've looked at the the existing tests.

@trshafer trshafer force-pushed the menu-positioning branch 3 times, most recently from 659775a to 5373c66 Compare January 9, 2017 22:14
@trshafer
Copy link
Contributor Author

trshafer commented Jan 9, 2017

Hey @kara, thanks for submitting the test fixes. I have written two tests for the overlap change.

I made an OverlapSubject class to reduce the testing duplication. Currently its visibility is limited to the new test describe block, however it might be helpful for the other tests as well.

@@ -115,7 +115,8 @@ Output:
### Customizing menu position

By default, the menu will display after and below its trigger. You can change this display position
using the `x-position` (`before | after`) and `y-position` (`above | below`) attributes.
using the `x-position` (`before | after`) and `y-position` (`above | below`) attributes. The menu
can be positioned over the menu button or outside using `overlap-trigger` (`true | false`).
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you update these in the OVERVIEW as well? The docs are generated from those files now, and the READMEs are probably going away soon.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated. Let me know if there is more documentation I should add.

}

updateTriggerStyle(style: any) {
return Object.assign(this.trigger.style, style);
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you use extendObject from core 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.

Done.

`
})
class NonOverlapMenu implements TestableMenu {
@ViewChild(MdMenuTrigger) trigger: MdMenuTrigger;
Copy link
Contributor

Choose a reason for hiding this comment

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

It doesn't look like you're using this. Remove?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

I see. Totally missed that!

// Since the menu is below the trigger, the overlay top should be the trigger bottom.
expect(Math.round(subject.overlayRect.top))
.toBe(Math.round(subject.triggerRect.bottom),
`Expected menu to open in "above" position if "below" position wouldn't fit.`);
Copy link
Contributor

Choose a reason for hiding this comment

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

Copy paste error? I think it should be "Expected menu to open in "below" position by default."

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

@@ -256,6 +256,66 @@ describe('MdMenu', () => {
}
});

describe('not overlapping', () => {
class OverlapSubject<T extends TestableMenu> {
Copy link
Contributor

Choose a reason for hiding this comment

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

Add comment describing class?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

@@ -54,7 +54,8 @@ export class MdMenu implements AfterContentInit, MdMenuPanel, OnDestroy {
@ContentChildren(MdMenuItem) items: QueryList<MdMenuItem>;

constructor(@Attribute('x-position') posX: MenuPositionX,
@Attribute('y-position') posY: MenuPositionY) {
@Attribute('y-position') posY: MenuPositionY,
@Attribute('overlap-trigger') public overlapTrigger = true) {
if (posX) { this._setPositionX(posX); }
if (posY) { this._setPositionY(posY); }
Copy link
Contributor

Choose a reason for hiding this comment

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

You'll need to update the positionY when the trigger is not overlapping, otherwise the animation origin will be the wrong side of the menu. If you slow the animation down to 10%, you'll see it's animating from the wrong direction.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch. I have updated the menu positioning classes and added a test.


if (this.menu.overlapTrigger) {
originY = overlayY === 'top' ? 'bottom' : 'top';
fallbackOriginY = fallbackOverlayY === 'top' ? 'bottom' : 'top';
Copy link
Contributor

@kara kara Jan 12, 2017

Choose a reason for hiding this comment

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

Returning to our previous discussion, this logic is actually backwards. The originY should match the overlayY when the menu is overlapping. When it's not overlapping, the top of the overlay should hit the bottom of the trigger, etc.

The reason that this happens to work in the demo and the tests is because overlapTrigger is a string. Strings are truthy, so if you set overlapTrigger to anything (including "false"), this.menu.overlapTrigger will be truthy and execute this overlap block. When you don't set the attribute (like in the original tests/demos), this.menu.overlapTrigger is null, so it doesn't default to true and doesn't hit this condition. If you set overlapTrigger="true" in the demo, you'll see that all values for overlapTrigger will make the menu not overlap, including "true".

I'd coerce this.menu.overlapTrigger to a boolean in menu-directive.ts, and invert the logic here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You are absolutely correct. The test of an explicit overlapTrigger = true is a good suggestion and exposes this in a test. Thanks for pushing back. Fixed.

.toBe(Math.round(subject.triggerRect.top),
`Expected menu to open in "above" position if "below" position wouldn't fit.`);
});
});
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you add a test for when overlapTrigger="true"?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

@kara kara removed their assignment Jan 17, 2017
@trshafer trshafer force-pushed the menu-positioning branch 4 times, most recently from b855e4f to ea72a57 Compare January 18, 2017 22:56
@trshafer
Copy link
Contributor Author

Hello @kara, thank you for the thorough review. I'm pretty sure I have responded to all of your comments. Let me know what else I can do to help move this across the finish line.

Copy link
Contributor

@kara kara left a comment

Choose a reason for hiding this comment

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

LGTM

@kara kara added pr: lgtm action: merge The PR is ready for merge by the caretaker and removed pr: needs review labels Jan 19, 2017
Copy link
Contributor

@mmalerba mmalerba left a comment

Choose a reason for hiding this comment

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

When I try to merge this into Google's codebase I get the following error:
Class 'Menu' incorrectly implements interface 'MdMenuPanel'.
Property 'overlapTrigger' is missing in type 'Menu'.

@@ -52,6 +52,7 @@ export class MdMenu implements AfterContentInit, MdMenuPanel, OnDestroy {

@ViewChild(TemplateRef) templateRef: TemplateRef<any>;
@ContentChildren(MdMenuItem) items: QueryList<MdMenuItem>;
@Input('overlap-trigger') overlapTrigger = true;
Copy link
Contributor

Choose a reason for hiding this comment

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

Don't need to rename to dash-case, we're trying to switch everything to camelCase

Copy link
Contributor Author

Choose a reason for hiding this comment

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

so should the api look like <md-menu [overlapTrigger]="false">?

@mmalerba mmalerba added in progress This issue is currently in progress cla: yes PR author has agreed to Google's Contributor License Agreement and removed cla: yes PR author has agreed to Google's Contributor License Agreement action: merge The PR is ready for merge by the caretaker pr: lgtm labels Jan 20, 2017
@trshafer trshafer force-pushed the menu-positioning branch 2 times, most recently from 26234eb to b8d6a51 Compare January 20, 2017 18:40
@trshafer
Copy link
Contributor Author

Switched overlap-trigger to overlapTrigger.

@@ -52,6 +52,7 @@ export class MdMenu implements AfterContentInit, MdMenuPanel, OnDestroy {

@ViewChild(TemplateRef) templateRef: TemplateRef<any>;
@ContentChildren(MdMenuItem) items: QueryList<MdMenuItem>;
@Input('overlapTrigger') overlapTrigger = true;
Copy link
Contributor

@mmalerba mmalerba Jan 20, 2017

Choose a reason for hiding this comment

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

tiny nit: if the variable and the html attr have the same name you can omit the alias:
@Input() overlapTrigger = true;

Copy link
Contributor Author

@trshafer trshafer Jan 20, 2017

Choose a reason for hiding this comment

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

Done, here and in the test.

@mmalerba mmalerba added pr: lgtm action: merge The PR is ready for merge by the caretaker and removed in progress This issue is currently in progress labels Jan 20, 2017
@mmalerba mmalerba merged commit 592f33f into angular:master Jan 20, 2017
@trshafer trshafer deleted the menu-positioning branch January 22, 2017 01:38
@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 Sep 6, 2019
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
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants