diff --git a/src/components/chart-elements/ScatterChart/ScatterChart.tsx b/src/components/chart-elements/ScatterChart/ScatterChart.tsx index 3388d3f7f..e666c588a 100644 --- a/src/components/chart-elements/ScatterChart/ScatterChart.tsx +++ b/src/components/chart-elements/ScatterChart/ScatterChart.tsx @@ -13,7 +13,12 @@ import { } from "recharts"; import { AxisDomain } from "recharts/types/util/types"; -import { constructCategories, constructCategoryColors, getYAxisDomain } from "../common/utils"; +import { + constructCategories, + constructCategoryColors, + deepEqual, + getYAxisDomain, +} from "../common/utils"; import NoData from "../common/NoData"; import BaseAnimationTimingProps from "../common/BaseAnimationTimingProps"; import ChartLegend from "components/chart-elements/common/ChartLegend"; @@ -35,6 +40,19 @@ export type ScatterChartValueFormatter = { size?: ValueFormatter; }; +const renderShape = (props: any, activeNode: any | undefined) => { + const { cx, cy, width, node, fillOpacity } = props; + + return ( + + ); +}; + export interface ScatterChartProps extends BaseAnimationTimingProps, React.HTMLAttributes { @@ -61,6 +79,7 @@ export interface ScatterChartProps minYValue?: number; maxYValue?: number; allowDecimals?: boolean; + onValueChange?: (value: any) => void; noDataText?: string; } @@ -95,12 +114,24 @@ const ScatterChart = React.forwardRef((props, minYValue, maxYValue, allowDecimals = true, + onValueChange, noDataText, className, ...other } = props; const [legendHeight, setLegendHeight] = useState(60); + const [activeNode, setActiveNode] = React.useState(undefined); + function onNodeClick(data: any, index: number, event: React.MouseEvent) { + event.stopPropagation(); + if (onValueChange == null) return; + if (deepEqual(activeNode, data.node)) { + setActiveNode(undefined); + } else { + setActiveNode(data.node); + onValueChange?.(data.payload); + } + } const categories = constructCategories(data, category); const categoryColors = constructCategoryColors(categories, colors); @@ -112,7 +143,7 @@ const ScatterChart = React.forwardRef((props,
{data?.length ? ( - + setActiveNode(undefined)}> {showGridLines ? ( ((props, data={category ? data.filter((d) => d[category] === cat) : data} isAnimationActive={showAnimation} animationDuration={animationDuration} + shape={(props) => renderShape(props, activeNode)} + onClick={onNodeClick} /> ); })} diff --git a/src/components/chart-elements/common/utils.ts b/src/components/chart-elements/common/utils.ts index a7720e04b..97293bc3b 100644 --- a/src/components/chart-elements/common/utils.ts +++ b/src/components/chart-elements/common/utils.ts @@ -32,3 +32,21 @@ export const constructCategories = (data: any[], color?: string): string[] => { }); return Array.from(categories); }; + +export const deepEqual = (obj1: any, obj2: any) => { + if (obj1 === obj2) return true; + + if (typeof obj1 !== "object" || typeof obj2 !== "object" || obj1 === null || obj2 === null) + return false; + + const keys1 = Object.keys(obj1); + const keys2 = Object.keys(obj2); + + if (keys1.length !== keys2.length) return false; + + for (const key of keys1) { + if (!keys2.includes(key) || !deepEqual(obj1[key], obj2[key])) return false; + } + + return true; +}; diff --git a/src/stories/chart-elements/ScatterChart.stories.tsx b/src/stories/chart-elements/ScatterChart.stories.tsx index 59b0d0d46..ee07e2952 100644 --- a/src/stories/chart-elements/ScatterChart.stories.tsx +++ b/src/stories/chart-elements/ScatterChart.stories.tsx @@ -15,20 +15,25 @@ export default { } as ComponentMeta; // More on component templates: https://storybook.js.org/docs/react/writing-stories/introduction#using-args -const ResponsiveTemplate: ComponentStory = (args) => ( - <> - Mobile -
+const ResponsiveTemplate: ComponentStory = (args) => { + if (args.onValueChange?.length === 0) { + args.onValueChange = undefined; + } + return ( + <> + Mobile +
+ + + +
+ Desktop -
- Desktop - - - - -); + + ); +}; const DefaultTemplate: ComponentStory = ({ ...args }) => ( @@ -101,6 +106,14 @@ WithNoDataText.args = { noDataText: "No data, try again later.", }; +export const WithOnValueChange = ResponsiveTemplate.bind({}); +// More on args: https://storybook.js.org/docs/react/writing-stories/args +WithOnValueChange.args = { + ...args, + data, + onValueChange: (value) => alert(JSON.stringify(value)), +}; + export const WithExampleDatas = ResponsiveTemplate.bind({}); // More on args: https://storybook.js.org/docs/react/writing-stories/args WithExampleDatas.args = {