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

New popover positioning service + updated EuiToolTip #924

Merged
merged 19 commits into from
Jun 19, 2018

Conversation

chandlerprall
Copy link
Contributor

@chandlerprall chandlerprall commented Jun 13, 2018

Fixes #610

  • browser tested in latest Chrome, FireFox, & Safari; tested in IE11 and Edge

Popover positioning service

Purpose is to replace & enhance existing calculatePopoverPosition

  • fix IE11 bugs
  • address some general mis-alignment issues
  • allow React elements OR HTML DOM nodes to be used as anchor & content (for canvas & Kibana top nav)
  • reported positioning is more flexible - e.g. topLeft, topCenter, and topRight instead of just top
  • intelligent position selection when the desired location doesn't have room
  • popover content can shift along the cross-axis to find space in the desired location
  • container element can be passed which replaces the document window as the boundary to keep the popover within

EuiToolTip

Refactored to use the new service. This was a small change to allow passing the tooltip content node between React components instead of relying on EuiToolTipPopover to pass the client rect. Previous functionality is maintained to avoid a breaking change.

tooltip repositioning

Todo after this PR

@@ -58,7 +58,7 @@ module.exports = {

devServer: {
contentBase: 'src-docs/build',
host: 'localhost',
host: '0.0.0.0',
Copy link
Contributor Author

Choose a reason for hiding this comment

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

this change allowed me to test from IE VM

Copy link
Contributor

Choose a reason for hiding this comment

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

:( I wasn't able to get this to work in a Virtual Box

@cchaos
Copy link
Contributor

cchaos commented Jun 13, 2018

reported positioning is more flexible - e.g. topLeft, topCenter, and topRight instead of just top

Just taking a first quick glance, does this mean if you send "top" it defaults to "topCenter" unless there isn't enough room to center and then it might be "topLeft" or "topRight"? This seems like appropriate behavior, but we'll then need to manipulate the positioning of the tooltip's arrow:

screen shot 2018-06-13 at 15 32 05 pm

screen shot 2018-06-13 at 15 33 04 pm

@cchaos
Copy link
Contributor

cchaos commented Jun 13, 2018

Hmm, it looks like it'll actually stick to the cross side when there's enough room on the explicit side. I'm not sure if this is the behavior we want, or if we need a way to find the side with the most room? I dunno, but here's an example where the tooltip alignment can look funky:

Copy link
Contributor

@snide snide left a comment

Choose a reason for hiding this comment

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

I did a feature leave test of this in IE/Chrome/FF/SAF. It works on all of them how I'd expect. I noticed some problems when the browser is resized, that go away after a couple hovers, but this is behavior in the current version and not a regression.

http://snid.es/0d3r1G1j1o10

I'm going to leave the code review to my javascript betters as these calculations look deep.

I did at least scan the interface for usage and it reads easy enough to me from a consumer perspective.

If we assume the docs are coming with a fairly quick followup PR it's ok to avoid that for the moment (since it closes that very bad IE bug), but we will definitely want to add those in.

@chandlerprall
Copy link
Contributor Author

chandlerprall commented Jun 13, 2018

Talked through some arrow positioning challenges with @cchaos and @snide, going to changing the shifting logic to lock it down into the 8 surrounding locations.

EDIT: see below for arrow positioning requirements

@cchaos
Copy link
Contributor

cchaos commented Jun 13, 2018

Also throwing this up here so I/we don't forget. We should make sure that we leave enough room for the pop in transition so we don't get horizontal scroll bars.

@cchaos
Copy link
Contributor

cchaos commented Jun 13, 2018

Ok, hopefully this helps illustrate the idea of positioning the arrow with the tooltip. Since we already know the dimensions of the trigger element, all we need to do is position the arrow always center of the trigger element. Here's a couple extra guidelines that should help make in work in most use cases.

group 14
This one should also help with the overflow scrollbars when animating in.

group 13

group 12

This also involves making the arrow slightly smaller. This can be achieved simply by changing the bottom: -8px to bottom: -4px on the pseudo element. Though with the positioning needing to be added via JS, that pseudo element will need to become a real element.

:pinocchio:

@chandlerprall
Copy link
Contributor Author

no closer than 16px to edge of the window

@cchaos should that be a hard coded 16px or a default that can be overridden depending on use case?

@snide two outstanding (and pre-existing) issues with the tooltips we've uncovered: 1. re-positioning the tooltip after resizing content 2. scrollbars jump a bit on some layouts ; I'd like to push addressing those to their own fast-follow PR. Thoughts?

@cchaos
Copy link
Contributor

cchaos commented Jun 14, 2018

I think the 16px should be hard-coded. I can't see any reason why it would be necessary to make the tooltips closer to the edge of the window. Also that will fix your no. 2 issue of the scrollbars coming in during the animation.

@chandlerprall
Copy link
Contributor Author

@cchaos Hard coding sounds good to me. It may fix some scroll bar issues, but there's sometimes a quick flash of vertical scroll bars changing as the content's added to the bottom of the page before being re-positioned.

@snide
Copy link
Contributor

snide commented Jun 15, 2018

@chandlerprall missed your question from yesterday, but yeah, it's cool to cover them in a follow up.

@chandlerprall
Copy link
Contributor Author

Service now returns top/left values for the arrow, tooltip is refactored to use.

@cchaos please take a look at the arrow size

@cjcenizal I added more documentation to the code, everything should be good for a full look-through

@cchaos
Copy link
Contributor

cchaos commented Jun 15, 2018

@chandlerprall I pushed a commit for ya that reduces the size of the arrow a bit. Looks like it still all looks good proximity wise.

The one thing I noticed is that the proximity to the edge of the browser window doesn't take into mind the scrollbars, wah wah. But I'm ok if you need to push this to another PR.

screen shot 2018-06-15 at 15 14 13 pm

And yeah after resizing the browser, it looks like the tooltip arrow is still being positioned correctly but the tooltip content is not.

screen shot 2018-06-15 at 15 13 01 pm

screen shot 2018-06-15 at 15 13 13 pm

chandlerprall and others added 3 commits June 18, 2018 10:37
…steps:

1) Find original cross-axis position.
2) Apply shift to cross-axis position, if necessary.
3) Find primary axis position.
Copy link
Contributor

