Add JS origin information to flame graph tooltips (Fixes #964)#1000
Conversation
Codecov Report
@@ Coverage Diff @@
## master #1000 +/- ##
==========================================
- Coverage 70.89% 70.88% -0.02%
==========================================
Files 141 141
Lines 8325 8287 -38
Branches 1895 1886 -9
==========================================
- Hits 5902 5874 -28
+ Misses 2140 2134 -6
+ Partials 283 279 -4
Continue to review full report at Codecov.
|
|
UI-wise this looks great to me. Thanks! I haven't looked at the code much yet. The only problem I could find during testing is that the tooltip can get very long if the script URL is long, but this is an existing problem that already plagues our other tooltips (I think?) as well as the context menu, so it should probably be fixed in a separate PR. Here's an example. |
|
Thanks! Yes, sounds good to address the long tooltips in a another PR. What would the proper solution be? I think it's too much information in such an URL to be meaningful if we'd just reformat them by adding line breaks. And it would make the context menu look weird. So perhaps just truncate them with an ellipsis? |
|
Truncating with an ellipsis sounds good to me. |
julienw
left a comment
There was a problem hiding this comment.
Hey Michael,
Great work, this looks good to me. I tried various cases locally (including previous bugs we used to have) and the behavior looks OK.
I left some comments though:
- potential performance optimizations or correctness
- placement of the icon
Please tell me if you disagree about anything :)
|
|
||
| const maybeHoveredItem = this._hoveredItemFromMouseEvent(event); | ||
| // These offsets seem to be identical to | ||
| // `event.nativeEvent.offsetX/Y`, albeit not recognized by flow. |
There was a problem hiding this comment.
I don't like that Flow forces us to use a less performant solution.
You can tell flow using something like this:
_onMouseMove(event: { nativeEvent: MouseEvent } & SyntheticMouseEvent<>) {I think the order is important, so maybe I'm wrong in the order and { nativeEvent: ... } should come after.
And then you can use offsetX and offsetY directly.
| pageX: event.pageX, | ||
| pageY: event.pageY, | ||
| offsetX, | ||
| offsetY, |
There was a problem hiding this comment.
offsetX and offsetY shouldn't go in the state. Each time you put something in the state this will trigger a rerender, so the rule of thumb is that we shouldn't put in the state something that's not used for render. Here you simply want a local variable (eg: this._offsetX).
In that case this is not a big deal as we change also pageX and pageY that we use at render, so there's no performance issue in this specific case. But I prefer that we use "the right way" as a good example for other similar cases too.
There was a problem hiding this comment.
Oh, I missed that; I considered the offsets to be almost the same thing as pageX/Y and thus just put them together in the state. But I agree with you it's better to use the the more correct way.
| display: inline-block; | ||
| margin-right: 3px; | ||
| align-self: flex-end; | ||
| } |
There was a problem hiding this comment.
It doesn't look very good when the icon is empty:

I think it's not ideal to hide it if there's no favicon because this would make the text "jump". Maybe the best is to put the favicon somewhere else where it's not so awkward when it's absent, so maybe on the right instead of the left. (for LTR languages, but Flex layout does the right thing if we ever make it work for RTL languages anyway).
There was a problem hiding this comment.
Yeah, that doesn't look very good.
But, (ignoring the fact that the icon is too close to the text in the image), putting it on the right side of the text looks a bit off to me:

I think the favicon goes better together with the domain name of the URL rather than the path on the right side. This exacerbates as the URL gets longer.
Perhaps we can align the URL with the function name above it, then it looks less bad if there's no favicon. What do you think?

There was a problem hiding this comment.
Why not... Counter-proposal though: what do you think of putting it at the top right ? (so at the right of the first line)
There was a problem hiding this comment.
Perhaps we can align the URL with the function name above it, then it looks less bad if there's no favicon. What do you think?
This looks pretty good to me and seems like the best option in my opinion.
I don't like the proposal of putting it on the right, probably because favicons are on the left in all other contexts where they're used.
There was a problem hiding this comment.
Ok, so I pushed new changes that implements the suggested option.
I'm not super pleased with that approach however, mainly because of how it looks when there's no lib/URL at all in the tooltip:

@julienw 's suggestion to put the whole thing on the top right would solve that problem, but the tooltips tend to be very long if we need to squeeze both function name and URL on the same line.
I'm out of ideas on how to solve this without having text jumping.
There was a problem hiding this comment.
I don't understand - why is there an empty row if neither the favicon nor the url are present?
There was a problem hiding this comment.
I was going to answer that it's because the icon has a min-height, but now I realize that I should just add back the conditional rendering of the icon depending on whether displayData.icon is defined. I had removed it, believing it was unnecessary.
Thank you for asking! This shouldn't be problem anymore :)
|
|
||
| .flameGraphCanvasTooltip .tooltipTitle { | ||
| white-space: nowrap; | ||
| overflow: visible; |
There was a problem hiding this comment.
Not adding this will cause tooltips to become truncated with an ellipsis, which is something desirable, but it does it "too much" when displaying tooltips close to the edge of the chart. I'm adding this to retain the current behavior and defer the solution to "too long tooltips"-problem to a separate PR.
|
This is ready for re-review. |
- Use offsetX/Y of nativeEvent directly - Don't store offsetX/Y as state - Align url/lib in tooltip better
julienw
left a comment
There was a problem hiding this comment.
There's a last nit I'd like, but otherwise this is ready to land :)
Thanks !
| displayData = { | ||
| ...this.getTimingDisplayData(callNodeIndex), | ||
| totalTime: `${formatNumber(totalTime)}`, | ||
| selfTime: selfTime === 0 ? '—' : `${formatNumber(selfTime)}`, |
There was a problem hiding this comment.
Small nit: We don't need to use a template string here as the the only content is the result of formatNumber. This could be useful if the result of the function wasn't a string, but here it's a string. So you can simply use: totalTime: formatNumber(totalTime) and the same for selfTime.
This shows the favicon and URL in the tooltip, (or the lib path if it's a native stack frame).
A few changes are introduced in order to make the extended tooltips work.
First, the formatting of the data to display in the tooltip has been removed from the calculation of the flame graph timing data structure. Instead it is done when the tooltip is shown. This speeds up the flame graph considerably since the display data does no need to be unnecessarily created for every box in the flame graph.
Second, the rendering of the favicon uses the NodeIcon component originally under the calltree subdirectory, but has now been moved to the shared directory. Along with this, the class name
treeRowIconis now callednodeIcon.Third, to stop the tooltip from disappearing when the NodeIcon component asynchronously loads an icon, the clearing of hovered items are yet again modified. Now the hovered item is only cleared when the new hovered item would be something different from the current one.