diff --git a/packages/charts/api/charts.api.md b/packages/charts/api/charts.api.md index 7ff6b71ec2..cc265f667e 100644 --- a/packages/charts/api/charts.api.md +++ b/packages/charts/api/charts.api.md @@ -2487,7 +2487,7 @@ export const Settings: (props: SFProps; +export const settingsBuildProps: BuildProps; // @public (undocumented) export type SettingsProps = ComponentProps; @@ -2846,7 +2846,7 @@ export function toEntries, S>(array: T[], acces export type ToggleSelectedTooltipItemCallback = (item: TooltipValue) => any; // @public -export const Tooltip: (props: SFProps, "id" | "chartType" | "specType", "body" | "footer" | "header" | "type" | "actions" | "selectionPrompt" | "actionsLoading" | "noActionsLoaded" | "snap" | "showNullValues" | "actionPrompt" | "pinningPrompt" | "maxTooltipItems" | "maxVisibleTooltipItems", "fallbackPlacements" | "placement" | "offset" | "boundary" | "boundaryPadding" | "headerFormatter" | "unit" | "customTooltip" | "stickTo", never>) => null; +export const Tooltip: (props: SFProps, "id" | "chartType" | "specType", "body" | "footer" | "header" | "type" | "snap" | "showNullValues" | "actions" | "actionsLoading" | "noActionsLoaded" | "actionPrompt" | "pinningPrompt" | "selectionPrompt" | "maxTooltipItems" | "maxVisibleTooltipItems", "fallbackPlacements" | "placement" | "offset" | "boundary" | "boundaryPadding" | "headerFormatter" | "unit" | "customTooltip" | "stickTo", never>) => null; // @public export type TooltipAction = { @@ -2857,7 +2857,7 @@ export type TooltipAction, "id" | "chartType" | "specType", "body" | "footer" | "header" | "type" | "actions" | "selectionPrompt" | "actionsLoading" | "noActionsLoaded" | "snap" | "showNullValues" | "actionPrompt" | "pinningPrompt" | "maxTooltipItems" | "maxVisibleTooltipItems", "fallbackPlacements" | "placement" | "offset" | "boundary" | "boundaryPadding" | "headerFormatter" | "unit" | "customTooltip" | "stickTo", never>; +export const tooltipBuildProps: BuildProps, "id" | "chartType" | "specType", "body" | "footer" | "header" | "type" | "snap" | "showNullValues" | "actions" | "actionsLoading" | "noActionsLoaded" | "actionPrompt" | "pinningPrompt" | "selectionPrompt" | "maxTooltipItems" | "maxVisibleTooltipItems", "fallbackPlacements" | "placement" | "offset" | "boundary" | "boundaryPadding" | "headerFormatter" | "unit" | "customTooltip" | "stickTo", never>; // @public export type TooltipCellStyle = Pick; @@ -2905,14 +2905,21 @@ export const TooltipDivider: ({ margin }: TooltipDividerProps) => JSX.Element; // @public (undocumented) export const TooltipFooter: ({ children }: TooltipFooterProps) => JSX.Element; -// Warning: (ae-forgotten-export) The symbol "TooltipHeaderProps" needs to be exported by the entry point index.d.ts -// // @public (undocumented) -export const TooltipHeader: (props: TooltipHeaderProps) => JSX.Element | null; +export const TooltipHeader: (props: TooltipHeaderProps) => JSX.Element | null; + +// @public +export type TooltipHeaderFormatter = (data: PointerValue) => JSX.Element | string; + +// @public (undocumented) +export type TooltipHeaderProps = PropsOrChildrenWithProps<{ + header?: PointerValue | null; + formatter?: TooltipHeaderFormatter; +}>; // @public export interface TooltipInfo { - header: TooltipValue | null; + header: PointerValue | null; values: TooltipValue[]; } @@ -2945,18 +2952,18 @@ export interface TooltipSpec; body: 'default' | 'none' | ComponentType<{ items: TooltipValue[]; - header: TooltipValue | null; + header: PointerValue | null; }>; customTooltip?: CustomTooltip; footer: 'default' | 'none' | ComponentType<{ items: TooltipValue[]; - header: TooltipValue | null; + header: PointerValue | null; }>; header: 'default' | 'none' | ComponentType<{ items: TooltipValue[]; - header: TooltipValue | null; + header: PointerValue | null; }>; - headerFormatter?: TooltipValueFormatter; + headerFormatter?: TooltipHeaderFormatter; maxTooltipItems: number; maxVisibleTooltipItems: number; noActionsLoaded: string | ComponentType<{ diff --git a/packages/charts/src/chart_types/xy_chart/state/selectors/get_tooltip_values_highlighted_geoms.ts b/packages/charts/src/chart_types/xy_chart/state/selectors/get_tooltip_values_highlighted_geoms.ts index a2c3670193..caefa777ec 100644 --- a/packages/charts/src/chart_types/xy_chart/state/selectors/get_tooltip_values_highlighted_geoms.ts +++ b/packages/charts/src/chart_types/xy_chart/state/selectors/get_tooltip_values_highlighted_geoms.ts @@ -30,13 +30,14 @@ import { getChartRotationSelector } from '../../../../state/selectors/get_chart_ import { getSettingsSpecSelector } from '../../../../state/selectors/get_settings_spec'; import { getTooltipInteractionState } from '../../../../state/selectors/get_tooltip_interaction_state'; import { getTooltipSpecSelector } from '../../../../state/selectors/get_tooltip_spec'; +import { PointerValue } from '../../../../state/types'; import { isNil, Rotation } from '../../../../utils/common'; import { isValidPointerOverEvent } from '../../../../utils/events'; import { IndexedGeometry } from '../../../../utils/geometry'; import { Point } from '../../../../utils/point'; import { getTooltipCompareFn } from '../../../../utils/series_sort'; import { isPointOnGeometry } from '../../rendering/utils'; -import { formatTooltip } from '../../tooltip/tooltip'; +import { formatTooltipHeader, formatTooltipValue } from '../../tooltip/tooltip'; import { defaultXYLegendSeriesSort } from '../../utils/default_series_sort_fn'; import { DataSeries } from '../../utils/series'; import { BasicSeriesSpec, AxisSpec } from '../../utils/specs'; @@ -124,7 +125,7 @@ function getTooltipAndHighlightFromValue( } // build the tooltip value list - let header: TooltipValue | null = null; + let header: PointerValue | null = null; const highlightedGeometries: IndexedGeometry[] = []; const xValues = new Set(); const hideNullValues = !tooltip.showNullValues; @@ -160,13 +161,13 @@ function getTooltipAndHighlightFromValue( } // format the tooltip values - const formattedTooltip = formatTooltip(indexedGeometry, spec, false, isHighlighted, hasSingleSeries, yAxis); + const formattedTooltip = formatTooltipValue(indexedGeometry, spec, isHighlighted, hasSingleSeries, yAxis); // format only one time the x value if (!header) { // if we have a tooltipHeaderFormatter, then don't pass in the xAxis as the user will define a formatter const formatterAxis = tooltip.headerFormatter ? undefined : xAxis; - header = formatTooltip(indexedGeometry, spec, true, false, hasSingleSeries, formatterAxis); + header = formatTooltipHeader(indexedGeometry, spec, formatterAxis); } xValues.add(indexedGeometry.value.x); @@ -188,6 +189,7 @@ function getTooltipAndHighlightFromValue( const sortedTooltipValues = values.sort((a, b) => { return tooltipSortFn(a.seriesIdentifier, b.seriesIdentifier); }); + return { tooltip: { header, @@ -206,7 +208,7 @@ export const getHighlightedTooltipTooltipValuesSelector = createCustomCachedSele const highlightedValues = values.tooltip.values.filter((v) => v.isHighlighted); const hasTooltipContent = values.tooltip.values.length > tooltip.maxTooltipItems && highlightedValues.length > 0; - if (!pinned && (isFollowTooltipType(tooltipType) || hasTooltipContent)) { + if (!pinned && !tooltip.customTooltip && (isFollowTooltipType(tooltipType) || hasTooltipContent)) { return { ...values, tooltip: { diff --git a/packages/charts/src/chart_types/xy_chart/tooltip/tooltip.test.ts b/packages/charts/src/chart_types/xy_chart/tooltip/tooltip.test.ts index 7b0331196e..2a3ae92157 100644 --- a/packages/charts/src/chart_types/xy_chart/tooltip/tooltip.test.ts +++ b/packages/charts/src/chart_types/xy_chart/tooltip/tooltip.test.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { formatTooltip } from './tooltip'; +import { formatTooltipHeader, formatTooltipValue } from './tooltip'; import { ChartType } from '../..'; import { MockBarGeometry } from '../../../mocks'; import { MockGlobalSpec, MockSeriesSpec } from '../../../mocks/specs'; @@ -117,7 +117,7 @@ describe('Tooltip formatting', () => { }); test('format simple tooltip', () => { - const tooltipValue = formatTooltip(indexedGeometry, SPEC_1, false, false, false, YAXIS_SPEC); + const tooltipValue = formatTooltipValue(indexedGeometry, SPEC_1, false, false, YAXIS_SPEC); expect(tooltipValue).toBeDefined(); expect(tooltipValue.valueAccessor).toBe('y1'); expect(tooltipValue.label).toBe('bar_1'); @@ -130,41 +130,39 @@ describe('Tooltip formatting', () => { }); it('should set name as spec name when provided', () => { const name = 'test - spec'; - const tooltipValue = formatTooltip(indexedBandedGeometry, { ...SPEC_1, name }, false, false, false, YAXIS_SPEC); + const tooltipValue = formatTooltipValue(indexedBandedGeometry, { ...SPEC_1, name }, false, false, YAXIS_SPEC); expect(tooltipValue.label).toBe(name); }); it('should set name as spec id when name is not provided', () => { - const tooltipValue = formatTooltip(indexedBandedGeometry, SPEC_1, false, false, false, YAXIS_SPEC); + const tooltipValue = formatTooltipValue(indexedBandedGeometry, SPEC_1, false, false, YAXIS_SPEC); expect(tooltipValue.label).toBe(SPEC_1.id); }); test('format banded tooltip - upper', () => { - const tooltipValue = formatTooltip(indexedBandedGeometry, bandedSpec, false, false, false, YAXIS_SPEC); + const tooltipValue = formatTooltipValue(indexedBandedGeometry, bandedSpec, false, false, YAXIS_SPEC); expect(tooltipValue.label).toBe('bar_1 - upper'); }); test('format banded tooltip - y1AccessorFormat', () => { - const tooltipValue = formatTooltip( + const tooltipValue = formatTooltipValue( indexedBandedGeometry, { ...bandedSpec, y1AccessorFormat: ' [max]' }, false, false, - false, YAXIS_SPEC, ); expect(tooltipValue.label).toBe('bar_1 [max]'); }); test('format banded tooltip - y1AccessorFormat as function', () => { - const tooltipValue = formatTooltip( + const tooltipValue = formatTooltipValue( indexedBandedGeometry, { ...bandedSpec, y1AccessorFormat: (label) => `[max] ${label}` }, false, false, - false, YAXIS_SPEC, ); expect(tooltipValue.label).toBe('[max] bar_1'); }); test('format banded tooltip - lower', () => { - const tooltipValue = formatTooltip( + const tooltipValue = formatTooltipValue( { ...indexedBandedGeometry, value: { @@ -175,13 +173,12 @@ describe('Tooltip formatting', () => { bandedSpec, false, false, - false, YAXIS_SPEC, ); expect(tooltipValue.label).toBe('bar_1 - lower'); }); test('format banded tooltip - y0AccessorFormat', () => { - const tooltipValue = formatTooltip( + const tooltipValue = formatTooltipValue( { ...indexedBandedGeometry, value: { @@ -192,13 +189,12 @@ describe('Tooltip formatting', () => { { ...bandedSpec, y0AccessorFormat: ' [min]' }, false, false, - false, YAXIS_SPEC, ); expect(tooltipValue.label).toBe('bar_1 [min]'); }); test('format banded tooltip - y0AccessorFormat as function', () => { - const tooltipValue = formatTooltip( + const tooltipValue = formatTooltipValue( { ...indexedBandedGeometry, value: { @@ -209,7 +205,6 @@ describe('Tooltip formatting', () => { { ...bandedSpec, y0AccessorFormat: (label) => `[min] ${label}` }, false, false, - false, YAXIS_SPEC, ); expect(tooltipValue.label).toBe('[min] bar_1'); @@ -226,7 +221,7 @@ describe('Tooltip formatting', () => { seriesKeys: ['y1'], }, }; - const tooltipValue = formatTooltip(geometry, SPEC_1, false, false, false, YAXIS_SPEC); + const tooltipValue = formatTooltipValue(geometry, SPEC_1, false, false, YAXIS_SPEC); expect(tooltipValue).toBeDefined(); expect(tooltipValue.valueAccessor).toBe('y1'); expect(tooltipValue.label).toBe('bar_1'); @@ -243,7 +238,7 @@ describe('Tooltip formatting', () => { accessor: 'y0', }, }; - const tooltipValue = formatTooltip(geometry, SPEC_1, false, false, false, YAXIS_SPEC); + const tooltipValue = formatTooltipValue(geometry, SPEC_1, false, false, YAXIS_SPEC); expect(tooltipValue).toBeDefined(); expect(tooltipValue.valueAccessor).toBe('y0'); expect(tooltipValue.label).toBe('bar_1'); @@ -260,17 +255,11 @@ describe('Tooltip formatting', () => { accessor: 'y0', }, }; - let tooltipValue = formatTooltip(geometry, SPEC_1, true, false, false, YAXIS_SPEC); - expect(tooltipValue).toBeDefined(); - expect(tooltipValue.valueAccessor).toBe('y0'); - expect(tooltipValue.label).toBe('bar_1'); - expect(tooltipValue.isHighlighted).toBe(false); - expect(tooltipValue.color).toBe('blue'); - expect(tooltipValue.value).toBe(1); - expect(tooltipValue.formattedValue).toBe('1'); - // disable any highlight on x value - tooltipValue = formatTooltip(geometry, SPEC_1, true, true, false, YAXIS_SPEC); - expect(tooltipValue.isHighlighted).toBe(false); + const tooltipHeader = formatTooltipHeader(geometry, SPEC_1, YAXIS_SPEC); + expect(tooltipHeader).toBeDefined(); + expect(tooltipHeader.valueAccessor).toBeUndefined(); + expect(tooltipHeader.value).toBe(1); + expect(tooltipHeader.formattedValue).toBe('1'); }); it('should format ticks with custom formatter from spec', () => { @@ -284,7 +273,7 @@ describe('Tooltip formatting', () => { ...SPEC_1, tickFormat: tickFormatter, }; - const tooltipValue = formatTooltip(indexedGeometry, spec, false, false, false, axisSpec); + const tooltipValue = formatTooltipValue(indexedGeometry, spec, false, false, axisSpec); expect(tooltipValue.value).toBe(10); expect(tooltipValue.formattedValue).toBe('10 spec'); }); @@ -295,13 +284,13 @@ describe('Tooltip formatting', () => { ...YAXIS_SPEC, tickFormat: axisTickFormatter, }; - const tooltipValue = formatTooltip(indexedGeometry, SPEC_1, false, false, false, axisSpec); + const tooltipValue = formatTooltipValue(indexedGeometry, SPEC_1, false, false, axisSpec); expect(tooltipValue.value).toBe(10); expect(tooltipValue.formattedValue).toBe('10 axis'); }); it('should format ticks with default formatter', () => { - const tooltipValue = formatTooltip(indexedGeometry, SPEC_1, false, false, false, YAXIS_SPEC); + const tooltipValue = formatTooltipValue(indexedGeometry, SPEC_1, false, false, YAXIS_SPEC); expect(tooltipValue.value).toBe(10); expect(tooltipValue.formattedValue).toBe('10'); }); @@ -317,9 +306,9 @@ describe('Tooltip formatting', () => { ...SPEC_1, tickFormat: tickFormatter, }; - const tooltipValue = formatTooltip(indexedGeometry, spec, true, false, false, axisSpec); - expect(tooltipValue.value).toBe(1); - expect(tooltipValue.formattedValue).toBe('1 axis'); + const tooltipHeader = formatTooltipHeader(indexedGeometry, spec, axisSpec); + expect(tooltipHeader.value).toBe(1); + expect(tooltipHeader.formattedValue).toBe('1 axis'); }); it('should format header with default formatter from axis', () => { @@ -328,9 +317,9 @@ describe('Tooltip formatting', () => { ...SPEC_1, tickFormat: tickFormatter, }; - const tooltipValue = formatTooltip(indexedGeometry, spec, true, false, false, YAXIS_SPEC); - expect(tooltipValue.value).toBe(1); - expect(tooltipValue.formattedValue).toBe('1'); + const tooltipHeader = formatTooltipHeader(indexedGeometry, spec, YAXIS_SPEC); + expect(tooltipHeader.value).toBe(1); + expect(tooltipHeader.formattedValue).toBe('1'); }); describe('markFormat', () => { @@ -347,7 +336,7 @@ describe('Tooltip formatting', () => { }; it('should format mark value with markFormat', () => { - const tooltipValue = formatTooltip( + const tooltipValue = formatTooltipValue( markIndexedGeometry, { ...SPEC_1, @@ -355,7 +344,6 @@ describe('Tooltip formatting', () => { }, false, false, - false, YAXIS_SPEC, ); expect(tooltipValue).toBeDefined(); @@ -365,7 +353,7 @@ describe('Tooltip formatting', () => { }); it('should format mark value with defaultTickFormatter', () => { - const tooltipValue = formatTooltip(markIndexedGeometry, SPEC_1, false, false, false, YAXIS_SPEC); + const tooltipValue = formatTooltipValue(markIndexedGeometry, SPEC_1, false, false, YAXIS_SPEC); expect(tooltipValue).toBeDefined(); expect(tooltipValue.markValue).toBe(10); expect(tooltipValue.formattedMarkValue).toBe('10'); diff --git a/packages/charts/src/chart_types/xy_chart/tooltip/tooltip.ts b/packages/charts/src/chart_types/xy_chart/tooltip/tooltip.ts index df5b9f4606..7438609bc7 100644 --- a/packages/charts/src/chart_types/xy_chart/tooltip/tooltip.ts +++ b/packages/charts/src/chart_types/xy_chart/tooltip/tooltip.ts @@ -9,6 +9,7 @@ import { LegendItemExtraValues } from '../../../common/legend'; import { SeriesKey } from '../../../common/series_id'; import { TooltipValue } from '../../../specs'; +import { PointerValue } from '../../../state/types'; import { getAccessorFormatLabel } from '../../../utils/accessor'; import { isDefined } from '../../../utils/common'; import { BandedAccessorType, IndexedGeometry } from '../../../utils/geometry'; @@ -49,10 +50,9 @@ export function getLegendItemExtraValues( } /** @internal */ -export function formatTooltip( - { color, value: { x, y, mark, accessor, datum }, seriesIdentifier }: IndexedGeometry, +export function formatTooltipValue( + { color, value: { y, mark, accessor, datum }, seriesIdentifier }: IndexedGeometry, spec: BasicSeriesSpec, - isHeader: boolean, isHighlighted: boolean, hasSingleSeries: boolean, axisSpec?: AxisSpec, @@ -65,18 +65,16 @@ export function formatTooltip( label = getAccessorFormatLabel(formatter, label); } const isVisible = label.length > 0 && (!spec.filterSeriesInTooltip || spec.filterSeriesInTooltip(seriesIdentifier)); - const value = isHeader ? x : y; - const markValue = isHeader || mark === null || Number.isNaN(mark) ? null : mark; + const markValue = mark === null || Number.isNaN(mark) ? null : mark; const tickFormatOptions: TickFormatterOptions | undefined = spec.timeZone ? { timeZone: spec.timeZone } : undefined; - const tickFormatter = - (isHeader ? axisSpec?.tickFormat : spec.tickFormat ?? axisSpec?.tickFormat) ?? defaultTickFormatter; + const tickFormatter = spec.tickFormat ?? axisSpec?.tickFormat ?? defaultTickFormatter; return { seriesIdentifier, valueAccessor: accessor, label, - value, - formattedValue: tickFormatter(value, tickFormatOptions), + value: y, + formattedValue: tickFormatter(y, tickFormatOptions), markValue, ...(isDefined(markValue) && { formattedMarkValue: spec.markFormat @@ -84,8 +82,23 @@ export function formatTooltip( : defaultTickFormatter(markValue), }), color, - isHighlighted: isHighlighted && !isHeader, + isHighlighted, isVisible, datum, }; } + +/** @internal */ +export function formatTooltipHeader( + { value: { x } }: IndexedGeometry, + spec: BasicSeriesSpec, + axisSpec?: AxisSpec, +): PointerValue { + const tickFormatOptions: TickFormatterOptions | undefined = spec.timeZone ? { timeZone: spec.timeZone } : undefined; + const tickFormatter = axisSpec?.tickFormat ?? defaultTickFormatter; + + return { + value: x, + formattedValue: tickFormatter(x, tickFormatOptions), + }; +} diff --git a/packages/charts/src/components/tooltip/components/tooltip_header.tsx b/packages/charts/src/components/tooltip/components/tooltip_header.tsx index 6e6845fc83..f01cff7e76 100644 --- a/packages/charts/src/components/tooltip/components/tooltip_header.tsx +++ b/packages/charts/src/components/tooltip/components/tooltip_header.tsx @@ -8,30 +8,30 @@ import React, { memo } from 'react'; -import { SeriesIdentifier } from '../../../common/series_id'; -import { BaseDatum, TooltipValue, TooltipValueFormatter } from '../../../specs'; +import { BaseDatum, TooltipHeaderFormatter } from '../../../specs'; +import { PointerValue } from '../../../state/types'; import { Datum, renderComplexChildren } from '../../../utils/common'; import { PropsOrChildrenWithProps } from '../types'; -type TooltipHeaderProps< - D extends BaseDatum = Datum, - SI extends SeriesIdentifier = SeriesIdentifier, -> = PropsOrChildrenWithProps<{ - header: TooltipValue | null; - formatter?: TooltipValueFormatter; +/** @public */ +export type TooltipHeaderProps = PropsOrChildrenWithProps<{ + header?: PointerValue | null; + formatter?: TooltipHeaderFormatter; }>; -const TooltipHeaderInner = ( - props: TooltipHeaderProps, -) => { +const TooltipHeaderInner = (props: TooltipHeaderProps) => { if ('children' in props) { return
{renderComplexChildren(props.children)}
; } + const { header, formatter } = props; - if (!header || !header.isVisible) return null; + + if (!header) return null; const formattedValue = formatter ? formatter(header) : header.formattedValue; + if (!formattedValue) return null; + return
{formattedValue}
; }; diff --git a/packages/charts/src/components/tooltip/tooltip.tsx b/packages/charts/src/components/tooltip/tooltip.tsx index c0bf2a4834..f4bd1eb7ab 100644 --- a/packages/charts/src/components/tooltip/tooltip.tsx +++ b/packages/charts/src/components/tooltip/tooltip.tsx @@ -169,10 +169,7 @@ export const TooltipComponent = label) ?? []), - info?.header?.label ?? '', - ]); + const isMostlyRTL = hasMostlyRTLItems(info?.values?.map?.(({ label }) => label) ?? []); const textDirectionality = isMostlyRTL ? 'rtl' : 'ltr'; const columns: TooltipTableColumn[] = [ @@ -222,14 +219,13 @@ export const TooltipComponent = v.displayOnly); - // don't show the tooltip if hidden or no TooltipInfo are available if (!info || !visible) { return null; } - const actionable = actions.length > 0 || !Array.isArray(actions); + const hideActions = (info?.disableActions ?? false) || info?.values.every((v) => v.displayOnly); + const actionable = actions.length > 0 || !Array.isArray(actions); // divider visibility const hasHeader = TooltipCustomHeader !== 'none' && info.header; const hasBody = TooltipCustomBody !== 'none' && info.values.length > 0; diff --git a/packages/charts/src/components/tooltip/types.ts b/packages/charts/src/components/tooltip/types.ts index 3e264983fc..c087fd7690 100644 --- a/packages/charts/src/components/tooltip/types.ts +++ b/packages/charts/src/components/tooltip/types.ts @@ -10,6 +10,7 @@ import { ComponentType, ReactNode } from 'react'; import { SeriesIdentifier } from '../../common/series_id'; import { BaseDatum, TooltipValue, TooltipValueFormatter } from '../../specs'; +import { PointerValue } from '../../state/types'; import { Datum } from '../../utils/common'; /** @@ -20,7 +21,7 @@ export interface TooltipInfo | null; + header: PointerValue | null; /** * The array of {@link TooltipValue}s to show on the tooltip. * On XYAxis chart correspond to the set of y values for each series diff --git a/packages/charts/src/specs/tooltip.ts b/packages/charts/src/specs/tooltip.ts index 1485a3475a..3c5e1c543f 100644 --- a/packages/charts/src/specs/tooltip.ts +++ b/packages/charts/src/specs/tooltip.ts @@ -76,6 +76,12 @@ export type TooltipValueFormatter, ) => JSX.Element | string; +/** + * A header formatter of tooltip {@link PointerValue} + * @public + */ +export type TooltipHeaderFormatter = (data: PointerValue) => JSX.Element | string; + /** * Either a {@link (TooltipProps:type)} or an {@link (TooltipProps:type)} configuration * @public @@ -144,9 +150,9 @@ export interface TooltipSpec; + headerFormatter?: TooltipHeaderFormatter; /** * Unit for event (i.e. `time`, `feet`, `count`, etc.). @@ -177,18 +183,18 @@ export interface TooltipSpec[]; header: TooltipValue | null }>; + header: 'default' | 'none' | ComponentType<{ items: TooltipValue[]; header: PointerValue | null }>; /** * Custom body for tooltip. Ignored when used with `customTooltip`. * Note: This is not the table body but spans the entire tooltip. */ - body: 'default' | 'none' | ComponentType<{ items: TooltipValue[]; header: TooltipValue | null }>; + body: 'default' | 'none' | ComponentType<{ items: TooltipValue[]; header: PointerValue | null }>; /** * Custom footer for tooltip. Ignored when used with `customTooltip`. * Note: This is not the table footers but spans the entire tooltip. */ - footer: 'default' | 'none' | ComponentType<{ items: TooltipValue[]; header: TooltipValue | null }>; + footer: 'default' | 'none' | ComponentType<{ items: TooltipValue[]; header: PointerValue | null }>; /** * Actions to enable tooltip selection diff --git a/packages/charts/src/state/types.ts b/packages/charts/src/state/types.ts index 66c90ef46a..b0cb8416b2 100644 --- a/packages/charts/src/state/types.ts +++ b/packages/charts/src/state/types.ts @@ -137,6 +137,7 @@ export interface DebugState { /** * Contains the value of the non-dependent variable at the point where the mouse * pointer is. + * * @public */ export interface PointerValue { diff --git a/storybook/stories/bar/17_time_stacked.story.tsx b/storybook/stories/bar/17_time_stacked.story.tsx index 2936f2b3a6..4de60862c5 100644 --- a/storybook/stories/bar/17_time_stacked.story.tsx +++ b/storybook/stories/bar/17_time_stacked.story.tsx @@ -13,21 +13,37 @@ import { Axis, BarSeries, Chart, + CustomTooltip, niceTimeFormatByDay, Position, ScaleType, Settings, timeFormatter, + Tooltip, + TooltipContainer, } from '@elastic/charts'; import { KIBANA_METRICS } from '@elastic/charts/src/utils/data_samples/test_dataset_kibana'; import { useBaseTheme } from '../../use_base_theme'; +const CustomTooltipWithSubChart: CustomTooltip = ({ values }) => { + const [value] = values.filter((v) => v.isHighlighted); + return ( + +
Hovering: {value.label}
+
+ ); +}; + export const Example = () => { + const useCustomTooltip = boolean('Use custom tooltip', false); const formatter = timeFormatter(niceTimeFormatByDay(1)); return ( + + {useCustomTooltip && } + = { header: { - seriesIdentifier: { - key: 'groupId{__global__}spec{bars1}yAccessor{y1}splitAccessors{g-a}', - specId: 'bars1', - xAccessor: 'x', - yAccessor: 'y1', - splitAccessors: new Map(), - seriesKeys: ['a', 'y1'], - }, valueAccessor: 'y1', - label: 'a - y1', value: 0, formattedValue: '2022-10-31 00:00:00.666', - markValue: null, - color: '#54B399', - isHighlighted: false, - isVisible: true, - datum: { x: 0, g: 'a', y1: 1, y2: 4 }, }, values: [ { @@ -110,23 +96,9 @@ export const simple: TooltipInfo = { export const long: TooltipInfo = { header: { - seriesIdentifier: { - key: 'groupId{__global__}spec{bars1}yAccessor{y1}splitAccessors{g-a}', - specId: 'bars1', - xAccessor: 'x', - yAccessor: 'y1', - splitAccessors: new Map(), - seriesKeys: ['a', 'y1'], - }, valueAccessor: 'y1', - label: 'a - y1', value: 0, formattedValue: '2022-10-31 00:00:00.666', - markValue: null, - color: '#54B399', - isHighlighted: false, - isVisible: true, - datum: { x: 0, g: 'a', y1: 1, y2: 4 }, }, values: [ { diff --git a/storybook/stories/interactions/1_bar_clicks.story.tsx b/storybook/stories/interactions/1_bar_clicks.story.tsx index 2dac35928f..cea7975847 100644 --- a/storybook/stories/interactions/1_bar_clicks.story.tsx +++ b/storybook/stories/interactions/1_bar_clicks.story.tsx @@ -18,8 +18,7 @@ import { ScaleType, Settings, Tooltip, - TooltipValue, - TooltipValueFormatter, + TooltipHeaderFormatter, } from '@elastic/charts'; import { useBaseTheme } from '../../use_base_theme'; @@ -33,17 +32,17 @@ const onElementListeners = { export const Example = () => { const useObjectAsX = boolean('use object on x', false); - const headerFormatter: TooltipValueFormatter = (tooltip: TooltipValue) => { - if (tooltip.value % 2 === 0) { + const headerFormatter: TooltipHeaderFormatter = ({ value }) => { + if (value % 2 === 0) { return (

special header for even x values

-

{tooltip.value}

+

{value}

); } - return tooltip.value; + return value; }; return (