From 96bc83d724f15964e3c4361ec6b47bb27247f8f2 Mon Sep 17 00:00:00 2001 From: i-subham Date: Thu, 3 Jul 2025 01:22:29 +0530 Subject: [PATCH 01/13] Adding tool tip to crayon bar chart xaxistip --- js/packages/react-ui/package.json | 3 +- .../components/Charts/BarChart/BarChart.tsx | 363 +++++++++--------- .../Charts/shared/XAxisTick/XAxisTick.tsx | 37 +- .../Charts/shared/XAxisTick/xAxisTick.scss | 87 +++++ js/pnpm-lock.yaml | 36 ++ 5 files changed, 334 insertions(+), 192 deletions(-) diff --git a/js/packages/react-ui/package.json b/js/packages/react-ui/package.json index 37c8ff193..5c4e6bcd2 100644 --- a/js/packages/react-ui/package.json +++ b/js/packages/react-ui/package.json @@ -66,6 +66,7 @@ "@radix-ui/react-switch": "^1.1.2", "@radix-ui/react-tabs": "^1.1.2", "@radix-ui/react-toggle-group": "^1.1.2", + "@radix-ui/react-tooltip": "^1.2.7", "clsx": "^2.1.1", "date-fns": "^4.1.0", "lodash-es": "^4.17.21", @@ -96,6 +97,7 @@ "@storybook/theming": "^8.5.3", "@types/lodash-es": "^4.17.12", "@types/node": "^22.12.0", + "@types/node-fetch": "2.6.11", "@types/react": ">=17.0.0", "@types/react-dom": ">=17.0.0", "@types/react-syntax-highlighter": "^15.5.13", @@ -108,7 +110,6 @@ "eslint-plugin-storybook": "^0.11.1", "eslint-plugin-unused-imports": "^3.1.0", "form-data": "^4.0.0", - "@types/node-fetch": "2.6.11", "postcss": "^8.5.1", "postcss-js": "^4.0.1", "postcss-remove-duplicate-values": "^1.0.0", diff --git a/js/packages/react-ui/src/components/Charts/BarChart/BarChart.tsx b/js/packages/react-ui/src/components/Charts/BarChart/BarChart.tsx index 3a07e5b9b..3b198e919 100644 --- a/js/packages/react-ui/src/components/Charts/BarChart/BarChart.tsx +++ b/js/packages/react-ui/src/components/Charts/BarChart/BarChart.tsx @@ -2,6 +2,7 @@ import clsx from "clsx"; import { ChevronLeft, ChevronRight } from "lucide-react"; import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { Bar, BarChart as RechartsBarChart, XAxis, YAxis } from "recharts"; +import * as Tooltip from "@radix-ui/react-tooltip"; import { useId } from "../../../polyfills"; import { IconButton } from "../../IconButton"; import { useTheme } from "../../ThemeProvider"; @@ -278,193 +279,195 @@ const BarChartComponent = ({ ); return ( - -
+ -
- {showYAxis && ( -
- {/* Y-axis only chart - synchronized with main chart */} - - +
+ {showYAxis && ( +
+ {/* Y-axis only chart - synchronized with main chart */} + } - /> - {/* Invisible bars to maintain scale synchronization */} - {dataKeys.map((key) => { - return ( - - ); - })} - -
- )} -
- - + } + /> + {/* Invisible bars to maintain scale synchronization */} + {dataKeys.map((key) => { + return ( + + ); + })} + +
+ )} +
+ - {grid && cartesianGrid()} - } - orientation="bottom" - // gives the padding on the 2 sides see the function for reference - padding={padding} - /> - {/* Y-axis is rendered in the separate synchronized chart */} - - } - cursor={{ - fill: "var(--crayon-sunk-fills)", - stroke: "var(--crayon-stroke-default)", - opacity: 1, - strokeWidth: 1, + } - offset={15} - /> - - {dataKeys.map((key, index) => { - const transformedKey = transformedKeys[key]; - const color = `var(--color-${transformedKey})`; - const isFirstInStack = index === 0; - const isLastInStack = index === dataKeys.length - 1; - - return ( - - } - /> - ); - })} - - + onClick={onBarsClick} + onMouseMove={handleChartMouseMove} + onMouseLeave={handleChartMouseLeave} + barGap={BAR_GAP} + barCategoryGap={BAR_CATEGORY_GAP} + syncId={chartSyncID} + > + {grid && cartesianGrid()} + } + orientation="bottom" + // gives the padding on the 2 sides see the function for reference + padding={padding} + /> + {/* Y-axis is rendered in the separate synchronized chart */} + + } + cursor={{ + fill: "var(--crayon-sunk-fills)", + stroke: "var(--crayon-stroke-default)", + opacity: 1, + strokeWidth: 1, + }} + content={} + offset={15} + /> + + {dataKeys.map((key, index) => { + const transformedKey = transformedKeys[key]; + const color = `var(--color-${transformedKey})`; + const isFirstInStack = index === 0; + const isLastInStack = index === dataKeys.length - 1; + + return ( + + } + /> + ); + })} + + +
+ {isSideBarTooltipOpen && }
- {isSideBarTooltipOpen && } -
- {/* if the data width is greater than the effective width, then show the scroll buttons */} - {dataWidth > effectiveWidth && ( -
- } - variant="secondary" - onClick={scrollLeft} - size="extra-small" - disabled={!canScrollLeft} - /> - - } - variant="secondary" - size="extra-small" - onClick={scrollRight} - disabled={!canScrollRight} + {/* if the data width is greater than the effective width, then show the scroll buttons */} + {dataWidth > effectiveWidth && ( +
+ } + variant="secondary" + onClick={scrollLeft} + size="extra-small" + disabled={!canScrollLeft} + /> + + } + variant="secondary" + size="extra-small" + onClick={scrollRight} + disabled={!canScrollRight} + /> +
+ )} + {legend && ( + -
- )} - {legend && ( - - )} -
-
+ )} +
+
+ ); }; diff --git a/js/packages/react-ui/src/components/Charts/shared/XAxisTick/XAxisTick.tsx b/js/packages/react-ui/src/components/Charts/shared/XAxisTick/XAxisTick.tsx index 8a1b40d80..2862a8744 100644 --- a/js/packages/react-ui/src/components/Charts/shared/XAxisTick/XAxisTick.tsx +++ b/js/packages/react-ui/src/components/Charts/shared/XAxisTick/XAxisTick.tsx @@ -1,3 +1,4 @@ +import * as Tooltip from "@radix-ui/react-tooltip"; import React from "react"; interface XAxisTickProps { x?: number; @@ -61,17 +62,31 @@ const XAxisTick = React.forwardRef((props, ref) => return ( - - {value} - {displayValue} - + + + + {displayValue} + + + + + {value} + + + + ); }); diff --git a/js/packages/react-ui/src/components/Charts/shared/XAxisTick/xAxisTick.scss b/js/packages/react-ui/src/components/Charts/shared/XAxisTick/xAxisTick.scss index 328931058..1bf9e3c0a 100644 --- a/js/packages/react-ui/src/components/Charts/shared/XAxisTick/xAxisTick.scss +++ b/js/packages/react-ui/src/components/Charts/shared/XAxisTick/xAxisTick.scss @@ -4,3 +4,90 @@ @include cssUtils.typography(label, extra-small); fill: cssUtils.$secondary-text; } + +.crayon-chart-x-axis-tick-tooltip { + z-index: 1000; + border-radius: cssUtils.$rounded-s; + padding: cssUtils.$spacing-xs cssUtils.$spacing-s; + background-color: cssUtils.$bg-container; + border: 1px solid cssUtils.$stroke-default; + box-shadow: cssUtils.$shadow-s; + color: cssUtils.$primary-text; + @include cssUtils.typography(label, extra-small); + max-width: 200px; + word-wrap: break-word; + animation-duration: 0.15s; + animation-timing-function: ease-out; + will-change: transform, opacity; +} + +.crayon-chart-x-axis-tick-tooltip[data-state="delayed-open"], +.crayon-chart-x-axis-tick-tooltip[data-state="instant-open"] { + animation-name: tooltipSlideUpAndFade; +} + +.crayon-chart-x-axis-tick-tooltip[data-side="top"] { + animation-name: tooltipSlideDownAndFade; +} + +.crayon-chart-x-axis-tick-tooltip[data-side="bottom"] { + animation-name: tooltipSlideUpAndFade; +} + +.crayon-chart-x-axis-tick-tooltip[data-side="left"] { + animation-name: tooltipSlideRightAndFade; +} + +.crayon-chart-x-axis-tick-tooltip[data-side="right"] { + animation-name: tooltipSlideLeftAndFade; +} + +.crayon-chart-x-axis-tick-tooltip-arrow { + fill: cssUtils.$bg-container; + stroke: cssUtils.$stroke-default; + stroke-width: 1px; +} + +@keyframes tooltipSlideUpAndFade { + from { + opacity: 0; + transform: translateY(2px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes tooltipSlideDownAndFade { + from { + opacity: 0; + transform: translateY(-2px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes tooltipSlideRightAndFade { + from { + opacity: 0; + transform: translateX(-2px); + } + to { + opacity: 1; + transform: translateX(0); + } +} + +@keyframes tooltipSlideLeftAndFade { + from { + opacity: 0; + transform: translateX(2px); + } + to { + opacity: 1; + transform: translateX(0); + } +} diff --git a/js/pnpm-lock.yaml b/js/pnpm-lock.yaml index 34977c713..090e95148 100644 --- a/js/pnpm-lock.yaml +++ b/js/pnpm-lock.yaml @@ -144,6 +144,9 @@ importers: '@radix-ui/react-toggle-group': specifier: ^1.1.2 version: 1.1.10(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-tooltip': + specifier: ^1.2.7 + version: 1.2.7(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) clsx: specifier: ^2.1.1 version: 2.1.1 @@ -1309,6 +1312,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-tooltip@1.2.7': + resolution: {integrity: sha512-Ap+fNYwKTYJ9pzqW+Xe2HtMRbQ/EeWkj2qykZ6SuEV4iS/o1bZI5ssJbk4D2r8XuDuOBVz/tIx2JObtuqU+5Zw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-use-callback-ref@1.1.1': resolution: {integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==} peerDependencies: @@ -4898,6 +4914,26 @@ snapshots: '@types/react': 19.1.8 '@types/react-dom': 19.1.6(@types/react@19.1.8) + '@radix-ui/react-tooltip@1.2.7(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.8)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.8)(react@19.1.0) + '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.8)(react@19.1.0) + '@radix-ui/react-popper': 1.2.7(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.1.8)(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.8)(react@19.1.0) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.1.8 + '@types/react-dom': 19.1.6(@types/react@19.1.8) + '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.1.8)(react@19.1.0)': dependencies: react: 19.1.0 From 335e6461f9a560902a9f38dff8aaac8dcef7ba07 Mon Sep 17 00:00:00 2001 From: i-subham Date: Thu, 3 Jul 2025 12:34:15 +0530 Subject: [PATCH 02/13] add Radix UI tooltip provider to AreaChart and LineChart components --- .../react-ui/src/components/Charts/AreaChart/AreaChart.tsx | 3 +++ .../react-ui/src/components/Charts/LineChart/LineChart.tsx | 3 +++ 2 files changed, 6 insertions(+) diff --git a/js/packages/react-ui/src/components/Charts/AreaChart/AreaChart.tsx b/js/packages/react-ui/src/components/Charts/AreaChart/AreaChart.tsx index ff3738c9f..733f118bb 100644 --- a/js/packages/react-ui/src/components/Charts/AreaChart/AreaChart.tsx +++ b/js/packages/react-ui/src/components/Charts/AreaChart/AreaChart.tsx @@ -2,6 +2,7 @@ import clsx from "clsx"; import { ChevronLeft, ChevronRight } from "lucide-react"; import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { Area, AreaChart as RechartsAreaChart, XAxis, YAxis } from "recharts"; +import * as Tooltip from "@radix-ui/react-tooltip"; import { useId } from "../../../polyfills"; import { IconButton } from "../../IconButton"; import { ChartConfig, ChartContainer, ChartTooltip } from "../Charts"; @@ -243,6 +244,7 @@ const AreaChartComponent = ({ ); return ( + ({ )} + ); }; diff --git a/js/packages/react-ui/src/components/Charts/LineChart/LineChart.tsx b/js/packages/react-ui/src/components/Charts/LineChart/LineChart.tsx index 1affc555e..60bcd4b62 100644 --- a/js/packages/react-ui/src/components/Charts/LineChart/LineChart.tsx +++ b/js/packages/react-ui/src/components/Charts/LineChart/LineChart.tsx @@ -2,6 +2,7 @@ import clsx from "clsx"; import { ChevronLeft, ChevronRight } from "lucide-react"; import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { Line, LineChart as RechartsLineChart, XAxis, YAxis } from "recharts"; +import * as Tooltip from "@radix-ui/react-tooltip"; import { useId } from "../../../polyfills"; import { IconButton } from "../../IconButton"; import { ChartConfig, ChartContainer, ChartTooltip } from "../Charts"; @@ -240,6 +241,7 @@ export const LineChart = ({ ); return ( + ({ )} + ); }; From f69862cc096b91cc7a2a53bbb432e92b5acbe88d Mon Sep 17 00:00:00 2001 From: i-subham Date: Thu, 3 Jul 2025 14:21:10 +0530 Subject: [PATCH 03/13] Refactor Toolitp and recharts component --- .../components/Charts/AreaChart/AreaChart.tsx | 354 +++++++++--------- .../components/Charts/BarChart/BarChart.tsx | 2 +- .../components/Charts/LineChart/LineChart.tsx | 316 ++++++++-------- .../Charts/shared/XAxisTick/XAxisTick.tsx | 7 +- 4 files changed, 337 insertions(+), 342 deletions(-) diff --git a/js/packages/react-ui/src/components/Charts/AreaChart/AreaChart.tsx b/js/packages/react-ui/src/components/Charts/AreaChart/AreaChart.tsx index 733f118bb..c2c5d4d7a 100644 --- a/js/packages/react-ui/src/components/Charts/AreaChart/AreaChart.tsx +++ b/js/packages/react-ui/src/components/Charts/AreaChart/AreaChart.tsx @@ -1,8 +1,8 @@ +import * as Tooltip from "@radix-ui/react-tooltip"; import clsx from "clsx"; import { ChevronLeft, ChevronRight } from "lucide-react"; import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { Area, AreaChart as RechartsAreaChart, XAxis, YAxis } from "recharts"; -import * as Tooltip from "@radix-ui/react-tooltip"; import { useId } from "../../../polyfills"; import { IconButton } from "../../IconButton"; import { ChartConfig, ChartContainer, ChartTooltip } from "../Charts"; @@ -245,189 +245,189 @@ const AreaChartComponent = ({ return ( - -
-
- {showYAxis && ( -
- {/* Y-axis only chart - synchronized with main chart */} - - +
+ {showYAxis && ( +
+ {/* Y-axis only chart - synchronized with main chart */} + } - /> - {/* Invisible area to maintain scale synchronization */} - {dataKeys.map((key) => { - return ( - - ); - })} - -
- )} -
- - + } + /> + {/* Invisible area to maintain scale synchronization */} + {dataKeys.map((key) => { + return ( + + ); + })} + +
+ )} +
+ - {grid && cartesianGrid()} - - } - orientation="bottom" - padding={{ - left: 25, - right: 20, + - - } offset={15} /> - - {dataKeys.map((key) => { - const transformedKey = transformedKeys[key]; - const color = `var(--color-${transformedKey})`; - return ( - - - - - - - ); - })} - - {dataKeys.map((key) => { - const transformedKey = transformedKeys[key]; - const color = `var(--color-${transformedKey})`; - return ( - } - dot={false} - isAnimationActive={isAnimationActive} - /> - ); - })} - - + syncId={chartSyncID} + onClick={onAreaClick} + > + {grid && cartesianGrid()} + + } + orientation="bottom" + padding={{ + left: 25, + right: 20, + }} + /> + + } offset={15} /> + + {dataKeys.map((key) => { + const transformedKey = transformedKeys[key]; + const color = `var(--color-${transformedKey})`; + return ( + + + + + + + ); + })} + + {dataKeys.map((key) => { + const transformedKey = transformedKeys[key]; + const color = `var(--color-${transformedKey})`; + return ( + } + dot={false} + isAnimationActive={isAnimationActive} + /> + ); + })} + + +
+ {isSideBarTooltipOpen && }
- {isSideBarTooltipOpen && } -
- {/* if the data width is greater than the effective width, then show the scroll buttons */} - {dataWidth > effectiveWidth && ( -
- } - variant="secondary" - onClick={scrollLeft} - size="extra-small" - disabled={!canScrollLeft} - /> - } - variant="secondary" - size="extra-small" - onClick={scrollRight} - disabled={!canScrollRight} + {/* if the data width is greater than the effective width, then show the scroll buttons */} + {dataWidth > effectiveWidth && ( +
+ } + variant="secondary" + onClick={scrollLeft} + size="extra-small" + disabled={!canScrollLeft} + /> + } + variant="secondary" + size="extra-small" + onClick={scrollRight} + disabled={!canScrollRight} + /> +
+ )} + {legend && ( + -
- )} - {legend && ( - - )} -
- + )} +
+
); }; diff --git a/js/packages/react-ui/src/components/Charts/BarChart/BarChart.tsx b/js/packages/react-ui/src/components/Charts/BarChart/BarChart.tsx index 3b198e919..1a5eca700 100644 --- a/js/packages/react-ui/src/components/Charts/BarChart/BarChart.tsx +++ b/js/packages/react-ui/src/components/Charts/BarChart/BarChart.tsx @@ -1,8 +1,8 @@ +import * as Tooltip from "@radix-ui/react-tooltip"; import clsx from "clsx"; import { ChevronLeft, ChevronRight } from "lucide-react"; import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { Bar, BarChart as RechartsBarChart, XAxis, YAxis } from "recharts"; -import * as Tooltip from "@radix-ui/react-tooltip"; import { useId } from "../../../polyfills"; import { IconButton } from "../../IconButton"; import { useTheme } from "../../ThemeProvider"; diff --git a/js/packages/react-ui/src/components/Charts/LineChart/LineChart.tsx b/js/packages/react-ui/src/components/Charts/LineChart/LineChart.tsx index 60bcd4b62..e26214412 100644 --- a/js/packages/react-ui/src/components/Charts/LineChart/LineChart.tsx +++ b/js/packages/react-ui/src/components/Charts/LineChart/LineChart.tsx @@ -1,8 +1,8 @@ +import * as Tooltip from "@radix-ui/react-tooltip"; import clsx from "clsx"; import { ChevronLeft, ChevronRight } from "lucide-react"; import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { Line, LineChart as RechartsLineChart, XAxis, YAxis } from "recharts"; -import * as Tooltip from "@radix-ui/react-tooltip"; import { useId } from "../../../polyfills"; import { IconButton } from "../../IconButton"; import { ChartConfig, ChartContainer, ChartTooltip } from "../Charts"; @@ -242,170 +242,170 @@ export const LineChart = ({ return ( - -
-
- {showYAxis && ( -
- {/* Y-axis only chart - synchronized with main chart */} - - +
+ {showYAxis && ( +
+ {/* Y-axis only chart - synchronized with main chart */} + } - /> - {/* Invisible lines to maintain scale synchronization */} - {dataKeys.map((key) => { - return ( - - ); - })} - -
- )} -
- - + } + /> + {/* Invisible lines to maintain scale synchronization */} + {dataKeys.map((key) => { + return ( + + ); + })} + +
+ )} +
+ - {grid && cartesianGrid()} - - } - orientation="bottom" - padding={{ - left: 25, - right: 20, + - - } offset={15} /> - - {dataKeys.map((key) => { - const transformedKey = transformedKeys[key]; - const color = `var(--color-${transformedKey})`; - return ( - } - isAnimationActive={isAnimationActive} - /> - ); - })} - - + syncId={chartSyncID} + onClick={onLineClick} + > + {grid && cartesianGrid()} + + } + orientation="bottom" + padding={{ + left: 25, + right: 20, + }} + /> + + } offset={15} /> + + {dataKeys.map((key) => { + const transformedKey = transformedKeys[key]; + const color = `var(--color-${transformedKey})`; + return ( + } + isAnimationActive={isAnimationActive} + /> + ); + })} + + +
+ {isSideBarTooltipOpen && }
- {isSideBarTooltipOpen && } -
- {/* if the data width is greater than the effective width, then show the scroll buttons */} - {dataWidth > effectiveWidth && ( -
- } - variant="secondary" - onClick={scrollLeft} - size="extra-small" - disabled={!canScrollLeft} - /> - } - variant="secondary" - size="extra-small" - onClick={scrollRight} - disabled={!canScrollRight} + {/* if the data width is greater than the effective width, then show the scroll buttons */} + {dataWidth > effectiveWidth && ( +
+ } + variant="secondary" + onClick={scrollLeft} + size="extra-small" + disabled={!canScrollLeft} + /> + } + variant="secondary" + size="extra-small" + onClick={scrollRight} + disabled={!canScrollRight} + /> +
+ )} + {legend && ( + -
- )} - {legend && ( - - )} -
- + )} +
+
); }; diff --git a/js/packages/react-ui/src/components/Charts/shared/XAxisTick/XAxisTick.tsx b/js/packages/react-ui/src/components/Charts/shared/XAxisTick/XAxisTick.tsx index 2862a8744..2aac17f4b 100644 --- a/js/packages/react-ui/src/components/Charts/shared/XAxisTick/XAxisTick.tsx +++ b/js/packages/react-ui/src/components/Charts/shared/XAxisTick/XAxisTick.tsx @@ -77,13 +77,8 @@ const XAxisTick = React.forwardRef((props, ref) => - + {value} - From 79b068b8426b1d36fc621897054837f1a2b7f2a9 Mon Sep 17 00:00:00 2001 From: i-subham Date: Thu, 3 Jul 2025 17:13:48 +0530 Subject: [PATCH 04/13] Created LabelTooltip and remove radix-ui tooltip from charts and the xaxistic --- .../components/Charts/AreaChart/AreaChart.tsx | 355 +++++++++-------- .../components/Charts/BarChart/BarChart.tsx | 363 +++++++++--------- .../components/Charts/LineChart/LineChart.tsx | 317 ++++++++------- .../shared/LabelTooltip/LabelTooltip.scss | 88 +++++ .../shared/LabelTooltip/LabelTooltip.tsx | 68 ++++ .../Charts/shared/XAxisTick/XAxisTick.tsx | 35 +- .../Charts/shared/XAxisTick/xAxisTick.scss | 87 ----- 7 files changed, 683 insertions(+), 630 deletions(-) create mode 100644 js/packages/react-ui/src/components/Charts/shared/LabelTooltip/LabelTooltip.scss create mode 100644 js/packages/react-ui/src/components/Charts/shared/LabelTooltip/LabelTooltip.tsx diff --git a/js/packages/react-ui/src/components/Charts/AreaChart/AreaChart.tsx b/js/packages/react-ui/src/components/Charts/AreaChart/AreaChart.tsx index c2c5d4d7a..ff3738c9f 100644 --- a/js/packages/react-ui/src/components/Charts/AreaChart/AreaChart.tsx +++ b/js/packages/react-ui/src/components/Charts/AreaChart/AreaChart.tsx @@ -1,4 +1,3 @@ -import * as Tooltip from "@radix-ui/react-tooltip"; import clsx from "clsx"; import { ChevronLeft, ChevronRight } from "lucide-react"; import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"; @@ -244,191 +243,189 @@ const AreaChartComponent = ({ ); return ( - - +
-
-
- {showYAxis && ( -
- {/* Y-axis only chart - synchronized with main chart */} - + {showYAxis && ( +
+ {/* Y-axis only chart - synchronized with main chart */} + + - } - /> - {/* Invisible area to maintain scale synchronization */} - {dataKeys.map((key) => { - return ( - - ); - })} - -
- )} -
- } + /> + {/* Invisible area to maintain scale synchronization */} + {dataKeys.map((key) => { + return ( + + ); + })} + +
+ )} +
+ + - + } + orientation="bottom" + padding={{ + left: 25, + right: 20, }} - syncId={chartSyncID} - onClick={onAreaClick} - > - {grid && cartesianGrid()} - - } - orientation="bottom" - padding={{ - left: 25, - right: 20, - }} - /> - - } offset={15} /> - - {dataKeys.map((key) => { - const transformedKey = transformedKeys[key]; - const color = `var(--color-${transformedKey})`; - return ( - - - - - - - ); - })} - - {dataKeys.map((key) => { - const transformedKey = transformedKeys[key]; - const color = `var(--color-${transformedKey})`; - return ( - } - dot={false} - isAnimationActive={isAnimationActive} - /> - ); - })} - - -
- {isSideBarTooltipOpen && } + /> + + } offset={15} /> + + {dataKeys.map((key) => { + const transformedKey = transformedKeys[key]; + const color = `var(--color-${transformedKey})`; + return ( + + + + + + + ); + })} + + {dataKeys.map((key) => { + const transformedKey = transformedKeys[key]; + const color = `var(--color-${transformedKey})`; + return ( + } + dot={false} + isAnimationActive={isAnimationActive} + /> + ); + })} +
+
- {/* if the data width is greater than the effective width, then show the scroll buttons */} - {dataWidth > effectiveWidth && ( -
- } - variant="secondary" - onClick={scrollLeft} - size="extra-small" - disabled={!canScrollLeft} - /> - } - variant="secondary" - size="extra-small" - onClick={scrollRight} - disabled={!canScrollRight} - /> -
- )} - {legend && ( - - )} + {isSideBarTooltipOpen && }
- - + {/* if the data width is greater than the effective width, then show the scroll buttons */} + {dataWidth > effectiveWidth && ( +
+ } + variant="secondary" + onClick={scrollLeft} + size="extra-small" + disabled={!canScrollLeft} + /> + } + variant="secondary" + size="extra-small" + onClick={scrollRight} + disabled={!canScrollRight} + /> +
+ )} + {legend && ( + + )} +
+ ); }; diff --git a/js/packages/react-ui/src/components/Charts/BarChart/BarChart.tsx b/js/packages/react-ui/src/components/Charts/BarChart/BarChart.tsx index 1a5eca700..3a07e5b9b 100644 --- a/js/packages/react-ui/src/components/Charts/BarChart/BarChart.tsx +++ b/js/packages/react-ui/src/components/Charts/BarChart/BarChart.tsx @@ -1,4 +1,3 @@ -import * as Tooltip from "@radix-ui/react-tooltip"; import clsx from "clsx"; import { ChevronLeft, ChevronRight } from "lucide-react"; import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"; @@ -279,195 +278,193 @@ const BarChartComponent = ({ ); return ( - - +
-
-
- {showYAxis && ( -
- {/* Y-axis only chart - synchronized with main chart */} - + {showYAxis && ( +
+ {/* Y-axis only chart - synchronized with main chart */} + + - } - /> - {/* Invisible bars to maintain scale synchronization */} - {dataKeys.map((key) => { - return ( - - ); - })} - -
- )} -
- } + /> + {/* Invisible bars to maintain scale synchronization */} + {dataKeys.map((key) => { + return ( + + ); + })} + +
+ )} +
+ + - } + orientation="bottom" + // gives the padding on the 2 sides see the function for reference + padding={padding} + /> + {/* Y-axis is rendered in the separate synchronized chart */} + + } + cursor={{ + fill: "var(--crayon-sunk-fills)", + stroke: "var(--crayon-stroke-default)", + opacity: 1, + strokeWidth: 1, }} - onClick={onBarsClick} - onMouseMove={handleChartMouseMove} - onMouseLeave={handleChartMouseLeave} - barGap={BAR_GAP} - barCategoryGap={BAR_CATEGORY_GAP} - syncId={chartSyncID} - > - {grid && cartesianGrid()} - } - orientation="bottom" - // gives the padding on the 2 sides see the function for reference - padding={padding} - /> - {/* Y-axis is rendered in the separate synchronized chart */} - - } - cursor={{ - fill: "var(--crayon-sunk-fills)", - stroke: "var(--crayon-stroke-default)", - opacity: 1, - strokeWidth: 1, - }} - content={} - offset={15} - /> - - {dataKeys.map((key, index) => { - const transformedKey = transformedKeys[key]; - const color = `var(--color-${transformedKey})`; - const isFirstInStack = index === 0; - const isLastInStack = index === dataKeys.length - 1; - - return ( - - } - /> - ); - })} - - -
- {isSideBarTooltipOpen && } + content={} + offset={15} + /> + + {dataKeys.map((key, index) => { + const transformedKey = transformedKeys[key]; + const color = `var(--color-${transformedKey})`; + const isFirstInStack = index === 0; + const isLastInStack = index === dataKeys.length - 1; + + return ( + + } + /> + ); + })} +
+
- {/* if the data width is greater than the effective width, then show the scroll buttons */} - {dataWidth > effectiveWidth && ( -
- } - variant="secondary" - onClick={scrollLeft} - size="extra-small" - disabled={!canScrollLeft} - /> - - } - variant="secondary" - size="extra-small" - onClick={scrollRight} - disabled={!canScrollRight} - /> -
- )} - {legend && ( - - )} + {isSideBarTooltipOpen && }
- - + {/* if the data width is greater than the effective width, then show the scroll buttons */} + {dataWidth > effectiveWidth && ( +
+ } + variant="secondary" + onClick={scrollLeft} + size="extra-small" + disabled={!canScrollLeft} + /> + + } + variant="secondary" + size="extra-small" + onClick={scrollRight} + disabled={!canScrollRight} + /> +
+ )} + {legend && ( + + )} +
+ ); }; diff --git a/js/packages/react-ui/src/components/Charts/LineChart/LineChart.tsx b/js/packages/react-ui/src/components/Charts/LineChart/LineChart.tsx index e26214412..1affc555e 100644 --- a/js/packages/react-ui/src/components/Charts/LineChart/LineChart.tsx +++ b/js/packages/react-ui/src/components/Charts/LineChart/LineChart.tsx @@ -1,4 +1,3 @@ -import * as Tooltip from "@radix-ui/react-tooltip"; import clsx from "clsx"; import { ChevronLeft, ChevronRight } from "lucide-react"; import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"; @@ -241,171 +240,169 @@ export const LineChart = ({ ); return ( - - +
-
-
- {showYAxis && ( -
- {/* Y-axis only chart - synchronized with main chart */} - + {showYAxis && ( +
+ {/* Y-axis only chart - synchronized with main chart */} + + - } - /> - {/* Invisible lines to maintain scale synchronization */} - {dataKeys.map((key) => { - return ( - - ); - })} - -
- )} -
- } + /> + {/* Invisible lines to maintain scale synchronization */} + {dataKeys.map((key) => { + return ( + + ); + })} + +
+ )} +
+ + - + } + orientation="bottom" + padding={{ + left: 25, + right: 20, }} - syncId={chartSyncID} - onClick={onLineClick} - > - {grid && cartesianGrid()} - - } - orientation="bottom" - padding={{ - left: 25, - right: 20, - }} - /> - - } offset={15} /> - - {dataKeys.map((key) => { - const transformedKey = transformedKeys[key]; - const color = `var(--color-${transformedKey})`; - return ( - } - isAnimationActive={isAnimationActive} - /> - ); - })} - - -
- {isSideBarTooltipOpen && } + /> + + } offset={15} /> + + {dataKeys.map((key) => { + const transformedKey = transformedKeys[key]; + const color = `var(--color-${transformedKey})`; + return ( + } + isAnimationActive={isAnimationActive} + /> + ); + })} +
+
- {/* if the data width is greater than the effective width, then show the scroll buttons */} - {dataWidth > effectiveWidth && ( -
- } - variant="secondary" - onClick={scrollLeft} - size="extra-small" - disabled={!canScrollLeft} - /> - } - variant="secondary" - size="extra-small" - onClick={scrollRight} - disabled={!canScrollRight} - /> -
- )} - {legend && ( - - )} + {isSideBarTooltipOpen && }
- - + {/* if the data width is greater than the effective width, then show the scroll buttons */} + {dataWidth > effectiveWidth && ( +
+ } + variant="secondary" + onClick={scrollLeft} + size="extra-small" + disabled={!canScrollLeft} + /> + } + variant="secondary" + size="extra-small" + onClick={scrollRight} + disabled={!canScrollRight} + /> +
+ )} + {legend && ( + + )} +
+ ); }; diff --git a/js/packages/react-ui/src/components/Charts/shared/LabelTooltip/LabelTooltip.scss b/js/packages/react-ui/src/components/Charts/shared/LabelTooltip/LabelTooltip.scss new file mode 100644 index 000000000..ce5ca9420 --- /dev/null +++ b/js/packages/react-ui/src/components/Charts/shared/LabelTooltip/LabelTooltip.scss @@ -0,0 +1,88 @@ +@use "../../../../cssUtils" as cssUtils; + +.crayon-chart-label-tooltip { + z-index: 1000; + border-radius: cssUtils.$rounded-s; + padding: cssUtils.$spacing-xs cssUtils.$spacing-s; + background-color: cssUtils.$bg-container; + border: 1px solid cssUtils.$stroke-default; + box-shadow: cssUtils.$shadow-s; + color: cssUtils.$primary-text; + @include cssUtils.typography(label, extra-small); + max-width: 200px; + word-wrap: break-word; + animation-duration: 0.15s; + animation-timing-function: ease-out; + will-change: transform, opacity; +} + +.crayon-chart-label-tooltip[data-state="delayed-open"], +.crayon-chart-label-tooltip[data-state="instant-open"] { + animation-name: tooltipSlideUpAndFade; +} + +.crayon-chart-label-tooltip[data-side="top"] { + animation-name: tooltipSlideDownAndFade; +} + +.crayon-chart-label-tooltip[data-side="bottom"] { + animation-name: tooltipSlideUpAndFade; +} + +.crayon-chart-label-tooltip[data-side="left"] { + animation-name: tooltipSlideRightAndFade; +} + +.crayon-chart-label-tooltip[data-side="right"] { + animation-name: tooltipSlideLeftAndFade; +} + +.crayon-chart-label-tooltip-arrow { + fill: cssUtils.$bg-container; + stroke: cssUtils.$stroke-default; + stroke-width: 1px; +} + +@keyframes tooltipSlideUpAndFade { + from { + opacity: 0; + transform: translateY(2px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes tooltipSlideDownAndFade { + from { + opacity: 0; + transform: translateY(-2px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes tooltipSlideRightAndFade { + from { + opacity: 0; + transform: translateX(-2px); + } + to { + opacity: 1; + transform: translateX(0); + } +} + +@keyframes tooltipSlideLeftAndFade { + from { + opacity: 0; + transform: translateX(2px); + } + to { + opacity: 1; + transform: translateX(0); + } +} diff --git a/js/packages/react-ui/src/components/Charts/shared/LabelTooltip/LabelTooltip.tsx b/js/packages/react-ui/src/components/Charts/shared/LabelTooltip/LabelTooltip.tsx new file mode 100644 index 000000000..0460a384e --- /dev/null +++ b/js/packages/react-ui/src/components/Charts/shared/LabelTooltip/LabelTooltip.tsx @@ -0,0 +1,68 @@ +import * as Tooltip from "@radix-ui/react-tooltip"; +import React from "react"; + +interface LabelTooltipProviderProps { + children: React.ReactNode; + delayDuration?: number; + skipDelayDuration?: number; + disableHoverableContent?: boolean; +} + +interface LabelTooltipProps { + children: React.ReactNode; + content: string; + side?: "top" | "bottom" | "left" | "right"; + sideOffset?: number; + delayDuration?: number; + className?: string; +} + +const LabelTooltipProvider = React.forwardRef( + (props, ref) => { + const { + children, + delayDuration = 300, + skipDelayDuration = 300, + disableHoverableContent = false, + } = props; + + return ( + + {children} + + ); + }, +); + +LabelTooltipProvider.displayName = "LabelTooltipProvider"; + +const LabelTooltip = React.forwardRef((props, ref) => { + const { + children, + content, + side = "top", + sideOffset = 1, + delayDuration = 300, + className = "crayon-chart-label-tooltip", + } = props; + + return ( + + {children} + + + {content} + + + + ); +}); + +LabelTooltip.displayName = "LabelTooltip"; + +export { LabelTooltip, LabelTooltipProvider }; +export type { LabelTooltipProps, LabelTooltipProviderProps }; diff --git a/js/packages/react-ui/src/components/Charts/shared/XAxisTick/XAxisTick.tsx b/js/packages/react-ui/src/components/Charts/shared/XAxisTick/XAxisTick.tsx index 2aac17f4b..305470fb0 100644 --- a/js/packages/react-ui/src/components/Charts/shared/XAxisTick/XAxisTick.tsx +++ b/js/packages/react-ui/src/components/Charts/shared/XAxisTick/XAxisTick.tsx @@ -1,5 +1,5 @@ -import * as Tooltip from "@radix-ui/react-tooltip"; import React from "react"; +import { LabelTooltip } from "../LabelTooltip/LabelTooltip"; interface XAxisTickProps { x?: number; y?: number; @@ -62,26 +62,19 @@ const XAxisTick = React.forwardRef((props, ref) => return ( - - - - {displayValue} - - - - - {value} - - - + + + {displayValue} + + ); }); diff --git a/js/packages/react-ui/src/components/Charts/shared/XAxisTick/xAxisTick.scss b/js/packages/react-ui/src/components/Charts/shared/XAxisTick/xAxisTick.scss index 1bf9e3c0a..328931058 100644 --- a/js/packages/react-ui/src/components/Charts/shared/XAxisTick/xAxisTick.scss +++ b/js/packages/react-ui/src/components/Charts/shared/XAxisTick/xAxisTick.scss @@ -4,90 +4,3 @@ @include cssUtils.typography(label, extra-small); fill: cssUtils.$secondary-text; } - -.crayon-chart-x-axis-tick-tooltip { - z-index: 1000; - border-radius: cssUtils.$rounded-s; - padding: cssUtils.$spacing-xs cssUtils.$spacing-s; - background-color: cssUtils.$bg-container; - border: 1px solid cssUtils.$stroke-default; - box-shadow: cssUtils.$shadow-s; - color: cssUtils.$primary-text; - @include cssUtils.typography(label, extra-small); - max-width: 200px; - word-wrap: break-word; - animation-duration: 0.15s; - animation-timing-function: ease-out; - will-change: transform, opacity; -} - -.crayon-chart-x-axis-tick-tooltip[data-state="delayed-open"], -.crayon-chart-x-axis-tick-tooltip[data-state="instant-open"] { - animation-name: tooltipSlideUpAndFade; -} - -.crayon-chart-x-axis-tick-tooltip[data-side="top"] { - animation-name: tooltipSlideDownAndFade; -} - -.crayon-chart-x-axis-tick-tooltip[data-side="bottom"] { - animation-name: tooltipSlideUpAndFade; -} - -.crayon-chart-x-axis-tick-tooltip[data-side="left"] { - animation-name: tooltipSlideRightAndFade; -} - -.crayon-chart-x-axis-tick-tooltip[data-side="right"] { - animation-name: tooltipSlideLeftAndFade; -} - -.crayon-chart-x-axis-tick-tooltip-arrow { - fill: cssUtils.$bg-container; - stroke: cssUtils.$stroke-default; - stroke-width: 1px; -} - -@keyframes tooltipSlideUpAndFade { - from { - opacity: 0; - transform: translateY(2px); - } - to { - opacity: 1; - transform: translateY(0); - } -} - -@keyframes tooltipSlideDownAndFade { - from { - opacity: 0; - transform: translateY(-2px); - } - to { - opacity: 1; - transform: translateY(0); - } -} - -@keyframes tooltipSlideRightAndFade { - from { - opacity: 0; - transform: translateX(-2px); - } - to { - opacity: 1; - transform: translateX(0); - } -} - -@keyframes tooltipSlideLeftAndFade { - from { - opacity: 0; - transform: translateX(2px); - } - to { - opacity: 1; - transform: translateX(0); - } -} From 4fe1e5f22b1e2005d51388ac479f6590df2f5462 Mon Sep 17 00:00:00 2001 From: i-subham Date: Thu, 3 Jul 2025 17:30:04 +0530 Subject: [PATCH 05/13] Removed labelTooltip from XaxixTick --- .../src/components/Charts/shared/XAxisTick/XAxisTick.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/js/packages/react-ui/src/components/Charts/shared/XAxisTick/XAxisTick.tsx b/js/packages/react-ui/src/components/Charts/shared/XAxisTick/XAxisTick.tsx index 305470fb0..ca2140bc6 100644 --- a/js/packages/react-ui/src/components/Charts/shared/XAxisTick/XAxisTick.tsx +++ b/js/packages/react-ui/src/components/Charts/shared/XAxisTick/XAxisTick.tsx @@ -1,5 +1,4 @@ import React from "react"; -import { LabelTooltip } from "../LabelTooltip/LabelTooltip"; interface XAxisTickProps { x?: number; y?: number; @@ -62,7 +61,6 @@ const XAxisTick = React.forwardRef((props, ref) => return ( - ((props, ref) => > {displayValue} - ); }); From 01ad03285be57f3d02141dc84b7da4a7d087e880 Mon Sep 17 00:00:00 2001 From: ankit-thesys Date: Thu, 3 Jul 2025 18:22:23 +0530 Subject: [PATCH 06/13] refactor(LabelTooltip): Simplify component structure and improve default prop handling This commit refactors the LabelTooltipProvider component by extracting default values for delayDuration, skipDelayDuration, and disableHoverableContent into constants. The component is also converted to a functional component for improved readability and maintainability. Additionally, the LabelTooltip component now utilizes the default delayDuration constant, enhancing consistency across the tooltip implementation. --- .../shared/LabelTooltip/LabelTooltip.tsx | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/js/packages/react-ui/src/components/Charts/shared/LabelTooltip/LabelTooltip.tsx b/js/packages/react-ui/src/components/Charts/shared/LabelTooltip/LabelTooltip.tsx index 0460a384e..9fcc39c9a 100644 --- a/js/packages/react-ui/src/components/Charts/shared/LabelTooltip/LabelTooltip.tsx +++ b/js/packages/react-ui/src/components/Charts/shared/LabelTooltip/LabelTooltip.tsx @@ -17,28 +17,28 @@ interface LabelTooltipProps { className?: string; } -const LabelTooltipProvider = React.forwardRef( - (props, ref) => { - const { - children, - delayDuration = 300, - skipDelayDuration = 300, - disableHoverableContent = false, - } = props; +const DEFAULT_DELAY_DURATION = 300; +const DEFAULT_SKIP_DELAY_DURATION = 300; +const DEFAULT_DISABLE_HOVERABLE_CONTENT = false; - return ( - - {children} - - ); - }, -); +const LabelTooltipProvider: React.FC = (props) => { + const { + children, + delayDuration = DEFAULT_DELAY_DURATION, + skipDelayDuration = DEFAULT_SKIP_DELAY_DURATION, + disableHoverableContent = DEFAULT_DISABLE_HOVERABLE_CONTENT, + } = props; -LabelTooltipProvider.displayName = "LabelTooltipProvider"; + return ( + + {children} + + ); +}; const LabelTooltip = React.forwardRef((props, ref) => { const { @@ -46,7 +46,7 @@ const LabelTooltip = React.forwardRef((props, content, side = "top", sideOffset = 1, - delayDuration = 300, + delayDuration = DEFAULT_DELAY_DURATION, className = "crayon-chart-label-tooltip", } = props; From 959d909ddd61a2f900073d92c73650a15bd1c19a Mon Sep 17 00:00:00 2001 From: ankit-thesys Date: Thu, 3 Jul 2025 18:51:42 +0530 Subject: [PATCH 07/13] feat(charts): Integrate LabelTooltipProvider into AreaChart, BarChart, and LineChart components This commit enhances the AreaChart, BarChart, and LineChart components by wrapping them with the new LabelTooltipProvider. This integration allows for improved tooltip functionality across these chart components, ensuring a consistent user experience when interacting with chart elements. Additionally, the charts maintain their existing tooltip features while benefiting from the new provider's capabilities. --- .../components/Charts/AreaChart/AreaChart.tsx | 237 ++++++++-------- .../components/Charts/BarChart/BarChart.tsx | 257 +++++++++--------- .../components/Charts/LineChart/LineChart.tsx | 195 ++++++------- .../src/components/Charts/charts.scss | 1 + 4 files changed, 350 insertions(+), 340 deletions(-) diff --git a/js/packages/react-ui/src/components/Charts/AreaChart/AreaChart.tsx b/js/packages/react-ui/src/components/Charts/AreaChart/AreaChart.tsx index 9b11960d4..cd546f97a 100644 --- a/js/packages/react-ui/src/components/Charts/AreaChart/AreaChart.tsx +++ b/js/packages/react-ui/src/components/Charts/AreaChart/AreaChart.tsx @@ -15,6 +15,7 @@ import { XAxisTick, YAxisTick, } from "../shared"; +import { LabelTooltipProvider } from "../shared/LabelTooltip/LabelTooltip"; import { LegendItem, XAxisTickVariant } from "../types"; import { findNearestSnapPosition, @@ -293,129 +294,131 @@ const AreaChartComponent = ({ }, [showYAxis, chartHeight, data, dataKeys, variant, id, maxLabelHeight]); return ( - -
+ -
- {/* Y-axis of the chart */} - {yAxis} -
- - +
+ {/* Y-axis of the chart */} + {yAxis} +
+ - {grid && cartesianGrid()} - - } - orientation="bottom" - padding={{ - left: X_AXIS_PADDING, - right: X_AXIS_PADDING, + - - } offset={15} /> - - {dataKeys.map((key) => { - const transformedKey = transformedKeys[key]; - const color = `var(--color-${transformedKey})`; - return ( - - - - - - - ); - })} - - {dataKeys.map((key) => { - const transformedKey = transformedKeys[key]; - const color = `var(--color-${transformedKey})`; - return ( - } - dot={false} - isAnimationActive={isAnimationActive} - /> - ); - })} - - + onClick={onAreaClick} + > + {grid && cartesianGrid()} + + } + orientation="bottom" + padding={{ + left: X_AXIS_PADDING, + right: X_AXIS_PADDING, + }} + /> + + } offset={15} /> + + {dataKeys.map((key) => { + const transformedKey = transformedKeys[key]; + const color = `var(--color-${transformedKey})`; + return ( + + + + + + + ); + })} + + {dataKeys.map((key) => { + const transformedKey = transformedKeys[key]; + const color = `var(--color-${transformedKey})`; + return ( + } + dot={false} + isAnimationActive={isAnimationActive} + /> + ); + })} + + +
+ {isSideBarTooltipOpen && }
- {isSideBarTooltipOpen && } -
- {/* if the data width is greater than the effective width, then show the scroll buttons */} - - {legend && ( - - )} -
-
+ {legend && ( + + )} +
+
+ ); }; diff --git a/js/packages/react-ui/src/components/Charts/BarChart/BarChart.tsx b/js/packages/react-ui/src/components/Charts/BarChart/BarChart.tsx index 4f669ceb2..23c7422d2 100644 --- a/js/packages/react-ui/src/components/Charts/BarChart/BarChart.tsx +++ b/js/packages/react-ui/src/components/Charts/BarChart/BarChart.tsx @@ -38,6 +38,7 @@ import { getWidthOfData, getWidthOfGroup, } from "./utils/BarChartUtils"; +import { LabelTooltipProvider } from "../shared/LabelTooltip/LabelTooltip"; // this a technic to get the type of the onClick event of the bar chart // we need to do this because the onClick event type is not exported by recharts @@ -332,139 +333,141 @@ const BarChartComponent = ({ ); return ( - -
+ -
- {/* Y-axis of the chart */} - {yAxis} -
- - +
+ {/* Y-axis of the chart */} + {yAxis} +
+ - {grid && cartesianGrid()} - - } - orientation="bottom" - // gives the padding on the 2 sides see the function for reference - padding={padding} - /> - {/* Y-axis is rendered in the separate synchronized chart */} - - } - cursor={{ - fill: "var(--crayon-sunk-fills)", - stroke: "var(--crayon-stroke-default)", - opacity: 1, - strokeWidth: 1, + } - offset={15} - /> - - {dataKeys.map((key, index) => { - const transformedKey = transformedKeys[key]; - const color = `var(--color-${transformedKey})`; - const isFirstInStack = index === 0; - const isLastInStack = index === dataKeys.length - 1; - - return ( - - } - /> - ); - })} - - + onClick={onBarsClick} + onMouseMove={handleChartMouseMove} + onMouseLeave={handleChartMouseLeave} + barGap={BAR_GAP} + barCategoryGap={BAR_CATEGORY_GAP} + > + {grid && cartesianGrid()} + + } + orientation="bottom" + // gives the padding on the 2 sides see the function for reference + padding={padding} + /> + {/* Y-axis is rendered in the separate synchronized chart */} + + } + cursor={{ + fill: "var(--crayon-sunk-fills)", + stroke: "var(--crayon-stroke-default)", + opacity: 1, + strokeWidth: 1, + }} + content={} + offset={15} + /> + + {dataKeys.map((key, index) => { + const transformedKey = transformedKeys[key]; + const color = `var(--color-${transformedKey})`; + const isFirstInStack = index === 0; + const isLastInStack = index === dataKeys.length - 1; + + return ( + + } + /> + ); + })} + + +
+ {isSideBarTooltipOpen && }
- {isSideBarTooltipOpen && } -
- {/* if the data width is greater than the effective width, then show the scroll buttons */} - - {legend && ( - - )} -
-
+ {legend && ( + + )} +
+
+ ); }; diff --git a/js/packages/react-ui/src/components/Charts/LineChart/LineChart.tsx b/js/packages/react-ui/src/components/Charts/LineChart/LineChart.tsx index 5b1a0038b..b032162dc 100644 --- a/js/packages/react-ui/src/components/Charts/LineChart/LineChart.tsx +++ b/js/packages/react-ui/src/components/Charts/LineChart/LineChart.tsx @@ -15,6 +15,7 @@ import { XAxisTick, YAxisTick, } from "../shared"; +import { LabelTooltipProvider } from "../shared/LabelTooltip/LabelTooltip"; import { LegendItem, XAxisTickVariant } from "../types"; import { findNearestSnapPosition, @@ -302,107 +303,109 @@ export const LineChart = ({ ]); return ( - -
+ -
- {/* Y-axis of the chart */} - {yAxis} -
- - +
+ {/* Y-axis of the chart */} + {yAxis} +
+ - {grid && cartesianGrid()} - - } - orientation="bottom" - padding={{ - left: X_AXIS_PADDING, - right: X_AXIS_PADDING, + - - } offset={15} /> - - {dataKeys.map((key) => { - const transformedKey = transformedKeys[key]; - const color = `var(--color-${transformedKey})`; - return ( - } - isAnimationActive={isAnimationActive} - /> - ); - })} - - + onClick={onLineClick} + > + {grid && cartesianGrid()} + + } + orientation="bottom" + padding={{ + left: X_AXIS_PADDING, + right: X_AXIS_PADDING, + }} + /> + + } offset={15} /> + + {dataKeys.map((key) => { + const transformedKey = transformedKeys[key]; + const color = `var(--color-${transformedKey})`; + return ( + } + isAnimationActive={isAnimationActive} + /> + ); + })} + + +
+ {isSideBarTooltipOpen && }
- {isSideBarTooltipOpen && } -
- {/* if the data width is greater than the effective width, then show the scroll buttons */} - - {legend && ( - - )} -
-
+ {legend && ( + + )} +
+
+ ); }; diff --git a/js/packages/react-ui/src/components/Charts/charts.scss b/js/packages/react-ui/src/components/Charts/charts.scss index d364fc021..9b991dd0e 100644 --- a/js/packages/react-ui/src/components/Charts/charts.scss +++ b/js/packages/react-ui/src/components/Charts/charts.scss @@ -28,6 +28,7 @@ @forward "./shared/StackedLegend/stackedLegend.scss"; @forward "./shared/YAxisTick/yAxisTick.scss"; @forward "./shared/ScrollButtonsHorizontal/scrollButtonsHorizontal.scss"; +@forward "./shared/LabelTooltip/labelTooltip.scss"; // portal tooltip css @forward "./shared/PortalTooltip/portalTooltip.scss"; From 015960d3d17d600c505248d7a93b20cf622a3bcf Mon Sep 17 00:00:00 2001 From: ankit-thesys Date: Thu, 3 Jul 2025 19:05:13 +0530 Subject: [PATCH 08/13] fix(BarChart): Remove duplicate import of LabelTooltipProvider This commit cleans up the BarChart component by removing a duplicate import statement for the LabelTooltipProvider. This change enhances code clarity and maintains consistency in the import structure. --- .../react-ui/src/components/Charts/BarChart/BarChart.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/packages/react-ui/src/components/Charts/BarChart/BarChart.tsx b/js/packages/react-ui/src/components/Charts/BarChart/BarChart.tsx index 23c7422d2..57032796e 100644 --- a/js/packages/react-ui/src/components/Charts/BarChart/BarChart.tsx +++ b/js/packages/react-ui/src/components/Charts/BarChart/BarChart.tsx @@ -19,6 +19,7 @@ import { ScrollButtonsHorizontal } from "../shared/ScrollButtonsHorizontal/Scrol import { XAxisTickVariant, type LegendItem } from "../types"; import { useChartPalette, type PaletteName } from "../utils/PalletUtils"; +import { LabelTooltipProvider } from "../shared/LabelTooltip/LabelTooltip"; import { get2dChartConfig, getColorForDataKey, @@ -38,7 +39,6 @@ import { getWidthOfData, getWidthOfGroup, } from "./utils/BarChartUtils"; -import { LabelTooltipProvider } from "../shared/LabelTooltip/LabelTooltip"; // this a technic to get the type of the onClick event of the bar chart // we need to do this because the onClick event type is not exported by recharts From 30fc0ed69512aa1794a9dfb42f7c5736be7ee42b Mon Sep 17 00:00:00 2001 From: ankit-thesys Date: Thu, 3 Jul 2025 19:06:33 +0530 Subject: [PATCH 09/13] feat(XAxisTick): Enhance tooltip functionality with LabelTooltip integration This commit updates the XAxisTick component to utilize the new LabelTooltip for displaying content. It introduces state management to check for text truncation and conditionally renders the tooltip based on the content's visibility. Additionally, the LabelTooltip component is integrated into both multi-line and angled tick variants, improving user experience by providing contextual information when text is truncated. --- .../shared/LabelTooltip/LabelTooltip.tsx | 19 ++++- .../Charts/shared/XAxisTick/XAxisTick.tsx | 75 ++++++++++++------- 2 files changed, 66 insertions(+), 28 deletions(-) diff --git a/js/packages/react-ui/src/components/Charts/shared/LabelTooltip/LabelTooltip.tsx b/js/packages/react-ui/src/components/Charts/shared/LabelTooltip/LabelTooltip.tsx index 9fcc39c9a..3a8ce48bb 100644 --- a/js/packages/react-ui/src/components/Charts/shared/LabelTooltip/LabelTooltip.tsx +++ b/js/packages/react-ui/src/components/Charts/shared/LabelTooltip/LabelTooltip.tsx @@ -15,6 +15,10 @@ interface LabelTooltipProps { sideOffset?: number; delayDuration?: number; className?: string; + disabled?: boolean; + open?: boolean; + defaultOpen?: boolean; + onOpenChange?: (open: boolean) => void; } const DEFAULT_DELAY_DURATION = 300; @@ -48,10 +52,23 @@ const LabelTooltip = React.forwardRef((props, sideOffset = 1, delayDuration = DEFAULT_DELAY_DURATION, className = "crayon-chart-label-tooltip", + disabled = false, + open, + defaultOpen, + onOpenChange, } = props; + if (disabled) { + return children; + } + return ( - + {children} diff --git a/js/packages/react-ui/src/components/Charts/shared/XAxisTick/XAxisTick.tsx b/js/packages/react-ui/src/components/Charts/shared/XAxisTick/XAxisTick.tsx index f33785593..b94922bac 100644 --- a/js/packages/react-ui/src/components/Charts/shared/XAxisTick/XAxisTick.tsx +++ b/js/packages/react-ui/src/components/Charts/shared/XAxisTick/XAxisTick.tsx @@ -1,5 +1,7 @@ -import React, { useRef } from "react"; +import React, { useLayoutEffect, useRef, useState } from "react"; import { XAxisTickVariant } from "../../types"; +import { LabelTooltip } from "../LabelTooltip/LabelTooltip"; + interface XAxisTickProps { x?: number; y?: number; @@ -43,6 +45,17 @@ const XAxisTick = React.forwardRef((props, ref) => const foreignObjectRef = useRef(null); const spanRef = useRef(null); + const [isMultiLineTruncated, setIsMultiLineTruncated] = useState(false); + + // Check if multiLine text is truncated + useLayoutEffect(() => { + if (variant === "multiLine" && spanRef.current) { + const element = spanRef.current; + // Check if the text is clamped by comparing scrollHeight with clientHeight + const isTruncated = element.scrollHeight > element.clientHeight; + setIsMultiLineTruncated(isTruncated); + } + }, [value, variant, widthOfGroup]); if (x === undefined || y === undefined) { return null; @@ -70,17 +83,18 @@ const XAxisTick = React.forwardRef((props, ref) => boxSizing: "border-box", }} > - - {value} - + + + {value} + +
@@ -89,31 +103,38 @@ const XAxisTick = React.forwardRef((props, ref) => if (variant === "angled") { const displayValue = value; + // For angled text, check if it's truncated based on dots at the end + const isTruncated = displayValue.endsWith("..."); + return ( - - {value} - {displayValue} - + + + {displayValue} + + ); } const displayValue = tickFormatter ? tickFormatter(payload?.value) : value; + // Check if displayValue ends with dots (indicating truncation) + const isTruncated = displayValue.endsWith("..."); return ( - - {value} - {displayValue} - + + + {displayValue} + + ); }); From 99bc7f6bea90e5ba2c7e6345847c3f3a2befdd73 Mon Sep 17 00:00:00 2001 From: ankit-thesys Date: Thu, 3 Jul 2025 19:19:03 +0530 Subject: [PATCH 10/13] refactor(XAxisTick): Remove angled variant and related code This commit removes the "angled" variant from the XAxisTick component, simplifying the implementation by eliminating unnecessary code related to angled text rendering. The XAxisTick type definition is also updated to reflect this change, enhancing clarity and maintainability of the component. --- .../Charts/shared/XAxisTick/XAxisTick.tsx | 23 ------------------- .../src/components/Charts/types/XAxisTicks.ts | 2 +- 2 files changed, 1 insertion(+), 24 deletions(-) diff --git a/js/packages/react-ui/src/components/Charts/shared/XAxisTick/XAxisTick.tsx b/js/packages/react-ui/src/components/Charts/shared/XAxisTick/XAxisTick.tsx index b94922bac..67e9d698d 100644 --- a/js/packages/react-ui/src/components/Charts/shared/XAxisTick/XAxisTick.tsx +++ b/js/packages/react-ui/src/components/Charts/shared/XAxisTick/XAxisTick.tsx @@ -101,29 +101,6 @@ const XAxisTick = React.forwardRef((props, ref) => ); } - if (variant === "angled") { - const displayValue = value; - // For angled text, check if it's truncated based on dots at the end - const isTruncated = displayValue.endsWith("..."); - - return ( - - - - {displayValue} - - - - ); - } - const displayValue = tickFormatter ? tickFormatter(payload?.value) : value; // Check if displayValue ends with dots (indicating truncation) const isTruncated = displayValue.endsWith("..."); diff --git a/js/packages/react-ui/src/components/Charts/types/XAxisTicks.ts b/js/packages/react-ui/src/components/Charts/types/XAxisTicks.ts index 03e2e1370..d7eb6822f 100644 --- a/js/packages/react-ui/src/components/Charts/types/XAxisTicks.ts +++ b/js/packages/react-ui/src/components/Charts/types/XAxisTicks.ts @@ -1 +1 @@ -export type XAxisTickVariant = "singleLine" | "multiLine" | "angled"; +export type XAxisTickVariant = "singleLine" | "multiLine"; From c3e258876afccb76ad777e44688bb1abec614e34 Mon Sep 17 00:00:00 2001 From: ankit-thesys Date: Thu, 3 Jul 2025 19:19:34 +0530 Subject: [PATCH 11/13] chore(react-ui): Bump version to 0.8.5 --- js/packages/react-ui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/packages/react-ui/package.json b/js/packages/react-ui/package.json index 5c4e6bcd2..1b893d457 100644 --- a/js/packages/react-ui/package.json +++ b/js/packages/react-ui/package.json @@ -2,7 +2,7 @@ "type": "module", "name": "@crayonai/react-ui", "license": "MIT", - "version": "0.8.4", + "version": "0.8.5", "description": "Component library for Generative UI SDK", "main": "dist/index.js", "types": "dist/index.d.ts", From 5ee85a04105190cf97a30d225ed9aeb680afad44 Mon Sep 17 00:00:00 2001 From: ankit-thesys Date: Thu, 3 Jul 2025 19:21:31 +0530 Subject: [PATCH 12/13] refactor(useMaxLabelHeight): Remove redundant angled variant handling This commit simplifies the useMaxLabelHeight hook by removing the redundant handling for the "angled" tick variant, as it is no longer necessary following recent refactoring of the XAxisTick component. This change enhances code clarity and maintainability. --- .../react-ui/src/components/Charts/hooks/useMaxLabelHeight.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/js/packages/react-ui/src/components/Charts/hooks/useMaxLabelHeight.tsx b/js/packages/react-ui/src/components/Charts/hooks/useMaxLabelHeight.tsx index ce719a78f..cc78f2d56 100644 --- a/js/packages/react-ui/src/components/Charts/hooks/useMaxLabelHeight.tsx +++ b/js/packages/react-ui/src/components/Charts/hooks/useMaxLabelHeight.tsx @@ -60,8 +60,6 @@ export const useMaxLabelHeight = ( if (tickVariant === "multiLine") { return Math.max(maxLabelHeight + 13, DEFAULT_HEIGHT); - } else if (tickVariant === "angled") { - return Math.max(maxLabelHeight + 13, DEFAULT_HEIGHT); } else { return DEFAULT_HEIGHT; } From 1e093a63b695881a5f395fbc56c721f3f55f673b Mon Sep 17 00:00:00 2001 From: ankit-thesys Date: Thu, 3 Jul 2025 19:24:25 +0530 Subject: [PATCH 13/13] rename scss file --- .../shared/LabelTooltip/{LabelTooltip.scss => labelTooltip.scss} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename js/packages/react-ui/src/components/Charts/shared/LabelTooltip/{LabelTooltip.scss => labelTooltip.scss} (100%) diff --git a/js/packages/react-ui/src/components/Charts/shared/LabelTooltip/LabelTooltip.scss b/js/packages/react-ui/src/components/Charts/shared/LabelTooltip/labelTooltip.scss similarity index 100% rename from js/packages/react-ui/src/components/Charts/shared/LabelTooltip/LabelTooltip.scss rename to js/packages/react-ui/src/components/Charts/shared/LabelTooltip/labelTooltip.scss