-
Notifications
You must be signed in to change notification settings - Fork 843
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
Refactor EuiPopover to use new positioning service #948
Refactor EuiPopover to use new positioning service #948
Conversation
Biggest thing I saw here was that i'd be nice if we can recalculate position on window resize. Right now if you resize the page while the popover is open, the popover retains its original position. I don't consider this a blocker, and given a choice between our current functionality where this thing breaks and doesn't work in overflows, I'd rather see a follow up PR (if we think it would take awhile to fix) so that the main fix gets in quickly. Either way I'd assume @cchaos or I would do a design PR as as a fast followup to fix the arrow coloring issue. |
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.
Code LGTM. I had a few small suggestions but I also noticed some bugs on the ContextMenu page.
I expected the first example to move above the button when the window was very short, but instead it stays below the button and gets clipped:
The last example appears over the button, even when there seems to be enough room below it:
src/components/popover/popover.js
Outdated
let arrowStyles = {}; | ||
let arrowPosition = null; | ||
if (node != null) { | ||
// panel is coming into existance |
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.
Typo: existence
src/components/popover/popover.js
Outdated
'left': 'left' | ||
}; | ||
function getPopoverPositionFromAnchorPosition(anchorPosition) { | ||
const [, primaryPosition] = anchorPosition.match(/^(.*?)[A-Z]/); |
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 not going to lie... I had to run this through regex101 to figure out what it was doing and understand how we're mapping anchor position to popover position.
I both like and dislike this code because it's clever. 😄 But I would really find something like this much more understandable:
const anchorPositionToPopoverPositionMap = {
'upCenter': 'top',
'upLeft': 'top',
'upRight': 'top',
'downCenter': 'bottom',
'downLeft': 'bottom',
'downRight': 'bottom',
'leftCenter': 'left',
'leftUp': 'left',
'leftDown': 'left',
'rightCenter': 'right',
'rightUp': 'right',
'rightDown': 'right',
};
function getPopoverPositionFromAnchorPosition(anchorPosition) {
return anchorPositionToPopoverPositionMap[anchorPosition];
}
WDYT?
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 disagree about it being clever; I did consider doing something like this, but my preference for some kind of parsing vs. a lookup mapping is because the target information isn't correlated to the input, it is itself encoded in the input. Parsing it out enforces that relationship. I'll add a comment to each function describing what the regex is doing.
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.
and I'll include tests for both!
@@ -22,9 +16,6 @@ export class EuiPortal extends Component { | |||
} = this.props; | |||
|
|||
this.portalNode = document.createElement('div'); | |||
} | |||
|
|||
componentDidMount() { |
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.
Nice!
Might be worth auditing the other examples for cases where popover is being used and making sure they look right. |
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.
After talking this over with Chandler, I understand his perspective but I still disagree. That said, I don't feel strongly about this point so I give this code my LGTM. 😄
src/components/popover/popover.js
Outdated
// extract the first positional word from anchorPosition: | ||
// starts at the beginning (" ^ ") of anchorPosition and | ||
// captures all of the characters (" (.*?) ") until the | ||
// first capital letter (" [A-Z] ") is encountered |
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 think the "why" (instead of "what") would be very helpful here too. Something like this:
// Pull out the part of the anchorPosition string that indicates cardinal position,
// e.g. "upCenter" -> "center", "leftUp" -> "up".
@cchaos that's on us. It existed in the old one too. Has to do with the extra div. We can fix that in a follow up. |
@cjcenizal the ContextMenu issues are related to that component's timing in rendering content vs. positioning the popover. When the popover is created, the context panel has a height of only 2 pixels, so the initial placement is correct. Then more content comes in which screws everything up. I'm going to pull this out into a separate issue because of that component's complexity, and I expect it to grow into a more general approach for handling dynamic child content. |
} | ||
}; | ||
|
||
buttonRef = node => this.button = node; |
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's this for? I only see it being added a new div wrapper around the actual button.
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 used to identify the bounding box of the anchor button (line 177)
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.
@cchaos I've added |
607f0a9
to
4215a8d
Compare
@cjcenizal fixed the problem where interacting with the popover would close it; issue was that the click outside detector wasn't respecting content rendered through a child portal, so I modified how the click detection works. Please review these changes & comments. |
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.
With the context menu fix I'm good with merging this. Tested it in multiple browsers.
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.
@chandlerprall Great solution to the onClickOutside
problem! Code LGTM. Thanks for the very clear explanation in the comments.
Closes #873
Refactored
EuiPopover
to use portals and take advantage of the positioning service. The service had to be expanded to include analign
parameter to allow shifting content into a given corner if there's room.