Skip to content

Commit

Permalink
feat: add custom annotation tooltip (#727)
Browse files Browse the repository at this point in the history
Added a prop to render custom annotation tooltip or tooltip details. Add tooltip portal options for more control of placement, offset, and boundary.
Depreciates `renderTooltip` prop in favor of customTooltipDetails prop.

Co-authored-by: Marco Vettorello <[email protected]>
  • Loading branch information
nickofthyme and markov00 authored Jul 6, 2020
1 parent ab5e413 commit 435c67c
Show file tree
Hide file tree
Showing 24 changed files with 397 additions and 180 deletions.
37 changes: 25 additions & 12 deletions api/charts.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ export const AnnotationDomainTypes: Readonly<{
// @public (undocumented)
export type AnnotationId = string;

// @public
export type AnnotationPortalSettings = TooltipPortalSettings<'chart'> & {
customTooltip?: CustomAnnotationTooltip;
customTooltipDetails?: AnnotationTooltipFormatter;
};

// @public (undocumented)
export type AnnotationSpec = LineAnnotationSpec | RectAnnotationSpec;

Expand Down Expand Up @@ -232,7 +238,7 @@ export type BarStyleOverride = RecursivePartial<BarSeriesStyle> | Color | null;
// Warning: (ae-missing-release-tag) "BaseAnnotationSpec" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export interface BaseAnnotationSpec<T extends typeof AnnotationTypes.Rectangle | typeof AnnotationTypes.Line, D extends RectAnnotationDatum | LineAnnotationDatum, S extends RectAnnotationStyle | LineAnnotationStyle> extends Spec {
export interface BaseAnnotationSpec<T extends typeof AnnotationTypes.Rectangle | typeof AnnotationTypes.Line, D extends RectAnnotationDatum | LineAnnotationDatum, S extends RectAnnotationStyle | LineAnnotationStyle> extends Spec, AnnotationPortalSettings {
annotationType: T;
// (undocumented)
chartType: typeof ChartTypes.XYAxis;
Expand Down Expand Up @@ -405,6 +411,12 @@ export const CurveType: Readonly<{
// @public (undocumented)
export type CurveType = $Values<typeof CurveType>;

// @public (undocumented)
export type CustomAnnotationTooltip = ComponentType<{
header?: string;
details?: string;
}> | null;

// @public
export type CustomTooltip = ComponentType<TooltipInfo>;

Expand Down Expand Up @@ -537,11 +549,8 @@ export type ElementOverListener = (elements: Array<XYChartElementEvent | Partiti

// @alpha
export interface ExternalPointerEventsSettings {
tooltip: {
tooltip: TooltipPortalSettings<'chart'> & {
visible?: boolean;
placement?: Placement;
fallbackPlacements?: Placement[];
boundary?: HTMLElement | 'chart';
};
}

Expand Down Expand Up @@ -1432,17 +1441,21 @@ export interface TooltipInfo {
}

// @public
export interface TooltipProps {
boundary?: HTMLElement | 'chart';
customTooltip?: CustomTooltip;
export interface TooltipPortalSettings<B = never> {
boundary?: HTMLElement | B;
fallbackPlacements?: Placement[];
headerFormatter?: TooltipValueFormatter;
offset?: number;
placement?: Placement;
snap?: boolean;
}

// @public
export type TooltipProps = TooltipPortalSettings<'chart'> & {
type?: TooltipType;
// @alpha
snap?: boolean;
headerFormatter?: TooltipValueFormatter;
unit?: string;
}
customTooltip?: CustomTooltip;
};

// @public
export type TooltipSettings = TooltipType | TooltipProps;
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 1 addition & 3 deletions src/chart_types/xy_chart/annotations/rect/tooltip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { Rotation } from '../../../../utils/commons';
import { Dimensions } from '../../../../utils/dimensions';
import { Point } from '../../../../utils/point';
import { AnnotationTypes } from '../../utils/specs';
import { AnnotationTooltipFormatter, AnnotationTooltipState, Bounds } from '../types';
import { AnnotationTooltipState, Bounds } from '../types';
import { getTransformedCursor } from '../utils';
import { isWithinRectBounds } from './dimensions';
import { AnnotationRectProps } from './types';
Expand All @@ -32,7 +32,6 @@ export function computeRectAnnotationTooltipState(
annotationRects: AnnotationRectProps[],
chartRotation: Rotation,
chartDimensions: Dimensions,
renderTooltip?: AnnotationTooltipFormatter,
): AnnotationTooltipState | null {
const rotatedProjectedCursorPosition = getTransformedCursor(cursorPosition, chartDimensions, chartRotation, true);
const totalAnnotationRect = annotationRects.length;
Expand All @@ -54,7 +53,6 @@ export function computeRectAnnotationTooltipState(
top: cursorPosition.y,
},
...(details && { details }),
...(renderTooltip && { renderTooltip }),
};
}
}
Expand Down
34 changes: 30 additions & 4 deletions src/chart_types/xy_chart/annotations/tooltip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
* under the License.
*/

import { TooltipPortalSettings } from '../../../components/portal';
import { Rotation } from '../../../utils/commons';
import { Dimensions } from '../../../utils/dimensions';
import { AnnotationId } from '../../../utils/ids';
Expand Down Expand Up @@ -48,7 +49,9 @@ export function computeAnnotationTooltipState(
if (spec.hideTooltips || !annotationDimension) {
continue;
}
const { groupId } = spec;
const { groupId, customTooltip, customTooltipDetails } = spec;

const tooltipSettings = getTooltipSettings(spec);

if (isLineAnnotation(spec)) {
if (spec.hideLines) {
Expand All @@ -64,22 +67,45 @@ export function computeAnnotationTooltipState(
);

if (lineAnnotationTooltipState) {
return lineAnnotationTooltipState;
return {
...lineAnnotationTooltipState,
tooltipSettings,
customTooltip,
customTooltipDetails,
};
}
} else if (isRectAnnotation(spec)) {
const rectAnnotationTooltipState = computeRectAnnotationTooltipState(
cursorPosition,
annotationDimension as AnnotationRectProps[],
chartRotation,
chartDimensions,
spec.renderTooltip,
);

if (rectAnnotationTooltipState) {
return rectAnnotationTooltipState;
return {
...rectAnnotationTooltipState,
tooltipSettings,
customTooltip,
customTooltipDetails: customTooltipDetails ?? spec.renderTooltip,
};
}
}
}

return null;
}

function getTooltipSettings({
placement,
fallbackPlacements,
boundary,
offset,
}: AnnotationSpec): TooltipPortalSettings<'chart'> {
return {
placement,
fallbackPlacements,
boundary,
offset,
};
}
13 changes: 12 additions & 1 deletion src/chart_types/xy_chart/annotations/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
* under the License.
*/

import { ComponentType } from 'react';

import { TooltipPortalSettings } from '../../../components/portal';
import { Position, Color } from '../../../utils/commons';
import { AnnotationType } from '../utils/specs';
import { AnnotationLineProps } from './line/types';
Expand All @@ -25,6 +28,12 @@ import { AnnotationRectProps } from './rect/types';
/** @public */
export type AnnotationTooltipFormatter = (details?: string) => JSX.Element | null;

/** @public */
export type CustomAnnotationTooltip = ComponentType<{
header?: string;
details?: string;
}> | null;

/**
* The header and description strings for an Annotation
* @internal
Expand Down Expand Up @@ -62,7 +71,9 @@ export interface AnnotationTooltipState {
top: number;
left: number;
};
renderTooltip?: AnnotationTooltipFormatter;
customTooltipDetails?: AnnotationTooltipFormatter;
customTooltip?: CustomAnnotationTooltip;
tooltipSettings?: TooltipPortalSettings<'chart'>;
}

/** @internal */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,21 @@
* under the License.
*/

import React, { useCallback, useMemo, useEffect } from 'react';
import React, { useCallback, useMemo, useEffect, RefObject } from 'react';

import { TooltipPortal, Placement } from '../../../../../components/portal';
import { TooltipPortal, Placement, TooltipPortalSettings } from '../../../../../components/portal';
import { AnnotationTooltipState } from '../../../annotations/types';
import { TooltipContent } from './tooltip_content';

interface RectAnnotationTooltipProps {
interface AnnotationTooltipProps {
state: AnnotationTooltipState | null;
chartRef: HTMLDivElement | null;
chartRef: RefObject<HTMLDivElement>;
chartId: string;
onScroll?: () => void;
}

/** @internal */
export const AnnotationTooltip = ({ state, chartRef, chartId, onScroll }: RectAnnotationTooltipProps) => {
export const AnnotationTooltip = ({ state, chartRef, chartId, onScroll }: AnnotationTooltipProps) => {
const renderTooltip = useCallback(() => {
if (!state || !state.isVisible) {
return null;
Expand All @@ -54,8 +54,22 @@ export const AnnotationTooltip = ({ state, chartRef, chartId, onScroll }: RectAn
}
}, []); // eslint-disable-line react-hooks/exhaustive-deps

const popperSettings = useMemo((): TooltipPortalSettings | undefined => {
const settings = state?.tooltipSettings;
if (!settings) {
return;
}

const { placement, boundary, ...rest } = settings;

return {
...rest,
placement: placement ?? state?.anchor?.position ?? Placement.Right,
boundary: boundary === 'chart' && chartRef.current ? chartRef.current : undefined,
};
}, [state?.tooltipSettings, state?.anchor?.position, chartRef]);

const position = useMemo(() => state?.anchor ?? null, [state?.anchor]);
const placement = useMemo(() => state?.anchor?.position ?? Placement.Right, [state?.anchor?.position]);
if (!state?.isVisible) {
return null;
}
Expand All @@ -65,12 +79,10 @@ export const AnnotationTooltip = ({ state, chartRef, chartId, onScroll }: RectAn
chartId={chartId}
anchor={{
position,
ref: chartRef,
ref: chartRef.current,
}}
visible={state?.isVisible ?? false}
settings={{
placement,
}}
settings={popperSettings}
>
{renderTooltip()}
</TooltipPortal>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ const AnnotationsComponent = ({
<AnnotationTooltip
chartId={chartId}
state={tooltipState}
chartRef={getChartContainerRef().current}
chartRef={getChartContainerRef()}
onScroll={onScroll}
/>
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,22 @@ import { AnnotationTypes } from '../../../../specs';
import { AnnotationTooltipState } from '../../../annotations/types';

/** @internal */
export const TooltipContent = ({ annotationType, header, details, renderTooltip }: AnnotationTooltipState) => {
export const TooltipContent = ({
annotationType,
header,
details,
customTooltip: CustomTooltip,
customTooltipDetails,
}: AnnotationTooltipState) => {
const renderLine = useCallback(() => (
<div className="echAnnotation__tooltip">
<p className="echAnnotation__header">{header}</p>
<div className="echAnnotation__details">{details}</div>
<div className="echAnnotation__details">{customTooltipDetails ? customTooltipDetails(details) : details}</div>
</div>
), [header, details]);
), [header, details, customTooltipDetails]);

const renderRect = useCallback(() => {
const tooltipContent = renderTooltip ? renderTooltip(details) : details;
const tooltipContent = customTooltipDetails ? customTooltipDetails(details) : details;
if (!tooltipContent) {
return null;
}
Expand All @@ -44,7 +50,11 @@ export const TooltipContent = ({ annotationType, header, details, renderTooltip
</div>
</div>
);
}, [details, renderTooltip]);
}, [details, customTooltipDetails]);

if (CustomTooltip) {
return <CustomTooltip details={details} header={header} />;
}

switch (annotationType) {
case AnnotationTypes.Line: {
Expand Down
27 changes: 24 additions & 3 deletions src/chart_types/xy_chart/utils/specs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import { $Values } from 'utility-types';

import { ChartTypes } from '../..';
import { TooltipPortalSettings } from '../../../components/portal/types';
import { ScaleContinuousType } from '../../../scales';
import { ScaleType } from '../../../scales/constants';
import { Spec } from '../../../specs';
Expand All @@ -39,7 +40,7 @@ import {
BubbleSeriesStyle,
} from '../../../utils/themes/theme';
import { PrimitiveValue } from '../../partition_chart/layout/utils/group_by_rollup';
import { AnnotationTooltipFormatter } from '../annotations/types';
import { AnnotationTooltipFormatter, CustomAnnotationTooltip } from '../annotations/types';
import { RawDataSeriesDatum, XYChartSeriesIdentifier } from './series';

/** @public */
Expand Down Expand Up @@ -756,7 +757,9 @@ export type RectAnnotationSpec = BaseAnnotationSpec<
RectAnnotationDatum,
RectAnnotationStyle
> & {
/** Custom rendering function for tooltip */
/**
* @deprecated use customTooltipDetails
*/
renderTooltip?: AnnotationTooltipFormatter;
/**
* z-index of the annotation relative to other elements in the chart
Expand All @@ -765,11 +768,29 @@ export type RectAnnotationSpec = BaseAnnotationSpec<
zIndex?: number;
};

/**
* Portal settings for annotation tooltips
*
* @public
*/
export type AnnotationPortalSettings = TooltipPortalSettings<'chart'> & {
/**
* The react component used to render a custom tooltip
* @public
*/
customTooltip?: CustomAnnotationTooltip;
/**
* The react component used to render a custom tooltip details
* @public
*/
customTooltipDetails?: AnnotationTooltipFormatter;
};

export interface BaseAnnotationSpec<
T extends typeof AnnotationTypes.Rectangle | typeof AnnotationTypes.Line,
D extends RectAnnotationDatum | LineAnnotationDatum,
S extends RectAnnotationStyle | LineAnnotationStyle
> extends Spec {
> extends Spec, AnnotationPortalSettings {
chartType: typeof ChartTypes.XYAxis;
specType: typeof SpecTypes.Annotation;
/**
Expand Down
2 changes: 1 addition & 1 deletion src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@
*/

export { Chart } from './chart';
export { Placement } from './portal';
export { Placement, TooltipPortalSettings } from './portal';
Loading

0 comments on commit 435c67c

Please sign in to comment.