@cjcenizal cjcenizal left a comment

Choose a reason for hiding this comment

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

Awesome work! I had some suggestions to improve the test readability, but other than that this looks fantastic.

const containerBoundingBox = { top: 10, right: 90, bottom: 190, left: 25 };

it('reports all empty space', () => {
expect(getAvailableSpace(anchorBoundingBox, containerBoundingBox, 0, 0, 'top')).toEqual({
Copy link
Contributor

Choose a reason for hiding this comment

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

I was a little thrown off by the arguments for buffer, offset, and offsetSide because I couldn't see how they were relevant to the assertion being made. I think this test would be simpler if it was just calling getAvailableSpace(anchorBoundingBox, containerBoundingBox). We could do this by adding some defaults:

export function getAvailableSpace(anchorBoundingBox, containerBoundingBox, buffer = 0, offset = 0, offsetSide = undefined) {

What do you think?

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 like keeping these as required arguments. It's an internal function and I think its interface should have some rigidity to force any implementers to think about the parameters,

// 80h x 65w
const containerBoundingBox = { top: 10, right: 90, bottom: 190, left: 25 };

it('reports all empty space', () => {
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 make this more specific? E.g. returns the distance from each side of the anchor to each side of the container

});
});

it('respects buffer', () => {
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 subtracts the buffer amount from the returned distances?

Copy link
Contributor

Choose a reason for hiding this comment

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

And is offset and offsetSide pertinent here? If not, can we remove them?

});
});

it('respects offset', () => {
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 subtracts the offset from the specified offsetSide?

If we want to cover all the bases, we could also iterate through all of the valid offsetSide values.

    // We can reuse this object in other assertions too.
    const expectedAvailableSpace = {
      top: 90,
      right: 30,
      bottom: 40,
      left: 5,
    };

    ['top', 'right', 'bottom', 'left'].forEach(side => {
      expect(getAvailableSpace(anchorBoundingBox, containerBoundingBox, 0, 5, side)).toEqual({
        ...expectedAvailableSpace,
        [side]: expectedAvailableSpace[side] - 5,
      });
    });

Copy link
Contributor Author

Choose a reason for hiding this comment

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

nice!

});
});

it('respects buffer & offset', () => {
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 subtracts the buffer and the offset from the specified offsetSide?

});

describe('getVisibleFit', () => {
it('calculates full visibility when container is large', () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

calculates full visibility when content is fully enclosed by container? And it might be more readable to use variables here:

const content = { top: 25, right: 50, bottom: 50, left: 25 };
const container = { top: 0, right: 500, bottom: 500, left: 0 };
expect(getVisibleFit(content, container).toBe(1);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

IE11 has issues with EuiTooltip
4 participants