From f1c52c16139f501b7fadc5d2a8a2329a9aed61a8 Mon Sep 17 00:00:00 2001 From: Akam Foad <41629832+akamfoad@users.noreply.github.com> Date: Mon, 12 Jun 2023 20:17:53 +0300 Subject: [PATCH] refactor: `DefaultTooltipContent` to be functional component (#3618) ## Description 1. The DefaultTooltipComponent is now a functional component. 2. The result of formatter function is either `[React.ReactNode, React.ReactNode]` or `React.ReactNode`, instead of being the same type as the inputs, because inputs can be transformed and the result of the format is eventually is rendered in the JSX, so its meaningful to le the user return anything that react accepts for rendering. 3. A story is added for a case of Tooltips that use click to show tooltip instead of hover with pointer. ## Related Issue Fixes: #2976 ## Motivation and Context ## How Has This Been Tested? ## Screenshots (if appropriate): ## Types of changes - [X] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) ## Checklist: - [X] My code follows the code style of this project. - [ ] My change requires a change to the documentation. - [ ] I have updated the documentation accordingly. - [ ] I have added tests to cover my changes. - [X] All new and existing tests passed. --- src/component/DefaultTooltipContent.tsx | 113 +++++++++--------- .../stories/API/tooltip/Tooltip.stories.tsx | 17 +++ .../stories/Examples/Tooltip.stories.tsx | 6 +- 3 files changed, 79 insertions(+), 57 deletions(-) diff --git a/src/component/DefaultTooltipContent.tsx b/src/component/DefaultTooltipContent.tsx index 4b7c381269..0ec1289527 100644 --- a/src/component/DefaultTooltipContent.tsx +++ b/src/component/DefaultTooltipContent.tsx @@ -2,7 +2,7 @@ * @fileOverview Default Tooltip Content */ import _ from 'lodash'; -import React, { PureComponent, CSSProperties, ReactNode } from 'react'; +import React, { CSSProperties, ReactNode } from 'react'; import classNames from 'classnames'; import { isNumOrStr } from '../util/DataUtils'; @@ -19,7 +19,7 @@ export type Formatter = ( item: Payload, index: number, payload: Array>, -) => [TValue, TName] | TValue; +) => [React.ReactNode, TName] | React.ReactNode; export interface Payload { type?: TooltipType; @@ -50,21 +50,24 @@ export interface Props { itemSorter?: (item: Payload) => number | string; } -export class DefaultTooltipContent extends PureComponent< - Props -> { - static displayName = 'DefaultTooltipContent'; - - static defaultProps = { - separator: ' : ', - contentStyle: {}, - itemStyle: {}, - labelStyle: {}, - }; - - renderContent() { - const { payload, separator, formatter, itemStyle, itemSorter } = this.props; +export const DefaultTooltipContent = ( + props: Props, +) => { + const { + separator = ' : ', + contentStyle = {}, + itemStyle = {}, + labelStyle = {}, + payload, + formatter, + itemSorter, + wrapperClassName, + labelClassName, + label, + labelFormatter, + } = props; + const renderContent = () => { if (payload && payload.length) { const listStyle = { padding: 0, margin: 0 }; @@ -81,21 +84,24 @@ export class DefaultTooltipContent - {isNumOrStr(name) ? {name} : null} - {isNumOrStr(name) ? {separator} : null} - {value} + {isNumOrStr(finalName) ? {finalName} : null} + {isNumOrStr(finalName) ? {separator} : null} + {finalValue} {entry.unit || ''} ); @@ -109,38 +115,35 @@ export class DefaultTooltipContent -

- {React.isValidElement(finalLabel) ? finalLabel : `${finalLabel}`} -

- {this.renderContent()} - - ); + if (hasLabel && labelFormatter && payload !== undefined && payload !== null) { + finalLabel = labelFormatter(label, payload); } -} + + return ( +
+

+ {React.isValidElement(finalLabel) ? finalLabel : `${finalLabel}`} +

+ {renderContent()} +
+ ); +}; diff --git a/storybook/stories/API/tooltip/Tooltip.stories.tsx b/storybook/stories/API/tooltip/Tooltip.stories.tsx index ec0bd557d5..8088ace2d3 100644 --- a/storybook/stories/API/tooltip/Tooltip.stories.tsx +++ b/storybook/stories/API/tooltip/Tooltip.stories.tsx @@ -88,6 +88,23 @@ export const MultipleValuedTooltip = { }, }; +export const TriggerTooltipByClick = { + render: (tooltipArgs: Record) => { + return ( + + + + + + {Marks.map(({ marks, fill }) => ( + + ))} + + + ); + }, +}; + const CustomMultipleValueTooltip = ({ active, payload }: CustomTooltipProps) => { if (active && payload && payload.length > 0) { return ( diff --git a/storybook/stories/Examples/Tooltip.stories.tsx b/storybook/stories/Examples/Tooltip.stories.tsx index 89c7ac4d27..c5794d919c 100644 --- a/storybook/stories/Examples/Tooltip.stories.tsx +++ b/storybook/stories/Examples/Tooltip.stories.tsx @@ -15,9 +15,11 @@ export const LockedByClick = { // Their update is interrupted by the click event, so we need to store them in a state. const [tooltipData, setTooltipData] = React.useState<{ payload?: unknown[]; label?: string; x?: number }>({}); - // A custom Tooltip that updates the payload of the tooltip if the chart is locked, and either way always renders using the normal Tooltip. + // A custom Tooltip that updates the payload of the tooltip if the + // chart is locked, and either way always renders using the normal Tooltip. const CustomTooltip = (props: any) => { - // If the chart is locked, and the payload is not empty, and the x position of the tooltip has changed, update the tooltipData. + // If the chart is locked, and the payload is not empty, and the + // x position of the tooltip has changed, update the tooltipData. if (!isLocked && props.payload && props.payload.length > 0 && props.coordinate.x !== tooltipData.x) { setTooltipData({ payload: props.payload, x: props.coordinate.x, label: props.label }); }