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

fix(replays): Memory Chart Tooltip Overflow #38746

Merged
merged 12 commits into from Sep 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
71 changes: 44 additions & 27 deletions static/app/components/charts/baseChart.tsx
Expand Up @@ -4,7 +4,7 @@ import 'echarts/lib/component/toolbox';
import 'zrender/lib/svg/svg';

import {forwardRef, useMemo} from 'react';
import {useTheme} from '@emotion/react';
import {css, Global, useTheme} from '@emotion/react';
import styled from '@emotion/styled';
import type {
AxisPointerComponentOption,
Expand Down Expand Up @@ -465,6 +465,8 @@ function BaseChartUnwrapped({
: undefined;
const bucketSize = seriesData ? seriesData[1][0] - seriesData[0][0] : undefined;

const isTooltipPortalled = tooltip?.appendToBody;

const tooltipOrNone =
tooltip !== null
? Tooltip({
Expand All @@ -474,6 +476,9 @@ function BaseChartUnwrapped({
utc,
bucketSize,
...tooltip,
className: isTooltipPortalled
? `${tooltip?.className ?? ''} chart-tooltip-portal`
: tooltip?.className,
})
: undefined;

Expand Down Expand Up @@ -538,6 +543,7 @@ function BaseChartUnwrapped({

return (
<ChartContainer autoHeightResize={autoHeightResize} data-test-id={dataTestId}>
{isTooltipPortalled && <Global styles={getPortalledTooltipStyles({theme})} />}
<ReactEchartsCore
ref={forwardedRef}
echarts={echarts}
Expand All @@ -559,32 +565,29 @@ function BaseChartUnwrapped({
);
}

// Contains styling for chart elements as we can't easily style those
// elements directly
const ChartContainer = styled('div')<{autoHeightResize: boolean}>`
${p => p.autoHeightResize && 'height: 100%;'}

// Tooltip styles shared for regular and portalled tooltips
const getTooltipStyles = (p: {theme: Theme}) => css`
/* Tooltip styling */
.tooltip-series,
.tooltip-date {
color: ${p => p.theme.subText};
font-family: ${p => p.theme.text.family};
color: ${p.theme.subText};
font-family: ${p.theme.text.family};
font-variant-numeric: tabular-nums;
padding: ${space(1)} ${space(2)};
border-radius: ${p => p.theme.borderRadius} ${p => p.theme.borderRadius} 0 0;
border-radius: ${p.theme.borderRadius} ${p.theme.borderRadius} 0 0;
}
.tooltip-series {
border-bottom: none;
}
.tooltip-series-solo {
border-radius: ${p => p.theme.borderRadius};
border-radius: ${p.theme.borderRadius};
}
.tooltip-label {
margin-right: ${space(1)};
}
.tooltip-label strong {
font-weight: normal;
color: ${p => p.theme.textColor};
color: ${p.theme.textColor};
}
.tooltip-label-indent {
margin-left: 18px;
Expand All @@ -595,11 +598,11 @@ const ChartContainer = styled('div')<{autoHeightResize: boolean}>`
align-items: baseline;
}
.tooltip-date {
border-top: solid 1px ${p => p.theme.innerBorder};
border-top: solid 1px ${p.theme.innerBorder};
text-align: center;
position: relative;
width: auto;
border-radius: ${p => p.theme.borderRadiusBottom};
border-radius: ${p.theme.borderRadiusBottom};
}
.tooltip-arrow {
top: 100%;
Expand All @@ -608,12 +611,12 @@ const ChartContainer = styled('div')<{autoHeightResize: boolean}>`
pointer-events: none;
border-left: 8px solid transparent;
border-right: 8px solid transparent;
border-top: 8px solid ${p => p.theme.backgroundElevated};
border-top: 8px solid ${p.theme.backgroundElevated};
margin-left: -8px;
&:before {
border-left: 8px solid transparent;
border-right: 8px solid transparent;
border-top: 8px solid ${p => p.theme.translucentBorder};
border-top: 8px solid ${p.theme.translucentBorder};
content: '';
display: block;
position: absolute;
Expand All @@ -623,26 +626,18 @@ const ChartContainer = styled('div')<{autoHeightResize: boolean}>`
}
}

.echarts-for-react div:first-of-type {
width: 100% !important;
}

.echarts-for-react text {
font-variant-numeric: tabular-nums !important;
}

/* Tooltip description styling */
.tooltip-description {
color: ${p => p.theme.white};
border-radius: ${p => p.theme.borderRadius};
color: ${p.theme.white};
border-radius: ${p.theme.borderRadius};
background: #000;
opacity: 0.9;
padding: 5px 10px;
position: relative;
font-weight: bold;
font-size: ${p => p.theme.fontSizeSmall};
font-size: ${p.theme.fontSizeSmall};
line-height: 1.4;
font-family: ${p => p.theme.text.family};
font-family: ${p.theme.text.family};
max-width: 230px;
min-width: 230px;
white-space: normal;
Expand All @@ -662,6 +657,28 @@ const ChartContainer = styled('div')<{autoHeightResize: boolean}>`
}
`;

// Contains styling for chart elements as we can't easily style those
// elements directly
const ChartContainer = styled('div')<{autoHeightResize: boolean}>`
${p => p.autoHeightResize && 'height: 100%;'}

.echarts-for-react div:first-of-type {
width: 100% !important;
}

.echarts-for-react text {
font-variant-numeric: tabular-nums !important;
}

${p => getTooltipStyles(p)}
`;

const getPortalledTooltipStyles = (p: {theme: Theme}) => css`
.chart-tooltip-portal {
${getTooltipStyles(p)};
}
`;

const BaseChart = forwardRef<ReactEchartsRef, Props>((props, ref) => (
<BaseChartUnwrapped forwardedRef={ref} {...props} />
));
Expand Down
24 changes: 19 additions & 5 deletions static/app/components/charts/components/tooltip.tsx
Expand Up @@ -251,7 +251,13 @@ function getFormatter({

type Props = ChartProps['tooltip'] &
Pick<ChartProps, NeededChartProps> &
Pick<FormatterOptions, 'addSecondsToTimeFormat'>;
Pick<FormatterOptions, 'addSecondsToTimeFormat'> & {
/**
* An ID for the chart when using renderToBody to portal the tooltip.
* A reference to the chart is needed to calculate the tooltip position.
*/
chartId?: string;
};

export default function Tooltip({
filter,
Expand All @@ -268,6 +274,7 @@ export default function Tooltip({
markerFormatter,
hideDelay,
subLabels,
chartId,
...props
}: Props = {}): TooltipComponentOption {
const theme = useTheme();
Expand Down Expand Up @@ -315,12 +322,19 @@ export default function Tooltip({
// Center the tooltip slightly above the cursor.
const [tipWidth, tipHeight] = size.contentSize;

let parentNode: Element = document.body;
if (dom.parentNode instanceof Element) {
parentNode = dom.parentNode;
}

const chartElement: Element =
props.appendToBody && chartId
? document.getElementById(chartId) ?? parentNode
: parentNode;

// Get the left offset of the tip container (the chart)
// so that we can estimate overflows
const chartLeft =
dom.parentNode instanceof Element
? dom.parentNode.getBoundingClientRect().left
: 0;
const chartLeft = chartElement.getBoundingClientRect().left ?? 0;

// Determine the new left edge.
let leftPos = Number(pos[0]) - tipWidth / 2;
Expand Down
5 changes: 4 additions & 1 deletion static/app/views/replays/detail/memoryChart.tsx
Expand Up @@ -56,7 +56,10 @@ function MemoryChart({
right: space(1),
}),
tooltip: Tooltip({
appendToBody: true,
trigger: 'axis',
renderMode: 'html',
Copy link
Member

Choose a reason for hiding this comment

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

what does this do?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

From the type def for the tooltips says the renderMode should be html if using appendToBody

    /**
     * 'auto': use html by default, and use non-html if `document` is not defined
     * 'html': use html for tooltip
     * 'richText': use canvas, svg, and etc. for tooltip
     */
    renderMode?: 'auto' | TooltipRenderMode;
    /**
     * If append popup dom to document.body
     * Only available when renderMode is html
     */
    appendToBody?: boolean;
    /**
     * specified class name of tooltip dom
     * Only available when renderMode is html
     */
    className?: string;
    ```

chartId: 'replay-memory-chart',
formatter: values => {
const seriesTooltips = values.map(
value => `
Expand Down Expand Up @@ -201,7 +204,7 @@ function MemoryChart({
];

return (
<MemoryChartWrapper>
<MemoryChartWrapper id="replay-memory-chart">
<AreaChart forwardedRef={forwardedRef} series={series} {...chartOptions} />
</MemoryChartWrapper>
);
Expand Down