Skip to content

Commit

Permalink
fix: tooltip container scroll issue (elastic#647)
Browse files Browse the repository at this point in the history
  • Loading branch information
nickofthyme authored Apr 28, 2020
1 parent ed91744 commit f411771
Show file tree
Hide file tree
Showing 11 changed files with 247 additions and 104 deletions.
14 changes: 11 additions & 3 deletions .playground/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,23 @@
*/
/* width: 100%;
height: 100%;*/
/* overflow-x: hidden; */
}
.chart {
background: white;
/*display: inline-block;
position: relative;
*/
width: 300px;
height: 300px;
margin: 20px;
width: 100%;
height: 500px;
overflow: auto;
}

.testing {
background: aquamarine;
position: relative;
width: 100vw;
overflow: auto;
}
</style>
</head>
Expand Down
32 changes: 4 additions & 28 deletions .playground/playground.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
* under the License. */

import React from 'react';
import { Chart, Partition, Settings, PartitionLayout, XYChartElementEvent, PartitionElementEvent } from '../src';
import { XYChartElementEvent, PartitionElementEvent } from '../src';
import { example } from '../stories/treemap/6_custom_style';

export class Playground extends React.Component {
onElementClick = (elements: (XYChartElementEvent | PartitionElementEvent)[]) => {
Expand All @@ -26,33 +27,8 @@ export class Playground extends React.Component {
};
render() {
return (
<div className="chart">
<Chart>
<Settings onElementClick={this.onElementClick} />
<Partition
id="111"
config={{
partitionLayout: PartitionLayout.treemap,
}}
valueAccessor={(d: { v: number }) => {
return d.v;
}}
data={[
{ g1: 'a', g2: 'a', v: 1 },
{ g1: 'a', g2: 'b', v: 1 },
{ g1: 'b', g2: 'a', v: 1 },
{ g1: 'b', g2: 'b', v: 1 },
]}
layers={[
{
groupByRollup: (datum: { g1: string }) => datum.g1,
},
{
groupByRollup: (datum: { g2: string }) => datum.g2,
},
]}
/>
</Chart>
<div className="testing">
<div className="chart">{example()}</div>
</div>
);
}
Expand Down
11 changes: 9 additions & 2 deletions src/chart_types/xy_chart/annotations/annotation_tooltip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
* under the License. */

import { Dimensions } from '../../../utils/dimensions';
import { Position } from '../../../utils/commons';

/** @internal */
export function getFinalAnnotationTooltipPosition(
Expand All @@ -27,16 +28,21 @@ export function getFinalAnnotationTooltipPosition(
tooltip: Dimensions,
/** the tooltip computed position not adjusted within chart bounds */
tooltipAnchor: { top: number; left: number },
/** the width of the tooltip portal container */
portalWidth: number,
padding = 10,
): {
left: string | null;
top: string | null;
anchor: 'left' | 'right';
} {
let left = 0;
let anchor: Position = Position.Left;

const annotationXOffset = window.pageXOffset + container.left + chartDimensions.left + tooltipAnchor.left;
if (chartDimensions.left + tooltipAnchor.left + tooltip.width + padding >= container.width) {
left = annotationXOffset - tooltip.width - padding;
if (chartDimensions.left + tooltipAnchor.left + portalWidth + padding >= container.width) {
left = annotationXOffset - portalWidth - padding;
anchor = Position.Right;
} else {
left = annotationXOffset + padding;
}
Expand All @@ -50,5 +56,6 @@ export function getFinalAnnotationTooltipPosition(
return {
left: `${Math.round(left)}px`,
top: `${Math.round(top)}px`,
anchor,
};
}
76 changes: 66 additions & 10 deletions src/chart_types/xy_chart/crosshair/crosshair_utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,10 @@ describe('Tooltip position', () => {
top: 0,
left: 0,
};
const portalWidth = 50;
describe('horizontal rotated chart', () => {
it('can position the tooltip on the top left corner', () => {
const position = getFinalTooltipPosition(container, tooltip, {
const position = getFinalTooltipPosition(container, tooltip, portalWidth, {
isRotated: false,
y1: 0,
y0: 0,
Expand All @@ -44,8 +45,9 @@ describe('Tooltip position', () => {
expect(position.left).toBe('25px');
expect(position.top).toBe('10px');
});

it('can position the tooltip on the bottom left corner', () => {
const position = getFinalTooltipPosition(container, tooltip, {
const position = getFinalTooltipPosition(container, tooltip, portalWidth, {
isRotated: false,
y0: 90,
y1: 90,
Expand All @@ -56,34 +58,61 @@ describe('Tooltip position', () => {
expect(position.left).toBe('25px');
expect(position.top).toBe('80px');
});

it('can position the tooltip on the top right corner', () => {
const position = getFinalTooltipPosition(container, tooltip, {
const position = getFinalTooltipPosition(container, tooltip, portalWidth, {
isRotated: false,
y0: 0,
y1: 0,
x0: 100,
x1: 100,
padding: 5,
});
expect(position.left).toBe('65px');
expect(position.left).toBe('55px');
expect(position.top).toBe('10px');
});

it('can position the tooltip on the bottom right corner', () => {
const position = getFinalTooltipPosition(container, tooltip, {
const position = getFinalTooltipPosition(container, tooltip, portalWidth, {
isRotated: false,
y0: 90,
y1: 90,
x0: 100,
x1: 100,
padding: 5,
});
expect(position.left).toBe('65px');
expect(position.left).toBe('55px');
expect(position.top).toBe('80px');
});

it('should render on right if portal width is within right side', () => {
const position = getFinalTooltipPosition(container, tooltip, 44, {
isRotated: false,
y0: 0,
y1: 0,
x0: 50,
x1: 50,
padding: 5,
});
expect(position.left).toBe('65px');
});

it('should render on left if portal width is NOT within right side', () => {
const position = getFinalTooltipPosition(container, tooltip, 46, {
isRotated: false,
y0: 0,
y1: 0,
x0: 50,
x1: 50,
padding: 5,
});
expect(position.left).toBe('9px');
});
});

describe('vertical rotated chart', () => {
it('can position the tooltip on the top left corner', () => {
const position = getFinalTooltipPosition(container, tooltip, {
const position = getFinalTooltipPosition(container, tooltip, portalWidth, {
isRotated: true,
y0: 0,
y1: 0,
Expand All @@ -94,8 +123,9 @@ describe('Tooltip position', () => {
expect(position.left).toBe('20px');
expect(position.top).toBe('15px');
});

it('can position the tooltip on the bottom left corner', () => {
const position = getFinalTooltipPosition(container, tooltip, {
const position = getFinalTooltipPosition(container, tooltip, portalWidth, {
isRotated: true,
y0: 90,
y1: 90,
Expand All @@ -106,8 +136,9 @@ describe('Tooltip position', () => {
expect(position.left).toBe('20px');
expect(position.top).toBe('65px');
});

it('can position the tooltip on the top right corner', () => {
const position = getFinalTooltipPosition(container, tooltip, {
const position = getFinalTooltipPosition(container, tooltip, portalWidth, {
isRotated: true,
y0: 0,
y1: 0,
Expand All @@ -118,8 +149,9 @@ describe('Tooltip position', () => {
expect(position.left).toBe('70px');
expect(position.top).toBe('15px');
});

it('can position the tooltip on the bottom right corner', () => {
const position = getFinalTooltipPosition(container, tooltip, {
const position = getFinalTooltipPosition(container, tooltip, portalWidth, {
isRotated: true,
y0: 90,
y1: 90,
Expand All @@ -130,5 +162,29 @@ describe('Tooltip position', () => {
expect(position.left).toBe('70px');
expect(position.top).toBe('65px');
});

it('should render on right if portal width is within right side', () => {
const position = getFinalTooltipPosition(container, tooltip, 44, {
isRotated: true,
y0: 0,
y1: 0,
x0: 50,
x1: 50,
padding: 5,
});
expect(position.left).toBe('60px');
});

it('should render on left if portal width is NOT within right side', () => {
const position = getFinalTooltipPosition(container, tooltip, 51, {
isRotated: true,
y0: 0,
y1: 0,
x0: 50,
x1: 50,
padding: 5,
});
expect(position.left).toBe('70px');
});
});
});
21 changes: 21 additions & 0 deletions src/chart_types/xy_chart/renderer/dom/annotation_tooltips.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ class AnnotationTooltipComponent extends React.Component<AnnotationTooltipProps>
static displayName = 'AnnotationTooltip';
portalNode: HTMLDivElement | null = null;
tooltipRef: React.RefObject<HTMLDivElement>;
/**
* Max allowable width for tooltip to grow to. Used to determine container fit.
*
* @unit px
*/
static MAX_WIDTH = 256;

constructor(props: AnnotationTooltipProps) {
super(props);
Expand All @@ -71,6 +77,7 @@ class AnnotationTooltipComponent extends React.Component<AnnotationTooltipProps>
} else {
this.portalNode = document.createElement('div');
this.portalNode.id = ANNOTATION_CONTAINER_ID;
this.portalNode.style.width = `${AnnotationTooltipComponent.MAX_WIDTH}px`;
document.body.appendChild(this.portalNode);
}
}
Expand All @@ -96,12 +103,15 @@ class AnnotationTooltipComponent extends React.Component<AnnotationTooltipProps>
}

const chartContainerBBox = chartContainerRef.current.getBoundingClientRect();
const width = Math.min(AnnotationTooltipComponent.MAX_WIDTH, chartContainerBBox.width * 0.7);
this.portalNode.style.width = `${width}px`;
const tooltipBBox = this.tooltipRef.current.getBoundingClientRect();
const tooltipStyle = getFinalAnnotationTooltipPosition(
chartContainerBBox,
chartDimensions,
tooltipBBox,
tooltipState.anchor,
width,
);

if (tooltipStyle.left) {
Expand All @@ -110,6 +120,17 @@ class AnnotationTooltipComponent extends React.Component<AnnotationTooltipProps>
if (tooltipStyle.top) {
this.portalNode.style.top = tooltipStyle.top;
}

if (tooltipStyle.left) {
this.portalNode.style.left = tooltipStyle.left;
if (this.tooltipRef.current) {
this.tooltipRef.current.style.left = tooltipStyle.anchor === 'right' ? 'auto' : '0px';
this.tooltipRef.current.style.right = tooltipStyle.anchor === 'right' ? '0px' : 'auto';
}
}
if (tooltipStyle.top) {
this.portalNode.style.top = tooltipStyle.top;
}
}

componentWillUnmount() {
Expand Down
2 changes: 1 addition & 1 deletion src/components/_annotation.scss
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#echAnnotationContainerPortal {
position: absolute;
width: 256px;
pointer-events: none;
}
.echAnnotation {
pointer-events: none;
Expand Down
5 changes: 3 additions & 2 deletions src/components/tooltip/_tooltip.scss
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#echTooltipContainerPortal {
position: absolute;
width: 256px;
pointer-events: none;
z-index: 10000000;
}
.echTooltip {
position: absolute;
Expand Down Expand Up @@ -41,7 +42,7 @@
font-weight: $euiFontWeightBold;
text-align: right;
font-feature-settings: 'tnum';
margin-left: 8px;
margin-left: $euiSizeS;
}

&__rowHighlighted {
Expand Down
20 changes: 20 additions & 0 deletions src/components/tooltip/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you 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. */

/** @internal */
export { TooltipPortal as Tooltip } from './tooltip_portal';
Loading

0 comments on commit f411771

Please sign in to comment.