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

ui: Flamegraph tooltip improvements #2600

Merged
merged 19 commits into from
Feb 21, 2023
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
7d6d120
Replaced the node margins with storkes to remove the flickering while…
manojVivek Feb 15, 2023
1721352
Reduced the throttle time and fixed the node margin bug
manojVivek Feb 15, 2023
4b496ee
Tooltip spacing, font size, content truncation tweaks
manojVivek Feb 15, 2023
6ecba34
Merge branch 'main' into flamegraph-tooltip-improvements
manojVivek Feb 15, 2023
e8dfb3b
Updated popper position so that the position is changed only when the…
manojVivek Feb 16, 2023
29f9fbb
Merge branch 'main' into flamegraph-tooltip-improvements
manojVivek Feb 16, 2023
42ce15d
Merge branch 'main' into flamegraph-tooltip-improvements
manojVivek Feb 16, 2023
f87262c
ESLint failures fixed'
manojVivek Feb 16, 2023
c3c74ad
Merge remote-tracking branch 'origin/flamegraph-tooltip-improvements'…
manojVivek Feb 16, 2023
a505e51
Merge remote-tracking branch 'origin/flamegraph-tooltip-improvements'…
manojVivek Feb 16, 2023
225b6f0
Merged origin/main
manojVivek Feb 20, 2023
f26dca0
Tooltip content modified to show all fields always
manojVivek Feb 20, 2023
4d7b57e
Tooltip mouse move perf improvements
manojVivek Feb 21, 2023
e1ac12a
Downgraded tailwind version
manojVivek Feb 21, 2023
694c757
Merge branch 'main' into flamegraph-tooltip-improvements
manojVivek Feb 21, 2023
ba6e42e
Merge remote-tracking branch 'origin/flamegraph-tooltip-improvements'…
manojVivek Feb 21, 2023
dfdb10b
Merge remote-tracking branch 'origin/main' into tailwind-downgrade
manojVivek Feb 21, 2023
9a9c709
Type error fix
manojVivek Feb 21, 2023
f40aba1
Merge remote-tracking branch 'origin/main' into flamegraph-tooltip-im…
manojVivek Feb 21, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright 2022 The Parca Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

interface Props {
value: string | number | undefined;
displayValue?: string | number | undefined;
}

export const ExpandOnHover = ({value, displayValue}: Props): JSX.Element => {
return (
<div className="relative group w-full">
<div className="text-ellipsis w-full overflow-hidden whitespace-nowrap">
{displayValue ?? value}
</div>
<div className="group-hover:flex hidden absolute -inset-2 max-w-[500px] whitespace-normal h-fit bg-gray-50 dark:bg-gray-900 shadow-[0_0_10px_2px_rgba(0,0,0,0.3)] rounded p-2 break-all">
{value}
</div>
</div>
);
};
104 changes: 51 additions & 53 deletions ui/packages/shared/profile/src/GraphTooltip/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,14 @@ import {usePopper} from 'react-popper';

import {CallgraphNode, FlamegraphNode, FlamegraphNodeMeta, FlamegraphRootNode} from '@parca/client';
import {getLastItem, valueFormatter} from '@parca/functions';
import {hexifyAddress, truncateString} from '../';
import {hexifyAddress, truncateString, truncateStringReverse} from '../';
import {useKeyDown} from '@parca/components';
import {
Function as ParcaFunction,
Location,
Mapping,
} from '@parca/client/dist/parca/metastore/v1alpha1/metastore';
import {ExpandOnHover} from './ExpandOnHoverValue';

