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

Feature/parallel chart tooltip #1186

Merged
merged 21 commits into from
Nov 30, 2022
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
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

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import classnames from 'classnames';
import * as d3 from 'd3';
import { HoverStateContext } from '../utils/hover-state-context';
import { v4 as uuidv4 } from 'uuid';
import { MetricsChartsTooltip, tooltipDefaultProps } from '../tooltip/tooltip';
import { sidebarWidth } from '../../../config';
import { formatTimestamp } from '../../../utils/date-utils';
Copy link
Member

Choose a reason for hiding this comment

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

Can we augment this util function to return a date in the format specified by the design? That format is this: DD MMM YYYY (e.g. 12 Jan 2022).

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 was going to check with @comym actually that whether we dont want to show the time at all, just a date but will update to match the design for now


import './parallel-coordinates.css';

Expand All @@ -12,6 +15,11 @@ const paddingLr = 80;
const axisGapBuffer = 3;
const selectedMarkerRotate = [45, 0, 0];

const tooltipMaxWidth = 300;
const tooltipLeftGap = 90;
const tooltipRightGap = 60;
const tooltipTopGap = 150;

const selectedMarkerColors = ['#00E3FF', '#3BFF95', '#FFE300'];

const yAxis = {};
Expand All @@ -21,6 +29,7 @@ export const ParallelCoordinates = ({ metricsData, selectedRuns }) => {
const [hoveredAxisG, setHoveredAxisG] = useState(null);
const [chartHeight, setChartHeight] = useState(0);
const [chartWidth, setChartWidth] = useState(0);
const [showTooltip, setShowTooltip] = useState(tooltipDefaultProps);

const { hoveredElementId, setHoveredElementId } =
useContext(HoverStateContext);
Expand Down Expand Up @@ -76,11 +85,81 @@ export const ParallelCoordinates = ({ metricsData, selectedRuns }) => {
};

const handleMouseOverMetric = (e, key) => {
const runsCount = graph.find((each) => each[0] === key)[1].length;
setHoveredAxisG(key);

const rect = e.target.getBoundingClientRect();
const y = rect.y - tooltipTopGap + rect.height / 2;
let x, direction;

if (window.innerWidth - rect.x > tooltipMaxWidth) {
x = e.clientX - sidebarWidth.open - tooltipRightGap;
direction = 'right';
} else {
x =
e.clientX - sidebarWidth.open - sidebarWidth.open / 2 - tooltipLeftGap;
direction = 'left';
}

setShowTooltip({
content: {
label1: 'Metrics Name',
Huongg marked this conversation as resolved.
Show resolved Hide resolved
value1: key,
label2: 'Runs Count',
Huongg marked this conversation as resolved.
Show resolved Hide resolved
value2: runsCount,
},
direction,
position: { x, y },
visible: true,
});
};

const handleMouseOutMetric = () => {
setHoveredAxisG(null);
setShowTooltip(tooltipDefaultProps);
};

const handleMouseOverLine = (e, key) => {
setHoveredElementId(key);

if (e) {
const y = e.clientY - tooltipTopGap;
const parsedDate = new Date(formatTimestamp(key));
let x, direction;

if (window.innerWidth - e.clientX > tooltipMaxWidth) {
x = e.clientX - sidebarWidth.open - tooltipRightGap;
direction = 'right';
} else {
x =
e.clientX -
sidebarWidth.open -
sidebarWidth.open / 2 -
tooltipLeftGap;
direction = 'left';
}

setShowTooltip({
content: {
label1: 'Metrics Name',
Huongg marked this conversation as resolved.
Show resolved Hide resolved
value1: key,
label2: 'Date',
value2: parsedDate.toLocaleDateString('default', {
day: 'numeric',
month: 'long',
year: 'numeric',
}),
},
direction,
position: { x, y },
visible: true,
});
}
};

const handleMouseOutLine = () => {
setHoveredElementId(null);
setShowTooltip(tooltipDefaultProps);
};

useEffect(() => {
Expand All @@ -95,6 +174,13 @@ export const ParallelCoordinates = ({ metricsData, selectedRuns }) => {

return (
<div className="parallel-coordinates">
<MetricsChartsTooltip
content={showTooltip.content}
direction={showTooltip.direction}
position={showTooltip.position}
visible={showTooltip.visible}
/>

<svg
preserveAspectRatio="xMinYMin meet"
viewBox={`0 0 ${chartWidth} ${chartHeight}`}
Expand Down Expand Up @@ -139,9 +225,9 @@ export const ParallelCoordinates = ({ metricsData, selectedRuns }) => {
d={linePath(value, i)}
id={id}
key={id}
onMouseLeave={() => setHoveredElementId(null)}
onMouseLeave={handleMouseOutLine}
onMouseOver={(e) => {
setHoveredElementId(id);
handleMouseOverLine(e, id);
d3.select(e.target).raise();
}}
/>
Expand Down
35 changes: 35 additions & 0 deletions src/components/experiment-tracking/tooltip/tooltip.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import React from 'react';
import classnames from 'classnames';

import './tooltip.css';

export const tooltipDefaultProps = {
content: { label1: '', value1: '', label2: '', value2: '' },
direction: 'right',
position: { x: -500, y: -500 },
visible: false,
};

export const MetricsChartsTooltip = ({
content = tooltipDefaultProps.content,
direction = tooltipDefaultProps.direction,
position = tooltipDefaultProps.position,
visible = tooltipDefaultProps.visible,
}) => {
return (
<div
className={classnames('tooltip', { 'tooltip--show': visible })}
style={{ transform: `translate(${position.x}px, ${position.y}px)` }}
>
<span
className={classnames('tooltip-arrow', `tooltip-arrow--${direction}`)}
/>
<h3 className="tooltip-label">{`${content?.label1}:`}</h3>
<h4 className="tooltip-value">{content?.value1}</h4>

<br />
<h3 className="tooltip-label">{`${content?.label2}:`}</h3>
<h4 className="tooltip-value">{content?.value2}</h4>
</div>
);
};
104 changes: 104 additions & 0 deletions src/components/experiment-tracking/tooltip/tooltip.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
@use '../../../styles/variables' as colors;

@mixin fade-in($waitTime) {
animation: wait #{$waitTime}, fade-in 800ms #{$waitTime};
Huongg marked this conversation as resolved.
Show resolved Hide resolved
}

@keyframes wait {
0% {
opacity: 0;
}

100% {
opacity: 0;
}
}

@keyframes fade-in {
0% {
opacity: 0;
}

100% {
opacity: 1;
}
}

$triangle-size: 10px;

.tooltip {
background: white;
display: flex;
flex-direction: column;
opacity: 0;
padding: 10px 30px 10px 10px;
position: absolute;
}

.tooltip--show {
@include fade-in('700ms');
animation-fill-mode: forwards;
}

.tooltip-arrow {
display: inline-block;
height: 0;
left: 1px;

margin-right: 1.6em;
margin-top: -1.2em;

position: absolute;
top: 22px;
white-space: nowrap;
width: 0;
}

.tooltip-arrow--right {
&::before {
border-left: $triangle-size solid transparent;
border-top: $triangle-size solid var(--color-bg-alt);

content: '';

height: 0;
left: -$triangle-size + 0.5;
position: absolute;
top: calc(50% - #{$triangle-size});
width: 0;
}
}

.tooltip-arrow--left {
&::before {
border-right: $triangle-size solid transparent;
border-top: $triangle-size solid var(--color-bg-alt);

content: '';

height: 0;
position: absolute;
right: -225.5px;
top: calc(50% - #{$triangle-size});
width: 0;
}
}

.tooltip-label,
.tooltip-value {
font-size: 12px;
font-weight: 400;
margin: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
width: 180px;
}

.tooltip-label {
color: #{colors.$black-500};
}

.tooltip-value {
color: #{colors.$black-900};
}
2 changes: 1 addition & 1 deletion src/utils/date-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import dayjs from 'dayjs';
import relativeTime from 'dayjs/plugin/relativeTime';
const _dayJs = dayjs.extend(relativeTime);

const formatTimestamp = (timestamp) =>
export const formatTimestamp = (timestamp) =>
timestamp.replace('.', ':').replace('.', ':');

/**
Expand Down