Skip to content
This repository has been archived by the owner on May 29, 2019. It is now read-only.

feat(tooltip): now can take the option for the close delay timeout #3576

Closed
wants to merge 1 commit into from
Closed

Conversation

tmcgee123
Copy link
Contributor

test(tooltip): testing that the close delay works as expected

This is a change to add control over the delay to remove the tooltip from the view. This solution came from the need to delay the closing of the tooltip without overriding bootstrap classes, manipulation of the DOM , or need to create custom triggers that depend on the usage of $timeout.

The use-case for this was:

  1. User hovers over the link for the popover/tooltip
  2. The popover/tooltip shows long enough for the user to move the cursor off of the element that triggers the popover and access any link inside of the popover/tooltip content

'tooltip-popup-close-delay="{{delay}}">Selector Text</span>'
))(scope);
elmScope = elm.scope();
tooltipScope = elmScope.$$childTail;
Copy link

Choose a reason for hiding this comment

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

Isn't using Angular's internal method $$childTail creating a potential for future bugs, should its implementation change in the future?

If I understand it correctly, this is to access child scope, which we should try to avoid to.
Here it is suggested instead to

move the model up into the parent...
http://stackoverflow.com/a/14006165/1614973

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've used the same setup as the test for the delay of the opening of the tooltip in the file, if you were to look at the rest of the spec it is doing the same. In a test, this is standard practice to access the directive's scope in order to check a value.

Copy link

Choose a reason for hiding this comment

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

This is just a concern that came up, I know it works for now, but might be helpful for future reference. There might be a better way without depending so deeply on Angular's mechanics.
I didn't want to add to already huge issue queue, so put it here.

@tmcgee123
Copy link
Contributor Author

I was able to successfully change each .trigger to .triggerHandler in this file. Also, I added more assertions for checking whether or not the element is visible and not just "dirty" checking the isOpen scope variable as @dmitriz has suggested for both. The only comment I was unable to address was the one pertaining to $$childTail as the isOpen variable is not accessible publicly and is truly "isolate" to this directive, but is set as a TODO on line 142 of tooltip.js where it states "// TODO add ability to start tooltip opened".

@@ -33,7 +33,7 @@ describe('tooltip', function() {
}));