interface GraphTooltipProps {
x: number;
Expand Down Expand Up @@ -140,25 +141,28 @@ const TooltipMetaInfo = ({
}`
}`;
};
const file = getTextForFile(hoveringNode);

return (
<>
{hoveringNode.meta.function?.filename !== undefined &&
hoveringNode.meta.function?.filename !== '' && (
<tr>
<td className="w-1/5">File</td>
<td className="w-4/5 break-all">
<CopyToClipboard onCopy={onCopy} text={getTextForFile(hoveringNode)}>
<button className="cursor-pointer text-left">{getTextForFile(hoveringNode)}</button>
<td className="w-1/4">File</td>
<td className="w-3/4 break-all">
<CopyToClipboard onCopy={onCopy} text={file}>
<button className="cursor-pointer text-left whitespace-nowrap">
<ExpandOnHover value={file} displayValue={truncateStringReverse(file, 40)} />
</button>
</CopyToClipboard>
</td>
</tr>
)}
{hoveringNode.meta.location?.address !== undefined &&
hoveringNode.meta.location?.address !== '0' && (
<tr>
<td className="w-1/5">Address</td>
<td className="w-4/5 break-all">
<td className="w-1/4">Address</td>
<td className="w-3/4 break-all">
<CopyToClipboard
onCopy={onCopy}
text={hexifyAddress(hoveringNode.meta.location.address)}
Expand All @@ -172,8 +176,8 @@ const TooltipMetaInfo = ({
)}
{hoveringNode.meta.mapping !== undefined && hoveringNode.meta.mapping.file !== '' && (
<tr>
<td className="w-1/5">Binary</td>
<td className="w-4/5 break-all">
<td className="w-1/4">Binary</td>
<td className="w-3/4 break-all">
<CopyToClipboard onCopy={onCopy} text={hoveringNode.meta.mapping.file}>
<button className="cursor-pointer">
{getLastItem(hoveringNode.meta.mapping.file)}
Expand All @@ -184,11 +188,11 @@ const TooltipMetaInfo = ({
)}
{hoveringNode.meta.mapping !== undefined && hoveringNode.meta.mapping.buildId !== '' && (
<tr>
<td className="w-1/5">Build Id</td>
<td className="w-4/5 break-all">
<td className="w-1/4">Build Id</td>
<td className="w-3/4 break-all">
<CopyToClipboard onCopy={onCopy} text={hoveringNode.meta.mapping.buildId}>
<button className="cursor-pointer">
{truncateString(getLastItem(hoveringNode.meta.mapping.buildId) as string, 16)}
{truncateString(getLastItem(hoveringNode.meta.mapping.buildId) as string, 28)}
</button>
</CopyToClipboard>
</td>
Expand Down Expand Up @@ -265,15 +269,12 @@ const GraphTooltipContent = ({
};

return (
<div className={`flex ${isFixed ? 'w-full h-36' : ''}`}>
<div className={`m-auto w-full ${isFixed ? 'w-full h-36' : ''}`}>
<div
className="border-gray-300 dark:border-gray-500 bg-gray-50 dark:bg-gray-900 rounded-lg p-3 shadow-lg opacity-90"
style={{borderWidth: 1}}
>
<div className={`text-sm flex ${isFixed ? 'w-full' : ''}`}>
<div className={`m-auto w-full ${isFixed ? 'w-full' : ''}`}>
<div className="border border-gray-300 dark:border-gray-500 bg-gray-50 dark:bg-gray-900 rounded-lg p-3 shadow-lg min-h-52 w-[500px] flex justify-between flex-col">
<div className="flex flex-row">
<div className="ml-2 mr-6">
<span className="font-semibold break-all">
<div className="mx-2">
<div className="font-semibold break-all h-10 flex items-center">
{hoveringNode.meta === undefined ? (
<p>root</p>
) : (
Expand Down Expand Up @@ -304,44 +305,41 @@ const GraphTooltipContent = ({
)}
</>
)}
</span>
<span className="text-gray-700 dark:text-gray-300 my-2">
<table className="table-fixed">
<tbody>
<tr>
<td className="w-1/5">Cumulative</td>
</div>
<table className="table-fixed pr-0 text-gray-700 dark:text-gray-300 my-2 w-full">
<tbody>
<tr>
<td className="w-1/4">Cumulative</td>

<td className="w-4/5">
<CopyToClipboard
onCopy={onCopy}
text={getTextForCumulative(hoveringNodeCumulative)}
>
<button className="cursor-pointer">
{getTextForCumulative(hoveringNodeCumulative)}
</button>
<td className="w-3/4">
<CopyToClipboard
onCopy={onCopy}
text={getTextForCumulative(hoveringNodeCumulative)}
>
<button className="cursor-pointer">
{getTextForCumulative(hoveringNodeCumulative)}
</button>
</CopyToClipboard>
</td>
</tr>
{hoveringNode.diff !== undefined && diff !== 0 && (
<tr>
<td className="w-1/4">Diff</td>
<td className="w-3/4">
<CopyToClipboard onCopy={onCopy} text={diffText}>
<button className="cursor-pointer">{diffText}</button>
</CopyToClipboard>
</td>
</tr>
{hoveringNode.diff !== undefined && diff !== 0 && (
<tr>
<td className="w-1/5">Diff</td>
<td className="w-4/5">
<CopyToClipboard onCopy={onCopy} text={diffText}>
<button className="cursor-pointer">{diffText}</button>
</CopyToClipboard>
</td>
</tr>
)}
{metaRows}
</tbody>
</table>
</span>

<span className="block text-gray-500 text-xs mt-2">
{isCopied ? 'Copied!' : 'Hold shift and click on a value to copy.'}
</span>
)}
{metaRows}
</tbody>
</table>
</div>
</div>
<span className="block text-gray-500 text-xs mx-2">
{isCopied ? 'Copied!' : 'Hold shift and click on a value to copy.'}
</span>
</div>
</div>
</div>
Expand All @@ -368,7 +366,7 @@ const GraphTooltip = ({
virtualContextElement ? virtualElement : contextElement,
popperElement,
{
placement: 'auto-start',
placement: 'bottom-start',
strategy: 'absolute',
modifiers: [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -226,12 +226,12 @@ export const IcicleNode = React.memo(function IcicleNode({
<rect
x={0}
y={0}
width={width - 1}
height={height - 1}
width={width}
height={height}
style={{
fill: colorResult,
}}
className={cx({
className={cx('stroke-white dark:stroke-gray-700', {
'opacity-50': isHighlightEnabled && !isHighlighted,
})}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import React, {memo, useEffect, useMemo, useRef, useState} from 'react';
import React, {memo, useCallback, useEffect, useMemo, useRef, useState} from 'react';

import cx from 'classnames';
import {throttle} from 'lodash';
Expand Down Expand Up @@ -83,17 +83,24 @@ export const IcicleGraph = memo(function IcicleGraph({
return scaleLinear().domain([0, total]).range([0, width]);
}, [total, width]);

const throttledSetPos = useMemo(() => {
return throttle(setPos, 15, {leading: true, trailing: true});
}, [setPos]);

const onMouseMove = useCallback(
(e: React.MouseEvent<SVGSVGElement | HTMLDivElement>): void => {
// X/Y coordinate array relative to svg
const rel = pointer(e);

throttledSetPos(rel);
},
[throttledSetPos]
);

if (coloredGraph.root === undefined || width === undefined) {
return <></>;
}

const throttledSetPos = throttle(setPos, 20);
const onMouseMove = (e: React.MouseEvent<SVGSVGElement | HTMLDivElement>): void => {
// X/Y coordinate array relative to svg
const rel = pointer(e);

throttledSetPos([rel[0], rel[1]]);
};
const isColorStackLegendVisible = colorProfileName !== 'default';

return (
Expand Down
8 changes: 8 additions & 0 deletions ui/packages/shared/profile/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,11 @@ export const truncateString = (str: string, num: number): string => {

return str.slice(0, num) + '...';
};

export const truncateStringReverse = (str: string, num: number): string => {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this is a good example of something that could live in the @parca/utils package once that is in place :) cc @yomete

Copy link
Contributor

Choose a reason for hiding this comment

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

will look out for it!

if (str.length <= num) {
return str;
}

return '...' + str.slice(str.length - num);
};
12 changes: 11 additions & 1 deletion ui/packages/shared/profile/tailwind.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,17 @@ module.exports = {
content: ['./src/**/*.{html,js,jsx,ts,tsx}'],
darkMode: ['class', '[class~="theme-dark"]'],
theme: {
extend: {},
extend: {
minHeight: theme => ({
...theme('spacing'),
}),
maxWidth: theme => ({
...theme('spacing'),
}),
minWidth: theme => ({
...theme('spacing'),
}),
},
},
plugins: [],
};