-
Notifications
You must be signed in to change notification settings - Fork 2.2k
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
Submenu flipping with Popper.js! #2053
Conversation
…cade to MenuItem via context fn (better than cloning children).
more docsPreview: documentation | landing | table |
getSubmenuPopperModifiers: assertFunctionProp, | ||
}; | ||
|
||
// simple alternative to prop-types dependency |
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.
I don't see the point to this -- we're already depending on prop-types
implicitly through react
. You should use the package directly and also remove this code:
// simple alternative to prop-types dependency |
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.
well dang, i didn't realize react depended on it!
blueprint git:(gg/submenus) yarn why prop-types
yarn why v1.3.2
[1/4] 🤔 Why do we have the module "prop-types"...?
[2/4] 🚚 Initialising dependency graph...
[3/4] 🔍 Finding dependency...
[4/4] 🚡 Calculating file sizes...
info Reasons this module exists
- "@blueprintjs/core#react-popper" depends on it
- "@blueprintjs/core#react" depends on it
- "@blueprintjs/core#react-transition-group" depends on it
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.
no-implicit-dependency-wise, is prop-types
a new peerDep, alongside react
?
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.
no, it's a regular dependency, not a peerDep.
`Menu` provides two `submenu` props to adjust this flipping behavior: you can customize the boundary element | ||
and the padding within that boundary element. Note that a `MenuItem` _must be inside_ a `Menu` element | ||
for these props to affect the submenus. On standalone `MenuItem`s (without a parent `Menu`, an anti-pattern) you can | ||
use `popoverProps.modifiers` directly to achieve the same result. |
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.
If it's an anti-pattern, why even mention it?
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.
yeah i had this exact thought. i figure it's going to happen (is already happening) so why not pass judgment on it.
@@ -83,25 +83,34 @@ To add a submenu to a `Menu`, simply nest `MenuItem`s within another `MenuItem`. | |||
The submenu opens to the right of its parent by default, but will adjust and flip to the left if | |||
there is not enough room to the right. | |||
|
|||
`Menu` provides two `submenu` props to adjust this flipping behavior: you can customize the boundary element | |||
and the padding within that boundary element. Note that a `MenuItem` _must be inside_ a `Menu` element |
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.
When would I need to customize these things? Genuinely curious.
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.
you might want to keep your Menu
inside a container other than the viewport, maybe on a canvas
. padding i think is less useful and less likely to be used so i could be convinced to remove it, but it's easy enough to support.
} | ||
// NOTE: use .pt-menu directly because using Menu would start a new context tree and | ||
// we'd have to pass through the submenu props. this is just simpler. | ||
const submenuContent = <ul className={Classes.MENU}>{this.renderChildren()}</ul>; |
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.
if this.renderChildren()
returns null, wouldn't you want to avoid rendering this <ul>
and <Popover>
altogether?
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.
hmm yes, makes sense! surprising that never came up.
private renderPopover(content: JSX.Element) { | ||
const { disabled, popoverProps } = this.props; | ||
const popoverClasses = classNames(Classes.MENU_SUBMENU, popoverProps.popoverClassName); | ||
// getSubmenuPopperModifiers will not be defined if `MenuItem` used outside a `Menu`. |
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 we throw an error in that case?
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.
🤔 i'm hesitant to throw errors in cases where it's not absolutely fatal. pretty easy to handle the no-context case here.
submenuLeft += adjustmentWidth; | ||
submenuRight += adjustmentWidth; | ||
} | ||
// NOTE: use .pt-menu directly because using Menu would start a new context tree and |
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.
What do you mean by new context tree? And by "the submenu props" do you mean submenuBoundaryElement
and submenuBoundaryPadding
?
This feels dirty, I'd rather use <Menu>
. You can derive the submenu info you need from props and context in the component constructor.
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.
new context tree: the nested Menu
overwrites the same context
key with a function that returns its default props, not the parent's. by not using Menu
, the root context gets passed all the way down.
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 doable with Menu
, i'll update the usage so you can see. don't need to do any constructor stuff, i can just pass parent props down.
use Menu and pass context propsPreview: documentation | landing | table |
|
@@ -4,21 +4,12 @@ | |||
* Licensed under the terms of the LICENSE file distributed with this project. | |||
*/ | |||
|
|||
import { isFunction } from "../../common/utils"; | |||
import { func } from "prop-types"; |
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.
not a fan of this import, the symbols are too terse. for example another possible import is any
, which would be very confusing to import. please switch to import * as PropTypes
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.
yeah i felt the terseness. went with this for theoretical tree-shaking purposes, but i agree the * as
import makes a lot more sense here.
import { | ||
IBlock, | ||
IKssPluginData, | ||
IMarkdownPluginData, | ||
ITsDocBase, | ||
ITypescriptPluginData, | ||
} from "documentalist/dist/client"; | ||
import { func } from "prop-types"; |
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.
same here, use namespace import
}); | ||
const { disabled, label } = this.props; | ||
const submenuChildren = this.renderSubmenuChildren(); | ||
const hasSubmenu = submenuChildren != null; |
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.
nit: only used once, can be inlined
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.
used twice, or i would have already inlined it 😄 (other usage is hidden in collapsed block, line 66)
@adidahiya ok, i'm onboard to remove the two props. the only real benefit they conferred was the cascading behavior to all nested submenus. the behavior is still entirely possible through |
remove submenu props docsPreview: documentation | landing | table |
Merge branch 'develop' of github.com:palantir/blueprint into gg/submenusPreview: documentation | landing | table |
delete menu testPreview: documentation | landing | table |
#2058 should fix test failures here. |
Merge branch 'develop' of github.com:palantir/blueprint into gg/submenusPreview: documentation | landing | table |
/** Props to spread to `Popover`. Note that `content` cannot be changed. */ | ||
popoverProps?: Partial<IPopoverProps> & object; | ||
/** Props to spread to `Popover`. Note that `content` and `minimal` cannot be changed. */ | ||
popoverProps?: Partial<IPopoverProps>; |
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.
& object
was there intentionally... IIRC without it, you can assign literally anything non-null to this field.
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.
button menu items inherit fontPreview: documentation | landing | table |
improve Overlay focus tests reliabilityPreview: documentation | landing | table |
Fixes #849, Fixes #1640, Fixes #2008
Changes proposed in this pull request:
MenuItem
useSmartPositioning
andsubmenuViewportMargin
props, and all associated logic, asPopper.js
beautifully handles flipping when a menu reaches the edge.MenuItem
popoverProps.modifiers
Screenshot
notice how the menu never leaves the viewport (or causes it to scroll #849)!!