it('should open on mouseenter', inject(function() {
elm.trigger( 'mouseenter' );
Copy link
Contributor

Choose a reason for hiding this comment

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

Any particular reason for all these changes from trigger to triggerHandler?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@dmitriz noted above that trigger is from the jQuery dependency: "It is correct that .trigger is currently used but it is from jQuery. The present tests have been written a while ago, but more recently the Project Philosophy sets the goal to get away from the jQuery dependency. So for new PRs, it might be a worth contribution towards the new Philosophy to support this trend." Please check the outdated comments above from the changes I made.

Copy link
Contributor

Choose a reason for hiding this comment

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

I would undo those changes - they can be tackled in a separate PR, but they pollute the feature.

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 has been properly reverted

@wesleycho
Copy link
Contributor

I like this change for it to be more configurable, but this PR seems polluted with some non-relevant changes.

Also for verification, I notice that the account that made the commits is different from the one filing the PR - I assume they are both yours?

@tmcgee123
Copy link
Contributor Author

@wesleycho can you elaborate on the "pollution" you mentioned above? I'm not sure what you mean by the account that made the commits is different, it's just showing my new avatar and updated profile. I'm the only person making these changes if that's what you are asking :D

@@ -1,7 +1,7 @@
{
"author": "https://github.com/angular-ui/bootstrap/graphs/contributors",
"name": "angular-ui-bootstrap",
"version": "0.13.2-SNAPSHOT",
"version": "0.13.2-A1.4.3",
Copy link
Contributor

Choose a reason for hiding this comment

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

Undo this change.

@wesleycho
Copy link
Contributor

I have considered opening code style change PRs and altering the build to enforce it, but it has the downside of instantly merge conflicting almost all PRs - an incremental improvement in style would help. If it is not made, it would likely be made on merging of the PR, which puts more burden on maintainers (it was already painful enough cutting issues down from over 500 to under 330, and open PRs from over 200 to almost 100 the past couple of months).

@tmcgee123
Copy link
Contributor Author

@wesleycho I've made all of the recommended changes that you've left comments on. Please let me know if there are any other changes! Thanks for the help!

@@ -853,9 +938,9 @@ describe( '$tooltipProvider', function() {
elm.trigger('fakeTrigger');
expect( tooltipScope.isOpen ).toBeTruthy();
elm.trigger('fakeTrigger');
$timeout.flush();
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this change necessary?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, the test was failing on my branch since there is a timeout for closing the popup.

@wesleycho
Copy link
Contributor

The history needs to be cleaned up - there should not be a merge commit, and it should be squashed into one.

@tmcgee123
Copy link
Contributor Author

@wesleycho I was able to successfully rebase and squash my commits. That was a first for me, barely made it through whew.

@tmcgee123
Copy link
Contributor Author

@wesleycho I've updated this again to resolve the merge conflicts, any chance on this getting merged soon? It'd be nice to be able to point to the main repository rather than pointing to my fork. Thanks!!

@wesleycho
Copy link
Contributor

I apologize for having to ask this again, but can you rebase off of master & adjust the feature one more time? Some major fixes landed with regards to tooltip positioning & $digest errors.

I intend on having this land for 0.14, so the sooner, the better :) .

@tmcgee123 tmcgee123 reopened this Oct 5, 2015
@tmcgee123
Copy link
Contributor Author

Okay Mr. @wesleycho , I went ahead and cleared my changes and re-applied them, as I was stuck in rebase misery :D still need to learn how to do that properly. Cheers!

@@ -20,6 +20,7 @@ will display:
- `tooltip-animation`: Should it fade in and out? Defaults to "true".
- `tooltip-popup-delay`: For how long should the user have to have the mouse
over the element before the tooltip shows (in milliseconds)? Defaults to 0.
- `tooltip--close-popup-delay`: For how long should the tooltip remained open?
Copy link
Contributor

Choose a reason for hiding this comment

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

One dash

@wesleycho
Copy link
Contributor

LGTM - I'll merge this soon with the fixes applied.

@tmcgee123
Copy link
Contributor Author

Thanks!!!

@ArielAleksandrus
Copy link

Does it work with popover? I'm not being able to use it with neither popover nor tooltip. Can I pass a literal value or it should be an angular variable? Is the time in milliseconds?

@wesleycho
Copy link
Contributor

It should work with both - this seems to not work as expected though.

@tmcgee123
Copy link
Contributor Author

It looks like in the midst of the many changes that were conflicting with my branch, the functionality stopped working.

For some reason, even though the $timeout is called in the hide function on line 269 in tooltip.js, it is not properly timing out. The ttScope.popupCloseDelay variable is being properly set to the passed in delay, so there must be something cancelling the timeout or something else that I am not aware of is going on.

if (ttScope.animation) {
  if (!transitionTimeout) {
    transitionTimeout = $timeout(removeTooltip, ttScope.popupCloseDelay); // here is where $timeout is not working for some reason
  }
} else {
  removeTooltip();
}

I'm still debugging, surely the fix will be simple. Any thoughts, points of interest, or information would be much appreciated!

@wesleycho
Copy link
Contributor

I noticed this too this past weekend, but I didn't have time to debug since I was out for a large part of it - if you don't find out what happened, I'll probably take a look again sometime later this week.

@tmcgee123
Copy link
Contributor Author

Ahh, it's because of the $evalAsync that is being run before we wait for the animation to completely remove it from the DOM. Since it sets the visibility to false, it is still present in the DOM, but not visible.

I was able to get the delay to work by changing this:

ttScope.$evalAsync(function() {
  ttScope.isOpen = false;
  if (isOpenParse && angular.isFunction(isOpenParse.assign)) {
    isOpenParse.assign(ttScope.origScope, ttScope.isOpen);
  }
});

to this (I'm not sure if this is correct since we using both $timeout and $evalAsync):

$timeout(function () {
  ttScope.$evalAsync(function() {
    ttScope.isOpen = false;
    if (isOpenParse && angular.isFunction(isOpenParse.assign)) {
      isOpenParse.assign(ttScope.origScope, ttScope.isOpen);
    }
  });
}, ttScope.popupCloseDelay);

Also, there is a potential for a slight refactor, since there is shared functionality inside of the callbacks for each $evalAsync:

...
ttScope.isOpen = false;
if (isOpenParse && angular.isFunction(isOpenParse.assign)) {
  isOpenParse.assign(ttScope.origScope, ttScope.isOpen);
}
...

Maybe we can make a function called assignIsOpenToScope (need better name for this)?

function assignIsOpenToScope(isOpen) {
  ttScope.isOpen = isOpen;
  if (isOpenParse && angular.isFunction(isOpenParse.assign)) {
    isOpenParse.assign(ttScope.origScope, ttScope.isOpen);
  }
}

@RobJacobs
Copy link
Contributor

There is already a timeout for when the removeTooltip() is getting called, I would rather see the hideTooltipBind() restructured to work like the showTooltipBind(). I'll put together a PR for review shortly.

@tmcgee123
Copy link
Contributor Author

I agree, I think it's rather silly that hideTooltipBind simply just calls hide. We can probably just use one or the other.

@RobJacobs
Copy link
Contributor

@tmcgee123 PR #4597 has been created to address this issue.

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

Successfully merging this pull request may close these issues.

None yet

6 participants