Skip to content

Commit

Permalink
fix(tooltip): add boundary padding (elastic#1065)
Browse files Browse the repository at this point in the history
# Conflicts:
#	api/charts.api.md
#	src/index.ts
  • Loading branch information
nickofthyme committed Mar 9, 2021
1 parent f6074c2 commit 81fd257
Show file tree
Hide file tree
Showing 5 changed files with 45 additions and 6 deletions.
7 changes: 7 additions & 0 deletions api/charts.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1410,6 +1410,12 @@ export interface OrderBy {
direction?: Direction;
}

// Warning: (ae-forgotten-export) The symbol "PerSideDistance" needs to be exported by the entry point index.d.ts
// Warning: (ae-missing-release-tag) "Padding" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export type Padding = PerSideDistance;

// Warning: (ae-missing-release-tag) "PARENT_KEY" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
Expand Down Expand Up @@ -2182,6 +2188,7 @@ export interface TooltipInfo {
// @public
export interface TooltipPortalSettings<B = never> {
boundary?: HTMLElement | B;
boundaryPadding?: Partial<Padding> | number;
fallbackPlacements?: Placement[];
offset?: number;
placement?: Placement;
Expand Down
20 changes: 18 additions & 2 deletions src/components/portal/tooltip_portal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { useRef, useEffect, useCallback, ReactNode, useMemo } from 'react';
import { createPortal } from 'react-dom';

import { mergePartial, isDefined } from '../../utils/common';
import { Padding } from '../../utils/dimensions';
import { TooltipPortalSettings, PortalAnchorRef } from './types';
import { DEFAULT_POPPER_SETTINGS, getOrCreateNode, isHTMLElement } from './utils';

Expand Down Expand Up @@ -56,6 +57,20 @@ type PortalTooltipProps = {
chartId: string;
};

function addToPadding(padding?: Partial<Padding> | number, extra: number = 0): Padding | number | undefined {
if (!padding) return undefined;
if (typeof padding === 'number') return padding + extra;

const { top = 0, right = 0, bottom = 0, left = 0 } = padding;

return {
top: top + extra,
right: right + extra,
bottom: bottom + extra,
left: left + extra,
};
}

const TooltipPortalComponent = ({
anchor,
scope,
Expand Down Expand Up @@ -113,7 +128,7 @@ const TooltipPortalComponent = ({
return;
}

const { fallbackPlacements, placement, boundary, offset } = popperSettings;
const { fallbackPlacements, placement, boundary, offset, boundaryPadding } = popperSettings;
popper.current = createPopper(anchorNode.current, portalNode.current, {
strategy: 'absolute',
placement,
Expand All @@ -128,6 +143,7 @@ const TooltipPortalComponent = ({
name: 'preventOverflow',
options: {
boundary,
padding: boundaryPadding,
},
},
{
Expand All @@ -138,7 +154,7 @@ const TooltipPortalComponent = ({
boundary,
// checks main axis overflow before trying to flip
altAxis: false,
padding: offset || 10,
padding: addToPadding(boundaryPadding, offset),
},
},
],
Expand Down
9 changes: 9 additions & 0 deletions src/components/portal/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@

import { $Values } from 'utility-types';

import { Padding } from '../../utils/dimensions';

/**
* Placement used in positioning tooltip
* @public
Expand Down Expand Up @@ -100,6 +102,13 @@ export interface TooltipPortalSettings<B = never> {
* @defaultValue parent scroll container
*/
boundary?: HTMLElement | B;
/**
* Boundary element padding.
* Used to reduce extents of boundary placement when magins or paddings are used on boundary
*
* @defaultValue 0
*/
boundaryPadding?: Partial<Padding> | number;
/**
* Custom tooltip offset
*/
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export * from './specs';
export { DebugState } from './state/types';
export { toEntries } from './utils/common';
export { CurveType } from './utils/curves';
export { SimplePadding } from './utils/dimensions';
export { SimplePadding, Padding } from './utils/dimensions';
export { timeFormatter, niceTimeFormatter, niceTimeFormatByDay } from './utils/data/formatters';
export { SeriesIdentifier, SeriesKey } from './common/series_id';
export { XYChartSeriesIdentifier, DataSeriesDatum, FilledValues } from './chart_types/xy_chart/utils/series';
Expand Down
13 changes: 10 additions & 3 deletions stories/bar/55_tooltip_boundary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ const rng = getRandomNumberGenerator();
export const Example = () => {
const showAxes = boolean('Show axes', false);
const groups = number('Groups', 5, { min: 2, max: 20, step: 1 });
const offset = number('Offset', 10, { min: 0, step: 1 });
const data = dg.generateGroupedSeries(4, groups).map((d) => {
return {
...d,
Expand All @@ -42,7 +43,7 @@ export const Example = () => {
const red = useRef<HTMLDivElement | null>(null);
const white = useRef<HTMLDivElement | null>(null);
const blue = useRef<HTMLDivElement | null>(null);
const boundaryMap: Record<string, TooltipProps['boundary'] | null> = {
const getBoundary: Record<string, TooltipProps['boundary'] | null> = {
default: undefined,
red: red.current,
white: white.current,
Expand All @@ -62,14 +63,20 @@ export const Example = () => {
},
'default',
);
const boundary = boundaryMap[boundarySting] ?? undefined;
const boundary = getBoundary[boundarySting] ?? undefined;
const boundaryPadding = {
top: number('Boundary top padding', 0, { min: 0 }),
right: number('Boundary right padding', 0, { min: 0 }),
bottom: number('Boundary bottom padding', 0, { min: 0 }),
left: number('Boundary left padding', 0, { min: 0 }),
};

return (
<div ref={red} style={{ backgroundColor: 'red', padding: 30, height: '100%' }}>
<div ref={white} style={{ backgroundColor: 'white', padding: 30, height: '100%' }}>
<div ref={blue} style={{ backgroundColor: 'blue', padding: 30, height: '100%' }}>
<Chart className="story-chart">
<Settings tooltip={{ boundary }} />
<Settings tooltip={{ boundary, boundaryPadding, offset }} />
<Axis id="bottom" hide={!showAxes} position={Position.Bottom} title="Bottom axis" showOverlappingTicks />
<Axis
id="left"
Expand Down

0 comments on commit 81fd257

Please sign in to comment.