From e7e3ef446d5766a92dc47c056660d8d9ec5da86c Mon Sep 17 00:00:00 2001 From: tewarig Date: Thu, 7 Aug 2025 16:31:29 +0530 Subject: [PATCH 001/105] feat: line chart pocs --- packages/blade/package.json | 3 +- .../Charts/LineCharts/LineCharts.stories.tsx | 183 +++++++++++++ .../src/components/Charts/LineCharts/index.ts | 25 ++ .../Charts/LineCharts/lineCharts.tsx | 253 ++++++++++++++++++ .../blade/src/components/Charts/README.md | 94 +++++++ packages/blade/src/components/Charts/index.ts | 1 + yarn.lock | 241 ++++++++++++++++- 7 files changed, 798 insertions(+), 2 deletions(-) create mode 100644 packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx create mode 100644 packages/blade/src/components/Charts/LineCharts/index.ts create mode 100644 packages/blade/src/components/Charts/LineCharts/lineCharts.tsx create mode 100644 packages/blade/src/components/Charts/README.md create mode 100644 packages/blade/src/components/Charts/index.ts diff --git a/packages/blade/package.json b/packages/blade/package.json index 25ad0d574f6..16cb3f825d9 100644 --- a/packages/blade/package.json +++ b/packages/blade/package.json @@ -149,7 +149,8 @@ "@mantine/hooks": "6.0.21", "dayjs": "1.11.10", "react-window": "1.8.11", - "react-zoom-pan-pinch": "3.7.0" + "react-zoom-pan-pinch": "3.7.0", + "recharts": "3.1.2" }, "devDependencies": { "http-server": "14.1.1", diff --git a/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx b/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx new file mode 100644 index 00000000000..614c7f3e207 --- /dev/null +++ b/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx @@ -0,0 +1,183 @@ +import type { StoryFn, Meta } from '@storybook/react'; +import React from 'react'; +import { + LineChart, + Line, + XAxis, + YAxis, + CartesianGrid, + Tooltip, + Legend, + ResponsiveContainer, + ReferenceLine, + ChartTooltip, +} from './lineCharts'; +import { Heading } from '~components/Typography/Heading'; +import { Sandbox } from '~utils/storybook/Sandbox'; +import StoryPageWrapper from '~utils/storybook/StoryPageWrapper'; +import { getStyledPropsArgTypes } from '~components/Box/BaseBox/storybookArgTypes'; + +const Page = (): React.ReactElement => { + return ( + + Usage + + {` + import { + LineChart, + Line, + XAxis, + YAxis, + CartesianGrid, + Tooltip, + Legend, + ResponsiveContainer + } from '@razorpay/blade/components'; + + function App() { + const data = [ + { name: 'Jan', sales: 4000 }, + { name: 'Feb', sales: 3000 }, + { name: 'Mar', sales: 2000 }, + ]; + + return ( + + + + + + + + + + + ) + } + + export default App; + `} + + + ); +}; + +export default { + title: 'Components/Charts/LineChart', + component: LineChart, + tags: ['autodocs'], + argTypes: { + ...getStyledPropsArgTypes(), + }, + parameters: { + docs: { + page: Page, + }, + }, +} as Meta; + +// Sample data for charts +const chartData = [ + { month: 'Jan', teamA: 4000, teamB: 2400 }, + { month: 'Feb', teamA: 3000, teamB: 1398 }, + { month: 'Mar', teamA: 2000, teamB: 9800 }, + { month: 'Apr', teamA: 2780, teamB: 3908 }, + { month: 'May', teamA: 1890, teamB: 4800 }, + { month: 'Jun', teamA: 2390, teamB: 3800 }, +]; + +const forecastData = [ + { date: 'Jan', historical: 4000, forecast: null }, + { date: 'Feb', historical: 3000, forecast: null }, + { date: 'Mar', historical: 2000, forecast: null }, + { date: 'Apr', historical: null, forecast: 2780 }, + { date: 'May', historical: null, forecast: 1890 }, + { date: 'Jun', historical: null, forecast: 2390 }, +]; + +// Simple Line Chart Example +export const SimpleLineChart: StoryFn = () => { + return ( +
+ + + + + + + + + + + + +
+ ); +}; + +// Tiny Line Chart Example +export const TinyLineChart: StoryFn = () => { + return ( +
+ + + + + +
+ ); +}; + +// Forecast Line Chart Example +export const ForecastLineChart: StoryFn = () => { + return ( +
+ + + + + + + + + + + +
+ ); +}; + +SimpleLineChart.storyName = 'Simple Line Chart'; +TinyLineChart.storyName = 'Tiny Line Chart'; +ForecastLineChart.storyName = 'Forecast Line Chart'; \ No newline at end of file diff --git a/packages/blade/src/components/Charts/LineCharts/index.ts b/packages/blade/src/components/Charts/LineCharts/index.ts new file mode 100644 index 00000000000..9e07e2616e7 --- /dev/null +++ b/packages/blade/src/components/Charts/LineCharts/index.ts @@ -0,0 +1,25 @@ +export { + LineChart, + Line, + XAxis, + YAxis, + CartesianGrid, + Tooltip, + Legend, + ResponsiveContainer, + ReferenceLine, + ChartTooltip, +} from './lineCharts'; + +export type { + LineChartProps, + LineProps, + XAxisProps, + YAxisProps, + CartesianGridProps, + TooltipProps, + LegendProps, + ResponsiveContainerProps, + ReferenceLineProps, + BladeColorToken, +} from './lineCharts'; diff --git a/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx b/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx new file mode 100644 index 00000000000..aefed12ba38 --- /dev/null +++ b/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx @@ -0,0 +1,253 @@ +import React from 'react'; +import type { ComponentProps } from 'react'; +import styled from 'styled-components'; +import { + LineChart as RechartsLineChart, + Line as RechartsLine, + XAxis as RechartsXAxis, + YAxis as RechartsYAxis, + CartesianGrid as RechartsCartesianGrid, + Tooltip as RechartsTooltip, + Legend as RechartsLegend, + ResponsiveContainer as RechartsResponsiveContainer, + ReferenceLine as RechartsReferenceLine, +} from 'recharts'; +import { useTheme } from '~components/BladeProvider'; +import type { Theme } from '~components/BladeProvider'; +import type { StyledPropsBlade } from '~components/Box/styledProps'; +import { getStyledProps } from '~components/Box/styledProps'; +import { metaAttribute } from '~utils/metaAttribute'; +import BaseBox from '~components/Box/BaseBox'; +import { castWebType } from '~utils'; + +// BladeColorToken type for charts +export type BladeColorToken = + | 'surface.text.gray.normal' + | 'surface.text.gray.muted' + | 'interactive.background.primary.default' + | 'feedback.text.positive.subtle' + | 'feedback.text.negative.subtle' + | 'feedback.text.notice.subtle' + | 'feedback.text.information.subtle' + | string; + +// Chart-specific interfaces based on user specifications +export interface LineProps extends Omit, 'type'> { + type?: 'step' | 'stepAfter' | 'stepBefore' | 'linear' | 'monotone'; + dot?: React.ReactNode; + connectNulls?: boolean; + legendType?: 'none' | 'line' | 'square' | 'diamond' | 'circle' | 'cross' | 'triangle' | 'triangleDown' | 'triangleUp' | 'star' | 'wye'; + dataKey: string; + name?: string; + color?: BladeColorToken; + strokeStyle?: 'dotted' | 'dashed' | 'solid'; +} + +export interface ReferenceLineProps { + y?: number; + x?: number; + label?: string; + color?: BladeColorToken; +} + +// Predefined chart colors using Blade tokens +const getChartColors = (theme: Theme): Record => ({ + primary: theme.colors.interactive.background.primary.default, + secondary: theme.colors.surface.text.gray.normal, + success: theme.colors.feedback.text.positive.subtle, + warning: theme.colors.feedback.text.notice.subtle, + error: theme.colors.feedback.text.negative.subtle, + info: theme.colors.feedback.text.information.subtle, + neutral: theme.colors.surface.text.gray.muted, + grid: theme.colors.surface.border.gray.muted, + background: theme.colors.surface.background.gray.intense, +}); + +// Helper function to resolve color tokens +const resolveColorToken = (color: BladeColorToken | undefined, theme: Theme): string => { + if (!color) return getChartColors(theme).primary; + + if (color.startsWith('surface.') || color.startsWith('feedback.') || color.startsWith('interactive.')) { + const parts = color.split('.'); + let value: any = theme.colors; + for (const part of parts) { + value = value[part]; + } + return value || getChartColors(theme).primary; + } + + return color; +}; + +// TypeScript prop types +export type LineChartProps = Omit, 'margin'> & StyledPropsBlade & { + children?: React.ReactNode; +}; + +export type XAxisProps = ComponentProps; +export type YAxisProps = ComponentProps; +export type CartesianGridProps = ComponentProps; +export type TooltipProps = ComponentProps; +export type LegendProps = ComponentProps; +export type ResponsiveContainerProps = ComponentProps; + +// Styled wrapper for LineChart with predefined margins +const StyledLineChart = styled(RechartsLineChart)<{ theme: Theme }>` + font-family: ${(props) => props.theme.typography.fonts.family.text}; +`; + +// Main components +export const LineChart: React.FC = ({ children, ...props }) => { + const { theme } = useTheme(); + const styledProps = getStyledProps(props); + + // Predefined margins - not exposed to user + const defaultMargin = { + top: 16, + right: 16, + bottom: 16, + left: 16, + }; + + return ( + + + {children} + + + ); +}; + +export const Line: React.FC = ({ + color, + strokeStyle = 'solid', + type = 'monotone', + ...props +}) => { + const { theme } = useTheme(); + const resolvedColor = resolveColorToken(color, theme); + + const strokeDasharray = strokeStyle === 'dashed' ? '5 5' : strokeStyle === 'dotted' ? '2 2' : undefined; + + return ( + + ); +}; + +export const ReferenceLine: React.FC = ({ + color, + label, + ...props +}) => { + const { theme } = useTheme(); + const resolvedColor = resolveColorToken(color, theme); + + return ( + + ); +}; + +export const XAxis: React.FC = (props) => { + const { theme } = useTheme(); + + return ( + + ); +}; + +export const YAxis: React.FC = (props) => { + const { theme } = useTheme(); + + return ( + + ); +}; + +export const CartesianGrid: React.FC = (props) => { + const { theme } = useTheme(); + + return ( + + ); +}; + +export const Tooltip: React.FC = (props) => { + const { theme } = useTheme(); + + return ( + + ); +}; + +export const Legend: React.FC = (props) => { + const { theme } = useTheme(); + + return ( + + ); +}; + +export const ResponsiveContainer: React.FC = (props) => { + return ; +}; + +// Custom ChartTooltip component for forecast charts +export const ChartTooltip: React.FC = (props) => { + return ; +}; diff --git a/packages/blade/src/components/Charts/README.md b/packages/blade/src/components/Charts/README.md new file mode 100644 index 00000000000..4b839112e6e --- /dev/null +++ b/packages/blade/src/components/Charts/README.md @@ -0,0 +1,94 @@ +# Charts Components + +This directory contains Blade's chart components built on top of Recharts with design system styling. + +## Components + +### LineChart Components + +- **LineChart**: Main container component with predefined margins +- **Line**: Chart line with custom styling options (solid, dashed, dotted) +- **ReferenceLine**: Reference lines for markers +- **XAxis/YAxis**: Styled axis components +- **CartesianGrid**: Styled grid component +- **Tooltip**: Themed tooltip +- **Legend**: Themed legend +- **ResponsiveContainer**: Responsive wrapper +- **ChartTooltip**: Custom tooltip for forecast charts + +## Usage Examples + +### Simple Line Chart +```tsx + + + + + + + + + + + + +``` + +### Tiny Line Chart +```tsx + + + + + +``` + +### Forecast Line Chart +```tsx + + + + + + + + + + + +``` + +## Color Tokens + +The components use Blade color tokens: +- `surface.text.gray.normal` +- `surface.text.gray.muted` +- `interactive.background.primary.default` +- `feedback.text.positive.subtle` +- `feedback.text.negative.subtle` +- `feedback.text.notice.subtle` +- `feedback.text.information.subtle` + +## Implementation Status + +✅ **Completed:** +- LineChart component with predefined margins +- Line component with custom color and stroke style support +- ReferenceLine component +- Styled XAxis, YAxis, CartesianGrid, Tooltip, Legend +- ResponsiveContainer re-export +- ChartTooltip component +- TypeScript interfaces +- Storybook examples + +⚠️ **Known Issues:** +- Some TypeScript type conflicts between React and Recharts types +- Need to refine color token resolution for all theme modes + +## Next Steps + +1. Fix TypeScript type issues with dot and margin props +2. Add more chart types (Bar, Area, Pie, etc.) +3. Add more comprehensive color token support +4. Add accessibility features +5. Add animation presets \ No newline at end of file diff --git a/packages/blade/src/components/Charts/index.ts b/packages/blade/src/components/Charts/index.ts new file mode 100644 index 00000000000..38556818bf2 --- /dev/null +++ b/packages/blade/src/components/Charts/index.ts @@ -0,0 +1 @@ +export * from './LineCharts'; \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 7016d380376..792c2bb2578 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5644,6 +5644,18 @@ invariant "^2.2.4" nullthrows "^1.1.1" +"@reduxjs/toolkit@1.x.x || 2.x.x": + version "2.8.2" + resolved "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.8.2.tgz#f4e9f973c6fc930c1e0f3bf462cc95210c28f5f9" + integrity sha512-MYlOhQ0sLdw4ud48FoC5w0dH9VfWQjtCjreKwYTT3l+r427qYC5Y8PihNutepr8XrNaBUDQo9khWUwQxZaqt5A== + dependencies: + "@standard-schema/spec" "^1.0.0" + "@standard-schema/utils" "^0.3.0" + immer "^10.0.3" + redux "^5.0.1" + redux-thunk "^3.1.0" + reselect "^5.1.0" + "@rollup/plugin-alias@5.0.0": version "5.0.0" resolved "https://registry.yarnpkg.com/@rollup/plugin-alias/-/plugin-alias-5.0.0.tgz#70f3d504bd17d8922e35c6b61c08b40a6ec25af2" @@ -5933,6 +5945,16 @@ resolved "https://registry.yarnpkg.com/@stackblitz/sdk/-/sdk-1.11.0.tgz#ba30c837decca221ce8d605ff768a774c0f92f89" integrity sha512-DFQGANNkEZRzFk1/rDP6TcFdM82ycHE+zfl9C/M/jXlH68jiqHWHFMQURLELoD8koxvu/eW5uhg94NSAZlYrUQ== +"@standard-schema/spec@^1.0.0": + version "1.0.0" + resolved "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz#f193b73dc316c4170f2e82a881da0f550d551b9c" + integrity sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA== + +"@standard-schema/utils@^0.3.0": + version "0.3.0" + resolved "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz#3d5e608f16c2390c10528e98e59aef6bf73cae7b" + integrity sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g== + "@stitches/core@^1.2.6": version "1.2.8" resolved "https://registry.yarnpkg.com/@stitches/core/-/core-1.2.8.tgz#dce3b8fdc764fbc6dbea30c83b73bfb52cf96173" @@ -8788,6 +8810,57 @@ dependencies: "@types/node" "*" +"@types/d3-array@^3.0.3": + version "3.2.1" + resolved "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz#1f6658e3d2006c4fceac53fde464166859f8b8c5" + integrity sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg== + +"@types/d3-color@*": + version "3.1.3" + resolved "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz#368c961a18de721da8200e80bf3943fb53136af2" + integrity sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A== + +"@types/d3-ease@^3.0.0": + version "3.0.2" + resolved "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz#e28db1bfbfa617076f7770dd1d9a48eaa3b6c51b" + integrity sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA== + +"@types/d3-interpolate@^3.0.1": + version "3.0.4" + resolved "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz#412b90e84870285f2ff8a846c6eb60344f12a41c" + integrity sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA== + dependencies: + "@types/d3-color" "*" + +"@types/d3-path@*": + version "3.1.1" + resolved "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz#f632b380c3aca1dba8e34aa049bcd6a4af23df8a" + integrity sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg== + +"@types/d3-scale@^4.0.2": + version "4.0.9" + resolved "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz#57a2f707242e6fe1de81ad7bfcccaaf606179afb" + integrity sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw== + dependencies: + "@types/d3-time" "*" + +"@types/d3-shape@^3.1.0": + version "3.1.7" + resolved "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.7.tgz#2b7b423dc2dfe69c8c93596e673e37443348c555" + integrity sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg== + dependencies: + "@types/d3-path" "*" + +"@types/d3-time@*", "@types/d3-time@^3.0.0": + version "3.0.4" + resolved "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz#8472feecd639691450dd8000eb33edd444e1323f" + integrity sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g== + +"@types/d3-timer@^3.0.0": + version "3.0.2" + resolved "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz#70bbda77dc23aa727413e22e214afa3f0e852f70" + integrity sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw== + "@types/debug@^4.0.0": version "4.1.12" resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.12.tgz#a155f21690871953410df4b6b6f53187f0500917" @@ -9544,6 +9617,11 @@ resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.10.tgz#04ffa7f406ab628f7f7e97ca23e290cd8ab15efc" integrity sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA== +"@types/use-sync-external-store@^0.0.6": + version "0.0.6" + resolved "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz#60be8d21baab8c305132eb9cb912ed497852aadc" + integrity sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg== + "@types/uuid@^8.3.1": version "8.3.4" resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.4.tgz#bd86a43617df0594787d38b735f55c805becf1bc" @@ -12631,7 +12709,7 @@ clsx@1.1.1: resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.1.1.tgz#98b3134f9abbdf23b2663491ace13c5c03a73188" integrity sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA== -clsx@^2.0.0: +clsx@^2.0.0, clsx@^2.1.1: version "2.1.1" resolved "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz#eed397c9fd8bd882bfb18deab7102049a2f32999" integrity sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA== @@ -13661,6 +13739,77 @@ cyclist@^1.0.1: resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.2.tgz#673b5f233bf34d8e602b949429f8171d9121bea3" integrity sha512-0sVXIohTfLqVIW3kb/0n6IiWF3Ifj5nm2XaSrLq2DI6fKIGa2fYAZdk917rUneaeLVpYfFcyXE2ft0fe3remsA== +"d3-array@2 - 3", "d3-array@2.10.0 - 3", d3-array@^3.1.6: + version "3.2.4" + resolved "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz#15fec33b237f97ac5d7c986dc77da273a8ed0bb5" + integrity sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg== + dependencies: + internmap "1 - 2" + +"d3-color@1 - 3": + version "3.1.0" + resolved "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz#395b2833dfac71507f12ac2f7af23bf819de24e2" + integrity sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA== + +d3-ease@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz#9658ac38a2140d59d346160f1f6c30fda0bd12f4" + integrity sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w== + +"d3-format@1 - 3": + version "3.1.0" + resolved "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz#9260e23a28ea5cb109e93b21a06e24e2ebd55641" + integrity sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA== + +"d3-interpolate@1.2.0 - 3", d3-interpolate@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz#3c47aa5b32c5b3dfb56ef3fd4342078a632b400d" + integrity sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g== + dependencies: + d3-color "1 - 3" + +d3-path@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz#22df939032fb5a71ae8b1800d61ddb7851c42526" + integrity sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ== + +d3-scale@^4.0.2: + version "4.0.2" + resolved "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz#82b38e8e8ff7080764f8dcec77bd4be393689396" + integrity sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ== + dependencies: + d3-array "2.10.0 - 3" + d3-format "1 - 3" + d3-interpolate "1.2.0 - 3" + d3-time "2.1.1 - 3" + d3-time-format "2 - 4" + +d3-shape@^3.1.0: + version "3.2.0" + resolved "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz#a1a839cbd9ba45f28674c69d7f855bcf91dfc6a5" + integrity sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA== + dependencies: + d3-path "^3.1.0" + +"d3-time-format@2 - 4": + version "4.1.0" + resolved "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz#7ab5257a5041d11ecb4fe70a5c7d16a195bb408a" + integrity sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg== + dependencies: + d3-time "1 - 3" + +"d3-time@1 - 3", "d3-time@2.1.1 - 3", d3-time@^3.0.0: + version "3.1.0" + resolved "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz#9310db56e992e3c0175e1ef385e545e48a9bb5c7" + integrity sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q== + dependencies: + d3-array "2 - 3" + +d3-timer@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz#6284d2a2708285b1abb7e201eda4380af35e63b0" + integrity sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA== + d@1, d@^1.0.1, d@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/d/-/d-1.0.2.tgz#2aefd554b81981e7dccf72d6842ae725cb17e5de" @@ -13831,6 +13980,11 @@ decamelize@^1.1.0, decamelize@^1.1.2, decamelize@^1.2.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== +decimal.js-light@^2.5.1: + version "2.5.1" + resolved "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz#134fd32508f19e208f4fb2f8dac0d2626a867934" + integrity sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg== + decimal.js@^10.2.1, decimal.js@^10.4.1, decimal.js@^10.4.2: version "10.4.3" resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23" @@ -14850,6 +15004,11 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" +es-toolkit@^1.39.3: + version "1.39.8" + resolved "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.39.8.tgz#314aca3988fc1f2c3faa95b49c3029f44c690f50" + integrity sha512-A8QO9TfF+rltS8BXpdu8OS+rpGgEdnRhqIVxO/ZmNvnXBYgOdSsxukT55ELyP94gZIntWJ+Li9QRrT2u1Kitpg== + es5-ext@^0.10.35, es5-ext@^0.10.62, es5-ext@^0.10.64, es5-ext@~0.10.14: version "0.10.64" resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.64.tgz#12e4ffb48f1ba2ea777f1fcdd1918ef73ea21714" @@ -15681,6 +15840,11 @@ eventemitter3@^4.0.0, eventemitter3@^4.0.4: resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== +eventemitter3@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4" + integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== + events@^3.0.0, events@^3.2.0, events@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" @@ -18169,6 +18333,11 @@ image-size@^1.0.2: dependencies: queue "6.0.2" +immer@^10.0.3, immer@^10.1.1: + version "10.1.1" + resolved "https://registry.npmjs.org/immer/-/immer-10.1.1.tgz#206f344ea372d8ea176891545ee53ccc062db7bc" + integrity sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw== + immer@^9.0.7: version "9.0.21" resolved "https://registry.yarnpkg.com/immer/-/immer-9.0.21.tgz#1e025ea31a40f24fb064f1fef23e931496330176" @@ -18361,6 +18530,11 @@ internal-slot@^1.0.4, internal-slot@^1.0.7: hasown "^2.0.0" side-channel "^1.0.4" +"internmap@1 - 2": + version "2.0.3" + resolved "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz#6685f23755e43c524e251d29cbc97248e3061009" + integrity sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg== + interpret@^1.0.0, interpret@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" @@ -26529,6 +26703,14 @@ react-popper@^1.3.7: typed-styles "^0.0.7" warning "^4.0.2" +"react-redux@8.x.x || 9.x.x": + version "9.2.0" + resolved "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz#96c3ab23fb9a3af2cb4654be4b51c989e32366f5" + integrity sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g== + dependencies: + "@types/use-sync-external-store" "^0.0.6" + use-sync-external-store "^1.4.0" + react-refresh@0.11.0, react-refresh@^0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.11.0.tgz#77198b944733f0f1f1a90e791de4541f9f074046" @@ -27008,6 +27190,23 @@ recast@^0.23.1: tiny-invariant "^1.3.3" tslib "^2.0.1" +recharts@3.1.2: + version "3.1.2" + resolved "https://registry.npmjs.org/recharts/-/recharts-3.1.2.tgz#44626509c6567ed8208d3347e915397d480bb3d2" + integrity sha512-vhNbYwaxNbk/IATK0Ki29k3qvTkGqwvCgyQAQ9MavvvBwjvKnMTswdbklJpcOAoMPN/qxF3Lyqob0zO+ZXkZ4g== + dependencies: + "@reduxjs/toolkit" "1.x.x || 2.x.x" + clsx "^2.1.1" + decimal.js-light "^2.5.1" + es-toolkit "^1.39.3" + eventemitter3 "^5.0.1" + immer "^10.1.1" + react-redux "8.x.x || 9.x.x" + reselect "5.1.1" + tiny-invariant "^1.3.3" + use-sync-external-store "^1.2.2" + victory-vendor "^37.0.2" + rechoir@^0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" @@ -27045,6 +27244,16 @@ redent@^3.0.0: indent-string "^4.0.0" strip-indent "^3.0.0" +redux-thunk@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz#94aa6e04977c30e14e892eae84978c1af6058ff3" + integrity sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw== + +redux@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz#97fa26881ce5746500125585d5642c77b6e9447b" + integrity sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w== + reflect.getprototypeof@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz#3ab04c32a8390b770712b7a8633972702d278859" @@ -27392,6 +27601,11 @@ requires-port@^1.0.0: resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== +reselect@5.1.1, reselect@^5.1.0: + version "5.1.1" + resolved "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz#c766b1eb5d558291e5e550298adb0becc24bb72e" + integrity sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w== + reselect@^4.0.0: version "4.1.8" resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.1.8.tgz#3f5dc671ea168dccdeb3e141236f69f02eaec524" @@ -30800,6 +31014,11 @@ use-sync-external-store@^1.0.0: resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a" integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA== +use-sync-external-store@^1.2.2, use-sync-external-store@^1.4.0: + version "1.5.0" + resolved "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz#55122e2a3edd2a6c106174c27485e0fd59bcfca0" + integrity sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A== + use@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" @@ -31028,6 +31247,26 @@ vfile@^5.0.0, vfile@^5.1.0, vfile@^5.3.7: unist-util-stringify-position "^3.0.0" vfile-message "^3.0.0" +victory-vendor@^37.0.2: + version "37.3.6" + resolved "https://registry.npmjs.org/victory-vendor/-/victory-vendor-37.3.6.tgz#401ac4b029a0b3d33e0cba8e8a1d765c487254da" + integrity sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ== + dependencies: + "@types/d3-array" "^3.0.3" + "@types/d3-ease" "^3.0.0" + "@types/d3-interpolate" "^3.0.1" + "@types/d3-scale" "^4.0.2" + "@types/d3-shape" "^3.1.0" + "@types/d3-time" "^3.0.0" + "@types/d3-timer" "^3.0.0" + d3-array "^3.1.6" + d3-ease "^3.0.1" + d3-interpolate "^3.0.1" + d3-scale "^4.0.2" + d3-shape "^3.1.0" + d3-time "^3.0.0" + d3-timer "^3.0.1" + vlq@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/vlq/-/vlq-1.0.1.tgz#c003f6e7c0b4c1edd623fd6ee50bbc0d6a1de468" From 4513e87619264f89ffc916dc161c6bf5fc57b4ab Mon Sep 17 00:00:00 2001 From: tewarig Date: Thu, 7 Aug 2025 16:32:47 +0530 Subject: [PATCH 002/105] fix: lint --- .../Charts/LineCharts/lineCharts.tsx | 61 +++++++++++-------- 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx b/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx index aefed12ba38..49e6f3b820e 100644 --- a/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx +++ b/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx @@ -21,7 +21,7 @@ import BaseBox from '~components/Box/BaseBox'; import { castWebType } from '~utils'; // BladeColorToken type for charts -export type BladeColorToken = +export type BladeColorToken = | 'surface.text.gray.normal' | 'surface.text.gray.muted' | 'interactive.background.primary.default' @@ -36,7 +36,18 @@ export interface LineProps extends Omit, 'ty type?: 'step' | 'stepAfter' | 'stepBefore' | 'linear' | 'monotone'; dot?: React.ReactNode; connectNulls?: boolean; - legendType?: 'none' | 'line' | 'square' | 'diamond' | 'circle' | 'cross' | 'triangle' | 'triangleDown' | 'triangleUp' | 'star' | 'wye'; + legendType?: + | 'none' + | 'line' + | 'square' + | 'diamond' + | 'circle' + | 'cross' + | 'triangle' + | 'triangleDown' + | 'triangleUp' + | 'star' + | 'wye'; dataKey: string; name?: string; color?: BladeColorToken; @@ -66,8 +77,12 @@ const getChartColors = (theme: Theme): Record => ({ // Helper function to resolve color tokens const resolveColorToken = (color: BladeColorToken | undefined, theme: Theme): string => { if (!color) return getChartColors(theme).primary; - - if (color.startsWith('surface.') || color.startsWith('feedback.') || color.startsWith('interactive.')) { + + if ( + color.startsWith('surface.') || + color.startsWith('feedback.') || + color.startsWith('interactive.') + ) { const parts = color.split('.'); let value: any = theme.colors; for (const part of parts) { @@ -75,14 +90,15 @@ const resolveColorToken = (color: BladeColorToken | undefined, theme: Theme): st } return value || getChartColors(theme).primary; } - + return color; }; // TypeScript prop types -export type LineChartProps = Omit, 'margin'> & StyledPropsBlade & { - children?: React.ReactNode; -}; +export type LineChartProps = Omit, 'margin'> & + StyledPropsBlade & { + children?: React.ReactNode; + }; export type XAxisProps = ComponentProps; export type YAxisProps = ComponentProps; @@ -118,16 +134,17 @@ export const LineChart: React.FC = ({ children, ...props }) => { ); }; -export const Line: React.FC = ({ +export const Line: React.FC = ({ color, strokeStyle = 'solid', type = 'monotone', - ...props + ...props }) => { const { theme } = useTheme(); const resolvedColor = resolveColorToken(color, theme); - - const strokeDasharray = strokeStyle === 'dashed' ? '5 5' : strokeStyle === 'dotted' ? '2 2' : undefined; + + const strokeDasharray = + strokeStyle === 'dashed' ? '5 5' : strokeStyle === 'dotted' ? '2 2' : undefined; return ( = ({ ); }; -export const ReferenceLine: React.FC = ({ - color, - label, - ...props -}) => { +export const ReferenceLine: React.FC = ({ color, label, ...props }) => { const { theme } = useTheme(); const resolvedColor = resolveColorToken(color, theme); @@ -163,12 +176,12 @@ export const ReferenceLine: React.FC = ({ export const XAxis: React.FC = (props) => { const { theme } = useTheme(); - + return ( = (props) => { export const YAxis: React.FC = (props) => { const { theme } = useTheme(); - + return ( = (props) => { export const CartesianGrid: React.FC = (props) => { const { theme } = useTheme(); - + return ( = (props) => { export const Tooltip: React.FC = (props) => { const { theme } = useTheme(); - + return ( = (props) => { export const Legend: React.FC = (props) => { const { theme } = useTheme(); - + return ( Date: Thu, 7 Aug 2025 17:11:11 +0530 Subject: [PATCH 003/105] chore: pie charts --- .../Charts/LineCharts/LineCharts.stories.tsx | 80 ++++- .../Charts/LineCharts/lineCharts.tsx | 41 +-- .../Charts/PieCharts/PieCharts.stories.tsx | 296 ++++++++++++++++++ .../components/Charts/PieCharts/PieCharts.tsx | 256 +++++++++++++++ .../src/components/Charts/PieCharts/index.ts | 28 ++ .../blade/src/components/Charts/README.md | 171 +++++++++- packages/blade/src/components/Charts/index.ts | 3 +- 7 files changed, 831 insertions(+), 44 deletions(-) create mode 100644 packages/blade/src/components/Charts/PieCharts/PieCharts.stories.tsx create mode 100644 packages/blade/src/components/Charts/PieCharts/PieCharts.tsx create mode 100644 packages/blade/src/components/Charts/PieCharts/index.ts diff --git a/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx b/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx index 614c7f3e207..2c687398fda 100644 --- a/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx +++ b/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx @@ -117,12 +117,6 @@ export const SimpleLineChart: StoryFn = () => { strokeStyle="solid" color="interactive.background.primary.default" /> - @@ -130,7 +124,7 @@ export const SimpleLineChart: StoryFn = () => { ); }; -// Tiny Line Chart Example +// Tiny Line Chart Example (no dots for cleaner look) export const TinyLineChart: StoryFn = () => { return (
@@ -140,6 +134,74 @@ export const TinyLineChart: StoryFn = () => { dataKey="teamA" strokeStyle="solid" color="surface.text.gray.normal" + dot={false} + activeDot={false} + /> + + +
+ ); +}; + +// Line Chart with Custom Dots +export const LineChartWithCustomDots: StoryFn = () => { + return ( +
+ + + + + + + + + + + +
+ ); +}; + +// Showcase Different Dot Configurations +export const DotConfigurationShowcase: StoryFn = () => { + return ( +
+ + + + + + + + {/* No dots at all */} + + {/* Default dots */} + @@ -170,7 +232,7 @@ export const ForecastLineChart: StoryFn = () => { strokeStyle="dashed" connectNulls={true} legendType="none" - color="feedback.text.notice.subtle" + color="surface.text.gray.muted" /> @@ -180,4 +242,6 @@ export const ForecastLineChart: StoryFn = () => { SimpleLineChart.storyName = 'Simple Line Chart'; TinyLineChart.storyName = 'Tiny Line Chart'; +LineChartWithCustomDots.storyName = 'Line Chart with Custom Dots'; +DotConfigurationShowcase.storyName = 'Dot Configuration Showcase'; ForecastLineChart.storyName = 'Forecast Line Chart'; \ No newline at end of file diff --git a/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx b/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx index 49e6f3b820e..99feeef2375 100644 --- a/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx +++ b/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx @@ -32,22 +32,12 @@ export type BladeColorToken = | string; // Chart-specific interfaces based on user specifications -export interface LineProps extends Omit, 'type'> { +export interface LineProps { type?: 'step' | 'stepAfter' | 'stepBefore' | 'linear' | 'monotone'; - dot?: React.ReactNode; + dot?: boolean | { r?: number; fill?: string; stroke?: string; strokeWidth?: number; [key: string]: any }; + activeDot?: boolean | { r?: number; fill?: string; stroke?: string; strokeWidth?: number; [key: string]: any }; connectNulls?: boolean; - legendType?: - | 'none' - | 'line' - | 'square' - | 'diamond' - | 'circle' - | 'cross' - | 'triangle' - | 'triangleDown' - | 'triangleUp' - | 'star' - | 'wye'; + legendType?: 'none' | 'line' | 'square' | 'diamond' | 'circle' | 'cross' | 'triangle' | 'wye'; dataKey: string; name?: string; color?: BladeColorToken; @@ -77,20 +67,20 @@ const getChartColors = (theme: Theme): Record => ({ // Helper function to resolve color tokens const resolveColorToken = (color: BladeColorToken | undefined, theme: Theme): string => { if (!color) return getChartColors(theme).primary; - + if ( color.startsWith('surface.') || color.startsWith('feedback.') || color.startsWith('interactive.') ) { const parts = color.split('.'); - let value: any = theme.colors; + let value: Record = theme.colors; for (const part of parts) { value = value[part]; } return value || getChartColors(theme).primary; } - + return color; }; @@ -138,6 +128,8 @@ export const Line: React.FC = ({ color, strokeStyle = 'solid', type = 'monotone', + dot = true, + activeDot = true, ...props }) => { const { theme } = useTheme(); @@ -146,14 +138,25 @@ export const Line: React.FC = ({ const strokeDasharray = strokeStyle === 'dashed' ? '5 5' : strokeStyle === 'dotted' ? '2 2' : undefined; + // Configure dot and activeDot based on props + const dotConfig = + dot === false ? false : dot === true ? { fill: resolvedColor, strokeWidth: 0, r: 4 } : dot; + + const activeDotConfig = + activeDot === false + ? false + : activeDot === true + ? { r: 6, strokeWidth: 0, fill: resolvedColor } + : activeDot; + return ( ); diff --git a/packages/blade/src/components/Charts/PieCharts/PieCharts.stories.tsx b/packages/blade/src/components/Charts/PieCharts/PieCharts.stories.tsx new file mode 100644 index 00000000000..727fc945791 --- /dev/null +++ b/packages/blade/src/components/Charts/PieCharts/PieCharts.stories.tsx @@ -0,0 +1,296 @@ +import type { StoryFn, Meta } from '@storybook/react'; +import React from 'react'; +import { + PieChart, + Pie, + Cell, + Tooltip, + Legend, + ResponsiveContainer, + Label, + ChartTooltip, +} from './PieCharts'; +import { Heading } from '~components/Typography/Heading'; +import { Text } from '~components/Typography/Text'; +import { Sandbox } from '~utils/storybook/Sandbox'; +import StoryPageWrapper from '~utils/storybook/StoryPageWrapper'; +import { getStyledPropsArgTypes } from '~components/Box/BaseBox/storybookArgTypes'; + +const Page = (): React.ReactElement => { + return ( + + Usage + + {` + import { + PieChart, + Pie, + Cell, + Tooltip, + Legend, + ResponsiveContainer + } from '@razorpay/blade/components'; + + function App() { + const data = [ + { name: 'Group A', value: 400 }, + { name: 'Group B', value: 300 }, + { name: 'Group C', value: 300 }, + { name: 'Group D', value: 200 }, + ]; + + const COLORS = ['interactive.background.primary.default', 'feedback.text.positive.subtle', 'feedback.text.notice.subtle', 'surface.text.gray.muted']; + + return ( + + + + {data.map((entry, index) => ( + + ))} + + + + + + ) + } + + export default App; + `} + + + ); +}; + +export default { + title: 'Components/Charts/PieChart', + component: PieChart, + tags: ['autodocs'], + argTypes: { + ...getStyledPropsArgTypes(), + }, + parameters: { + docs: { + page: Page, + }, + }, +} as Meta; + +// Sample data for charts +const pieData = [ + { name: 'Group A', value: 400 }, + { name: 'Group B', value: 300 }, + { name: 'Group C', value: 300 }, + { name: 'Group D', value: 200 }, +]; + +const COLORS = [ + 'interactive.background.primary.default', + 'feedback.text.positive.subtle', + 'feedback.text.notice.subtle', + 'surface.text.gray.muted', +]; + +// 1. Simple Pie Chart +export const SimplePieChart: StoryFn = () => { + return ( +
+ + + }> + {pieData.map((entry, index) => ( + + ))} + + + + + +
+ ); +}; + +// 2. Half Pie Chart (StraightAngleChart) +export const HalfPieChart: StoryFn = () => { + return ( +
+ + + + {pieData.map((entry, index) => ( + + ))} + + + + + +
+ ); +}; + +// 3. Simple Donut Chart +export const DonutChart: StoryFn = () => { + return ( +
+ + + + {pieData.map((entry, index) => ( + + ))} + + + + + +
+ ); +}; + +// 4. Donut with Text in Center +export const DonutWithCenterText: StoryFn = () => { + const totalValue = pieData.reduce((sum, entry) => sum + entry.value, 0); + + return ( +
+ + + + {pieData.map((entry, index) => ( + + ))} + + + + + +
+ ); +}; + +// 5. Donut Size Variations +export const DonutSizeVariations: StoryFn = () => { + return ( +
+ {(['small', 'medium', 'large', 'extraLarge'] as const).map((size) => ( +
+ + {size.charAt(0).toUpperCase() + size.slice(1)} Donut + + + + + {pieData.slice(0, 3).map((entry, index) => ( + + ))} + + + + +
+ ))} +
+ ); +}; + +// 6. Circle Type Variations +export const CircleTypeVariations: StoryFn = () => { + return ( +
+ {(['full', 'half', 'quarter'] as const).map((type) => ( +
+ + {type.charAt(0).toUpperCase() + type.slice(1)} Circle + + + + + {pieData.map((entry, index) => ( + + ))} + + + + +
+ ))} +
+ ); +}; + +SimplePieChart.storyName = 'Simple Pie Chart'; +HalfPieChart.storyName = 'Half Pie Chart'; +DonutChart.storyName = 'Donut Chart'; +DonutWithCenterText.storyName = 'Donut with Center Text'; +DonutSizeVariations.storyName = 'Donut Size Variations'; +CircleTypeVariations.storyName = 'Circle Type Variations'; diff --git a/packages/blade/src/components/Charts/PieCharts/PieCharts.tsx b/packages/blade/src/components/Charts/PieCharts/PieCharts.tsx new file mode 100644 index 00000000000..339fad6a034 --- /dev/null +++ b/packages/blade/src/components/Charts/PieCharts/PieCharts.tsx @@ -0,0 +1,256 @@ +import React from 'react'; +import type { ComponentProps } from 'react'; +import styled from 'styled-components'; +import { + PieChart as RechartsPieChart, + Pie as RechartsPie, + Cell as RechartsCell, + Tooltip as RechartsTooltip, + Legend as RechartsLegend, + ResponsiveContainer as RechartsResponsiveContainer, + Label as RechartsLabel, +} from 'recharts'; +import { useTheme } from '~components/BladeProvider'; +import type { Theme } from '~components/BladeProvider'; +import type { StyledPropsBlade } from '~components/Box/styledProps'; +import { getStyledProps } from '~components/Box/styledProps'; +import { metaAttribute } from '~utils/metaAttribute'; +import BaseBox from '~components/Box/BaseBox'; +import { castWebType } from '~utils'; + +// BladeColorToken type for charts +export type BladeColorToken = + | 'surface.text.gray.normal' + | 'surface.text.gray.muted' + | 'interactive.background.primary.default' + | 'feedback.text.positive.subtle' + | 'feedback.text.negative.subtle' + | 'feedback.text.notice.subtle' + | 'feedback.text.information.subtle' + | string; + +// Donut radius configurations +type DonutRadius = 'small' | 'medium' | 'large' | 'extraLarge' | 'none'; +type CircleType = 'full' | 'half' | 'quarter'; +type PaddingAngle = 'none' | 'small' | 'medium' | 'large' | 'extraLarge'; + +// Pie Chart specific interfaces +export interface PieProps extends Omit, 'innerRadius' | 'outerRadius' | 'startAngle' | 'endAngle' | 'paddingAngle'> { + dataKey: string; + nameKey?: string; + data: { [key: string]: string | number }[]; + cx?: string | number; + cy?: string | number; + donutRadius?: DonutRadius; + circleType?: CircleType; + paddingAngle?: PaddingAngle; + activeShape?: React.ReactElement | ((props: any) => React.ReactNode); +} + +export interface CellProps extends ComponentProps { + fill?: BladeColorToken; +} + +// Chart color tokens +const getChartColors = (theme: Theme): Record => ({ + primary: theme.colors.interactive.background.primary.default, + secondary: theme.colors.surface.text.gray.normal, + tertiary: theme.colors.feedback.text.positive.subtle, + quaternary: theme.colors.feedback.text.notice.subtle, + success: theme.colors.feedback.text.positive.subtle, + warning: theme.colors.feedback.text.notice.subtle, + error: theme.colors.feedback.text.negative.subtle, + info: theme.colors.feedback.text.information.subtle, + neutral: theme.colors.surface.text.gray.muted, +}); + +// Helper function to resolve color tokens +const resolveColorToken = (color: BladeColorToken | undefined, theme: Theme): string => { + if (!color) return getChartColors(theme).primary; + + if ( + color.startsWith('surface.') || + color.startsWith('feedback.') || + color.startsWith('interactive.') + ) { + const parts = color.split('.'); + let value: Record = theme.colors; + for (const part of parts) { + value = value[part]; + } + return (value as string) || getChartColors(theme).primary; + } + + return color; +}; + +// Helper functions for radius and angle calculations +const getDonutRadiusValues = (donutRadius: DonutRadius): { innerRadius: number; outerRadius: number } => { + switch (donutRadius) { + case 'small': + return { innerRadius: 40, outerRadius: 80 }; + case 'medium': + return { innerRadius: 50, outerRadius: 90 }; + case 'large': + return { innerRadius: 60, outerRadius: 100 }; + case 'extraLarge': + return { innerRadius: 70, outerRadius: 110 }; + case 'none': + default: + return { innerRadius: 0, outerRadius: 80 }; + } +}; + +const getCircleTypeAngles = (circleType: CircleType): { startAngle: number; endAngle: number } => { + switch (circleType) { + case 'half': + return { startAngle: 180, endAngle: 0 }; + case 'quarter': + return { startAngle: 270, endAngle: 0 }; + case 'full': + default: + return { startAngle: 0, endAngle: 360 }; + } +}; + +const getPaddingAngleValue = (paddingAngle: PaddingAngle): number => { + switch (paddingAngle) { + case 'small': + return 1; + case 'medium': + return 2; + case 'large': + return 4; + case 'extraLarge': + return 6; + case 'none': + default: + return 0; + } +}; + +// TypeScript prop types +export type PieChartProps = ComponentProps & StyledPropsBlade & { + children?: React.ReactNode; +}; + +export type TooltipProps = ComponentProps; +export type LegendProps = ComponentProps; +export type ResponsiveContainerProps = ComponentProps; +export type LabelProps = ComponentProps; + +// Styled wrapper for PieChart +const StyledPieChart = styled(RechartsPieChart)<{ theme: Theme }>` + font-family: ${(props) => props.theme.typography.fonts.family.text}; +`; + +// Main components +export const PieChart: React.FC = ({ children, ...props }) => { + const { theme } = useTheme(); + const styledProps = getStyledProps(props); + + return ( + + + {children} + + + ); +}; + +export const Pie: React.FC = ({ + donutRadius = 'none', + circleType = 'full', + paddingAngle = 'none', + cx = '50%', + cy = '50%', + ...props +}) => { + const radiusValues = getDonutRadiusValues(donutRadius); + const angleValues = getCircleTypeAngles(circleType); + const paddingValue = getPaddingAngleValue(paddingAngle); + + return ( + + ); +}; + +export const Cell: React.FC = ({ fill, ...props }) => { + const { theme } = useTheme(); + const resolvedColor = fill ? resolveColorToken(fill, theme) : undefined; + + return ( + + ); +}; + +export const Tooltip: React.FC = (props) => { + const { theme } = useTheme(); + + return ( + + ); +}; + +export const Legend: React.FC = (props) => { + const { theme } = useTheme(); + + return ( + + ); +}; + +export const ResponsiveContainer: React.FC = (props) => { + return ; +}; + +export const Label: React.FC = (props) => { + const { theme } = useTheme(); + + return ( + + ); +}; + +// Custom ChartTooltip component +export const ChartTooltip: React.FC = (props) => { + return ; +}; diff --git a/packages/blade/src/components/Charts/PieCharts/index.ts b/packages/blade/src/components/Charts/PieCharts/index.ts new file mode 100644 index 00000000000..41b3cf11a4a --- /dev/null +++ b/packages/blade/src/components/Charts/PieCharts/index.ts @@ -0,0 +1,28 @@ +export { + PieChart, + Pie, + Cell, + Label, +} from './PieCharts'; + +export type { + PieChartProps, + PieProps, + CellProps, + LabelProps, +} from './PieCharts'; + +// Re-export common components from LineCharts to avoid duplication +export { + Tooltip, + Legend, + ResponsiveContainer, + ChartTooltip, +} from '../LineCharts'; + +export type { + TooltipProps, + LegendProps, + ResponsiveContainerProps, + BladeColorToken, +} from '../LineCharts'; diff --git a/packages/blade/src/components/Charts/README.md b/packages/blade/src/components/Charts/README.md index 4b839112e6e..f3e85006cdf 100644 --- a/packages/blade/src/components/Charts/README.md +++ b/packages/blade/src/components/Charts/README.md @@ -16,6 +16,14 @@ This directory contains Blade's chart components built on top of Recharts with d - **ResponsiveContainer**: Responsive wrapper - **ChartTooltip**: Custom tooltip for forecast charts +### PieChart Components + +- **PieChart**: Main pie chart container component +- **Pie**: Pie/donut chart with configurable radius and angles +- **Cell**: Individual pie slice with color tokens +- **Label**: Styled labels for center text in donuts +- **Tooltip/Legend/ResponsiveContainer**: Shared styled components + ## Usage Examples ### Simple Line Chart @@ -34,30 +42,156 @@ This directory contains Blade's chart components built on top of Recharts with d ``` -### Tiny Line Chart +### Simple Pie Chart ```tsx +const COLORS = ['interactive.background.primary.default', 'feedback.text.positive.subtle', 'feedback.text.notice.subtle', 'surface.text.gray.muted']; + - - - + + + {data.map((entry, index) => ( + + ))} + + + + ``` -### Forecast Line Chart +### Donut Chart ```tsx - - - - - + + + {data.map((entry, index) => ( + + ))} + + - - - + + +``` + +### Donut with Center Text +```tsx + + + + {data.map((entry, index) => ( + + ))} + + + + + +``` + +### Half Pie Chart +```tsx + + + + {data.map((entry, index) => ( + + ))} + + + + ``` +## Line Component Props + +### Dot Configuration Options +The `Line` component supports flexible dot configuration: + +```tsx +// No dots for clean minimal charts + + +// Default themed dots + + +// Custom dot styling with size and colors + + +// Only active dots on hover (cleaner look) + + +// Large custom dots + +``` + +### Dot Configuration Properties +When using custom dot objects, you can configure: +- `r`: Radius of the dot +- `fill`: Fill color of the dot +- `stroke`: Stroke color of the dot +- `strokeWidth`: Width of the stroke +- Any other SVG circle properties + +### Use Cases +- **Tiny Charts**: `dot={false}` for minimal clutter +- **Dense Data**: Remove dots to prevent overlap +- **Brand Colors**: Custom colors that match your design +- **Interactive Focus**: Show only `activeDot` for hover states +- **Data Emphasis**: Larger dots for important data points + +## Pie Component Props + +### Donut Radius Options +- `none`: Regular pie chart (innerRadius: 0) +- `small`: Small donut hole (innerRadius: 40, outerRadius: 80) +- `medium`: Medium donut hole (innerRadius: 50, outerRadius: 90) +- `large`: Large donut hole (innerRadius: 60, outerRadius: 100) +- `extraLarge`: Extra large donut hole (innerRadius: 70, outerRadius: 110) + +### Circle Type Options +- `full`: Complete 360° circle (default) +- `half`: Semi-circle (180°) +- `quarter`: Quarter circle (90°) + +### Padding Angle Options +- `none`: No spacing between slices (default) +- `small`: 1px spacing +- `medium`: 2px spacing +- `large`: 4px spacing +- `extraLarge`: 6px spacing + ## Color Tokens The components use Blade color tokens: @@ -74,12 +208,17 @@ The components use Blade color tokens: ✅ **Completed:** - LineChart component with predefined margins - Line component with custom color and stroke style support +- **Optional dots and activeDots for Line component** - ReferenceLine component +- **PieChart component with Blade styling** +- **Pie component with donutRadius, circleType, and paddingAngle** +- **Cell component with color token support** +- **Label component for center text in donuts** - Styled XAxis, YAxis, CartesianGrid, Tooltip, Legend - ResponsiveContainer re-export - ChartTooltip component - TypeScript interfaces -- Storybook examples +- Storybook examples with dot variations and pie chart examples ⚠️ **Known Issues:** - Some TypeScript type conflicts between React and Recharts types @@ -87,8 +226,8 @@ The components use Blade color tokens: ## Next Steps -1. Fix TypeScript type issues with dot and margin props -2. Add more chart types (Bar, Area, Pie, etc.) +1. Fix TypeScript type issues with activeShape and margin props +2. Add more chart types (Bar, Area, etc.) 3. Add more comprehensive color token support 4. Add accessibility features 5. Add animation presets \ No newline at end of file diff --git a/packages/blade/src/components/Charts/index.ts b/packages/blade/src/components/Charts/index.ts index 38556818bf2..96fa4dbfbb4 100644 --- a/packages/blade/src/components/Charts/index.ts +++ b/packages/blade/src/components/Charts/index.ts @@ -1 +1,2 @@ -export * from './LineCharts'; \ No newline at end of file +export * from './LineCharts'; +export * from './PieCharts'; \ No newline at end of file From 76ed2c6587eb7183b44fa9707442ba36fcb42173 Mon Sep 17 00:00:00 2001 From: tewarig Date: Mon, 11 Aug 2025 14:49:43 +0530 Subject: [PATCH 004/105] feat: add area charts --- .../components/Carousel/Carousel.stories.tsx | 7 +- .../Charts/AreaCharts/AreaCharts.stories.tsx | 302 ++++++++++++++++++ .../Charts/AreaCharts/AreaCharts.tsx | 280 ++++++++++++++++ .../src/components/Charts/AreaCharts/index.ts | 28 ++ .../blade/src/components/Charts/README.md | 99 +++++- packages/blade/src/components/Charts/index.ts | 10 +- 6 files changed, 720 insertions(+), 6 deletions(-) create mode 100644 packages/blade/src/components/Charts/AreaCharts/AreaCharts.stories.tsx create mode 100644 packages/blade/src/components/Charts/AreaCharts/AreaCharts.tsx create mode 100644 packages/blade/src/components/Charts/AreaCharts/index.ts diff --git a/packages/blade/src/components/Carousel/Carousel.stories.tsx b/packages/blade/src/components/Carousel/Carousel.stories.tsx index a834dcb59b0..d73f2904b38 100644 --- a/packages/blade/src/components/Carousel/Carousel.stories.tsx +++ b/packages/blade/src/components/Carousel/Carousel.stories.tsx @@ -580,7 +580,7 @@ export const WithInteractiveCards = InteractiveCarouselTestimonialTemplate.bind( export const WithPeek: StoryFn = (props) => { return ( - + Active card is centered with adjacent cards peeking from the sides. @@ -589,7 +589,10 @@ export const WithPeek: StoryFn = (props) => { snapAlign to "center", and adding gap for spacing between items. - + { + return ( + + Usage + + {` + import { + AreaChart, + Area, + XAxis, + YAxis, + CartesianGrid, + Tooltip, + Legend, + ResponsiveContainer + } from '@razorpay/blade/components'; + + function App() { + const data = [ + { month: 'Jan', teamA: 4000 }, + { month: 'Feb', teamA: 3000 }, + { month: 'Mar', teamA: 2000 }, + { month: 'Apr', teamA: 2780 }, + { month: 'May', teamA: 1890 }, + { month: 'Jun', teamA: 2390 }, + ]; + + return ( + + + + + + + + + + ) + } + + export default App; + `} + + + ); +}; + +export default { + title: 'Components/Charts/AreaChart', + component: AreaChart, + tags: ['autodocs'], + argTypes: { + ...getStyledPropsArgTypes(), + }, + parameters: { + docs: { + page: Page, + }, + }, +} as Meta; + +// Sample data for charts +const chartData = [ + { month: 'Jan', teamA: 4000, teamB: 2400, teamC: 1800 }, + { month: 'Feb', teamA: 3000, teamB: 1398, teamC: 2200 }, + { month: 'Mar', teamA: 2000, teamB: 9800, teamC: 1500 }, + { month: 'Apr', teamA: 2780, teamB: 3908, teamC: 2800 }, + { month: 'May', teamA: 1890, teamB: 4800, teamC: 2100 }, + { month: 'Jun', teamA: 2390, teamB: 3800, teamC: 2500 }, +]; + +// Data with null values for connectNulls example +const dataWithNulls = [ + { month: 'Jan', sales: 4000 }, + { month: 'Feb', sales: 3000 }, + { month: 'Mar', sales: null }, + { month: 'Apr', sales: null }, + { month: 'May', sales: 1890 }, + { month: 'Jun', sales: 2390 }, +]; + +// Simple Area Chart +export const SimpleAreaChart: StoryFn = () => { + return ( +
+ + + + + + + + + +
+ ); +}; + +// Stacked Area Chart +export const StackedAreaChart: StoryFn = () => { + return ( +
+ + + + + + + + + + + + +
+ ); +}; + +// Area Chart that Connects Nulls +export const AreaChartConnectNulls: StoryFn = () => { + return ( +
+ + + + + + + + + + +
+ ); +}; + +// Tiny Area Chart (Sparkline) +export const TinyAreaChart: StoryFn = () => { + return ( +
+ + + + + +
+ ); +}; + +// Area Chart with Reference Line +export const AreaChartWithReferenceLine: StoryFn = () => { + return ( +
+ + + + + + + + + + + +
+ ); +}; + +// Multiple Area Chart (Non-Stacked) +export const MultipleAreaChart: StoryFn = () => { + return ( +
+ + + + + + + + + + + +
+ ); +}; + +// Area Chart Type Variations +export const AreaTypeVariations: StoryFn = () => { + const types: Array<'monotone' | 'linear' | 'step' | 'stepAfter' | 'stepBefore'> = [ + 'monotone', + 'linear', + 'step', + 'stepAfter', + 'stepBefore', + ]; + + return ( +
+ {types.map((type) => ( +
+ + {type.charAt(0).toUpperCase() + type.slice(1)} Type + + + + + + + + + + +
+ ))} +
+ ); +}; + +SimpleAreaChart.storyName = 'Simple Area Chart'; +StackedAreaChart.storyName = 'Stacked Area Chart'; +AreaChartConnectNulls.storyName = 'Area Chart (Connect Nulls)'; +TinyAreaChart.storyName = 'Tiny Area Chart'; +AreaChartWithReferenceLine.storyName = 'Area Chart with Reference Line'; +MultipleAreaChart.storyName = 'Multiple Area Chart'; +AreaTypeVariations.storyName = 'Area Type Variations'; \ No newline at end of file diff --git a/packages/blade/src/components/Charts/AreaCharts/AreaCharts.tsx b/packages/blade/src/components/Charts/AreaCharts/AreaCharts.tsx new file mode 100644 index 00000000000..99d89bcc2a7 --- /dev/null +++ b/packages/blade/src/components/Charts/AreaCharts/AreaCharts.tsx @@ -0,0 +1,280 @@ +import React from 'react'; +import type { ComponentProps } from 'react'; +import styled from 'styled-components'; +import { + AreaChart as RechartsAreaChart, + Area as RechartsArea, + XAxis as RechartsXAxis, + YAxis as RechartsYAxis, + CartesianGrid as RechartsCartesianGrid, + Tooltip as RechartsTooltip, + Legend as RechartsLegend, + ResponsiveContainer as RechartsResponsiveContainer, + ReferenceLine as RechartsReferenceLine, +} from 'recharts'; +import { useTheme } from '~components/BladeProvider'; +import type { Theme } from '~components/BladeProvider'; +import type { StyledPropsBlade } from '~components/Box/styledProps'; +import { getStyledProps } from '~components/Box/styledProps'; +import { metaAttribute } from '~utils/metaAttribute'; +import BaseBox from '~components/Box/BaseBox'; +import { castWebType } from '~utils'; + +// BladeColorToken type for charts +export type BladeColorToken = + | 'surface.text.gray.normal' + | 'surface.text.gray.muted' + | 'interactive.background.primary.default' + | 'feedback.text.positive.subtle' + | 'feedback.text.negative.subtle' + | 'feedback.text.notice.subtle' + | 'feedback.text.information.subtle' + | string; + +// Area Chart specific interfaces +export interface AreaProps { + dataKey: string; + name: string; + type: 'step' | 'stepAfter' | 'stepBefore' | 'linear' | 'monotone'; + stackId?: string | number; + connectNulls?: boolean; + color?: BladeColorToken; + // Additional common props + fill?: string; + stroke?: string; + strokeWidth?: number; + fillOpacity?: number; + strokeOpacity?: number; +} + +export interface ReferenceLineProps { + y: number; + label: string; + color?: BladeColorToken; +} + +// Predefined chart colors using Blade tokens +const getChartColors = (theme: Theme): Record => ({ + primary: theme.colors.interactive.background.primary.default, + secondary: theme.colors.surface.text.gray.normal, + tertiary: theme.colors.feedback.text.positive.subtle, + quaternary: theme.colors.feedback.text.notice.subtle, + success: theme.colors.feedback.text.positive.subtle, + warning: theme.colors.feedback.text.notice.subtle, + error: theme.colors.feedback.text.negative.subtle, + info: theme.colors.feedback.text.information.subtle, + neutral: theme.colors.surface.text.gray.muted, + grid: theme.colors.surface.border.gray.muted, + background: theme.colors.surface.background.gray.intense, +}); + +// Curated palette for auto-assignment +const getCuratedPalette = (theme: Theme): string[] => [ + theme.colors.interactive.background.primary.default, + theme.colors.feedback.text.positive.subtle, + theme.colors.feedback.text.notice.subtle, + theme.colors.feedback.text.information.subtle, + theme.colors.surface.text.gray.muted, +]; + +// Helper function to resolve color tokens +const resolveColorToken = (color: BladeColorToken | undefined, theme: Theme): string => { + if (!color) return getChartColors(theme).primary; + + if ( + color.startsWith('surface.') || + color.startsWith('feedback.') || + color.startsWith('interactive.') + ) { + const parts = color.split('.'); + let value: Record = theme.colors; + for (const part of parts) { + value = value[part]; + } + return (value as unknown as string) || getChartColors(theme).primary; + } + + return color; +}; + +// Auto-assign colors from curated palette +let colorIndex = 0; +const getAutoAssignedColor = (theme: Theme): string => { + const palette = getCuratedPalette(theme); + const color = palette[colorIndex % palette.length]; + colorIndex++; + return color; +}; + +// TypeScript prop types +export type AreaChartProps = Omit, 'margin'> & + StyledPropsBlade & { + children?: React.ReactNode; + }; + +export type XAxisProps = ComponentProps; +export type YAxisProps = ComponentProps; +export type CartesianGridProps = ComponentProps; +export type TooltipProps = ComponentProps; +export type LegendProps = ComponentProps; +export type ResponsiveContainerProps = ComponentProps; + +// Styled wrapper for AreaChart with predefined margins +const StyledAreaChart = styled(RechartsAreaChart)<{ theme: Theme }>` + font-family: ${(props) => props.theme.typography.fonts.family.text}; +`; + +// Main components +export const AreaChart: React.FC = ({ children, ...props }) => { + const { theme } = useTheme(); + const styledProps = getStyledProps(props); + + // Predefined margins - not exposed to user + const defaultMargin = { + top: 16, + right: 16, + bottom: 16, + left: 16, + }; + + // Reset color index for each chart render + colorIndex = 0; + + return ( + + + {children} + + + ); +}; + +export const Area: React.FC = ({ + color, + type = 'monotone', + connectNulls = false, + fillOpacity = 0.6, + strokeWidth = 2, + ...props +}) => { + const { theme } = useTheme(); + const resolvedColor = color ? resolveColorToken(color, theme) : getAutoAssignedColor(theme); + + return ( + + ); +}; + +export const ReferenceLine: React.FC = ({ color, label, ...props }) => { + const { theme } = useTheme(); + const resolvedColor = resolveColorToken(color, theme); + + return ( + + ); +}; + +export const XAxis: React.FC = (props) => { + const { theme } = useTheme(); + + return ( + + ); +}; + +export const YAxis: React.FC = (props) => { + const { theme } = useTheme(); + + return ( + + ); +}; + +export const CartesianGrid: React.FC = (props) => { + const { theme } = useTheme(); + + return ( + + ); +}; + +export const Tooltip: React.FC = (props) => { + const { theme } = useTheme(); + + return ( + + ); +}; + +export const Legend: React.FC = (props) => { + const { theme } = useTheme(); + + return ( + + ); +}; + +export const ResponsiveContainer: React.FC = (props) => { + return ; +}; + +// Custom ChartTooltip component +export const ChartTooltip: React.FC = (props) => { + return ; +}; \ No newline at end of file diff --git a/packages/blade/src/components/Charts/AreaCharts/index.ts b/packages/blade/src/components/Charts/AreaCharts/index.ts new file mode 100644 index 00000000000..f43a02ae45d --- /dev/null +++ b/packages/blade/src/components/Charts/AreaCharts/index.ts @@ -0,0 +1,28 @@ +export { AreaChart, Area } from './AreaCharts'; + +export type { AreaChartProps, AreaProps } from './AreaCharts'; + +// Re-export common components from LineCharts to avoid duplication +export { + XAxis, + YAxis, + CartesianGrid, + Tooltip, + Legend, + ResponsiveContainer, + ReferenceLine, + ChartTooltip, +} from '../LineCharts'; + +export type { + XAxisProps, + YAxisProps, + CartesianGridProps, + TooltipProps, + LegendProps, + ResponsiveContainerProps, + BladeColorToken, +} from '../LineCharts'; + +// Re-export ReferenceLine types +export type { ReferenceLineProps } from './AreaCharts'; \ No newline at end of file diff --git a/packages/blade/src/components/Charts/README.md b/packages/blade/src/components/Charts/README.md index f3e85006cdf..cafe478d046 100644 --- a/packages/blade/src/components/Charts/README.md +++ b/packages/blade/src/components/Charts/README.md @@ -24,6 +24,13 @@ This directory contains Blade's chart components built on top of Recharts with d - **Label**: Styled labels for center text in donuts - **Tooltip/Legend/ResponsiveContainer**: Shared styled components +### AreaChart Components + +- **AreaChart**: Main area chart container component with predefined margins +- **Area**: Area component with auto-color assignment and stacking support +- **ReferenceLine**: Reference lines for area charts +- **XAxis/YAxis/CartesianGrid/Tooltip/Legend/ResponsiveContainer**: Shared styled components + ## Usage Examples ### Simple Line Chart @@ -42,6 +49,63 @@ This directory contains Blade's chart components built on top of Recharts with d ``` +### Simple Area Chart +```tsx + + + + + + + + + +``` + +### Stacked Area Chart +```tsx + + + + + + + + + + + + +``` + +### Area Chart with Null Connections +```tsx + + + + + + + + + +``` + +### Tiny Area Chart (Sparkline) +```tsx + + + + + +``` + ### Simple Pie Chart ```tsx const COLORS = ['interactive.background.primary.default', 'feedback.text.positive.subtle', 'feedback.text.notice.subtle', 'surface.text.gray.muted']; @@ -171,6 +235,31 @@ When using custom dot objects, you can configure: - **Interactive Focus**: Show only `activeDot` for hover states - **Data Emphasis**: Larger dots for important data points +## Area Component Props + +### Required Props +- `dataKey`: string - Key to identify data value in dataset (required) +- `name`: string - Display name for legend and tooltips (required) +- `type`: 'step' | 'stepAfter' | 'stepBefore' | 'linear' | 'monotone' - Interpolation type (required) + +### Optional Props +- `stackId`: string | number - Groups areas into stacks (required for multiple areas >2) +- `connectNulls`: boolean - Connect area over null data points (default: false) +- `color`: BladeColorToken - Color token for area fill (auto-assigned if not provided) +- `fillOpacity`: number - Opacity of area fill (default: 0.6) +- `strokeWidth`: number - Width of area border (default: 2) + +### Stacking Behavior +- For **2 or fewer areas**: No stackId needed, areas will naturally overlay +- For **3+ areas**: Must provide `stackId` to each area for proper stacking +- Areas with the same `stackId` will be stacked together + +### Auto-Color Assignment +- Colors are automatically assigned from a curated palette of Blade tokens +- **Curated Palette**: primary → success → warning → info → neutral +- Colors cycle through the palette for multiple areas +- Override with `color` prop for specific branding needs + ## Pie Component Props ### Donut Radius Options @@ -214,11 +303,15 @@ The components use Blade color tokens: - **Pie component with donutRadius, circleType, and paddingAngle** - **Cell component with color token support** - **Label component for center text in donuts** +- **AreaChart component with predefined margins** +- **Area component with auto-color assignment and stacking** +- **connectNulls support for handling null data points** +- **Curated color palette with automatic assignment** - Styled XAxis, YAxis, CartesianGrid, Tooltip, Legend - ResponsiveContainer re-export - ChartTooltip component - TypeScript interfaces -- Storybook examples with dot variations and pie chart examples +- Comprehensive Storybook examples for all chart types ⚠️ **Known Issues:** - Some TypeScript type conflicts between React and Recharts types @@ -226,8 +319,8 @@ The components use Blade color tokens: ## Next Steps -1. Fix TypeScript type issues with activeShape and margin props -2. Add more chart types (Bar, Area, etc.) +1. Fix TypeScript type issues with margin props across all chart types +2. Add more chart types (Bar, Scatter, etc.) 3. Add more comprehensive color token support 4. Add accessibility features 5. Add animation presets \ No newline at end of file diff --git a/packages/blade/src/components/Charts/index.ts b/packages/blade/src/components/Charts/index.ts index 96fa4dbfbb4..17efa1759f8 100644 --- a/packages/blade/src/components/Charts/index.ts +++ b/packages/blade/src/components/Charts/index.ts @@ -1,2 +1,10 @@ +// Export LineCharts (includes shared components) export * from './LineCharts'; -export * from './PieCharts'; \ No newline at end of file + +// Export PieChart specific components only +export { PieChart, Pie, Cell, Label } from './PieCharts'; +export type { PieChartProps, PieProps, CellProps, LabelProps } from './PieCharts'; + +// Export AreaChart specific components only +export { AreaChart, Area } from './AreaCharts'; +export type { AreaChartProps, AreaProps, ReferenceLineProps } from './AreaCharts'; \ No newline at end of file From b2e5faee32b590d85189326256cbaa8890612b6c Mon Sep 17 00:00:00 2001 From: tewarig Date: Tue, 26 Aug 2025 14:28:42 +0530 Subject: [PATCH 005/105] feat: add line chart --- .../Charts/AreaCharts/AreaCharts.stories.tsx | 302 ------------------ .../Charts/AreaCharts/AreaCharts.tsx | 280 ---------------- .../src/components/Charts/AreaCharts/index.ts | 28 -- .../Charts/LineCharts/lineCharts.tsx | 14 +- .../Charts/PieCharts/PieCharts.stories.tsx | 296 ----------------- .../components/Charts/PieCharts/PieCharts.tsx | 256 --------------- .../src/components/Charts/PieCharts/index.ts | 28 -- packages/blade/src/components/Charts/index.ts | 8 - 8 files changed, 10 insertions(+), 1202 deletions(-) delete mode 100644 packages/blade/src/components/Charts/AreaCharts/AreaCharts.stories.tsx delete mode 100644 packages/blade/src/components/Charts/AreaCharts/AreaCharts.tsx delete mode 100644 packages/blade/src/components/Charts/AreaCharts/index.ts delete mode 100644 packages/blade/src/components/Charts/PieCharts/PieCharts.stories.tsx delete mode 100644 packages/blade/src/components/Charts/PieCharts/PieCharts.tsx delete mode 100644 packages/blade/src/components/Charts/PieCharts/index.ts diff --git a/packages/blade/src/components/Charts/AreaCharts/AreaCharts.stories.tsx b/packages/blade/src/components/Charts/AreaCharts/AreaCharts.stories.tsx deleted file mode 100644 index c678e1ba73c..00000000000 --- a/packages/blade/src/components/Charts/AreaCharts/AreaCharts.stories.tsx +++ /dev/null @@ -1,302 +0,0 @@ -import type { StoryFn, Meta } from '@storybook/react'; -import React from 'react'; -import { - AreaChart, - Area, - XAxis, - YAxis, - CartesianGrid, - Tooltip, - Legend, - ResponsiveContainer, - ReferenceLine, - ChartTooltip, -} from './AreaCharts'; -import { Heading } from '~components/Typography/Heading'; -import { Sandbox } from '~utils/storybook/Sandbox'; -import StoryPageWrapper from '~utils/storybook/StoryPageWrapper'; -import { getStyledPropsArgTypes } from '~components/Box/BaseBox/storybookArgTypes'; - -const Page = (): React.ReactElement => { - return ( - - Usage - - {` - import { - AreaChart, - Area, - XAxis, - YAxis, - CartesianGrid, - Tooltip, - Legend, - ResponsiveContainer - } from '@razorpay/blade/components'; - - function App() { - const data = [ - { month: 'Jan', teamA: 4000 }, - { month: 'Feb', teamA: 3000 }, - { month: 'Mar', teamA: 2000 }, - { month: 'Apr', teamA: 2780 }, - { month: 'May', teamA: 1890 }, - { month: 'Jun', teamA: 2390 }, - ]; - - return ( - - - - - - - - - - ) - } - - export default App; - `} - - - ); -}; - -export default { - title: 'Components/Charts/AreaChart', - component: AreaChart, - tags: ['autodocs'], - argTypes: { - ...getStyledPropsArgTypes(), - }, - parameters: { - docs: { - page: Page, - }, - }, -} as Meta; - -// Sample data for charts -const chartData = [ - { month: 'Jan', teamA: 4000, teamB: 2400, teamC: 1800 }, - { month: 'Feb', teamA: 3000, teamB: 1398, teamC: 2200 }, - { month: 'Mar', teamA: 2000, teamB: 9800, teamC: 1500 }, - { month: 'Apr', teamA: 2780, teamB: 3908, teamC: 2800 }, - { month: 'May', teamA: 1890, teamB: 4800, teamC: 2100 }, - { month: 'Jun', teamA: 2390, teamB: 3800, teamC: 2500 }, -]; - -// Data with null values for connectNulls example -const dataWithNulls = [ - { month: 'Jan', sales: 4000 }, - { month: 'Feb', sales: 3000 }, - { month: 'Mar', sales: null }, - { month: 'Apr', sales: null }, - { month: 'May', sales: 1890 }, - { month: 'Jun', sales: 2390 }, -]; - -// Simple Area Chart -export const SimpleAreaChart: StoryFn = () => { - return ( -
- - - - - - - - - -
- ); -}; - -// Stacked Area Chart -export const StackedAreaChart: StoryFn = () => { - return ( -
- - - - - - - - - - - - -
- ); -}; - -// Area Chart that Connects Nulls -export const AreaChartConnectNulls: StoryFn = () => { - return ( -
- - - - - - - - - - -
- ); -}; - -// Tiny Area Chart (Sparkline) -export const TinyAreaChart: StoryFn = () => { - return ( -
- - - - - -
- ); -}; - -// Area Chart with Reference Line -export const AreaChartWithReferenceLine: StoryFn = () => { - return ( -
- - - - - - - - - - - -
- ); -}; - -// Multiple Area Chart (Non-Stacked) -export const MultipleAreaChart: StoryFn = () => { - return ( -
- - - - - - - - - - - -
- ); -}; - -// Area Chart Type Variations -export const AreaTypeVariations: StoryFn = () => { - const types: Array<'monotone' | 'linear' | 'step' | 'stepAfter' | 'stepBefore'> = [ - 'monotone', - 'linear', - 'step', - 'stepAfter', - 'stepBefore', - ]; - - return ( -
- {types.map((type) => ( -
- - {type.charAt(0).toUpperCase() + type.slice(1)} Type - - - - - - - - - - -
- ))} -
- ); -}; - -SimpleAreaChart.storyName = 'Simple Area Chart'; -StackedAreaChart.storyName = 'Stacked Area Chart'; -AreaChartConnectNulls.storyName = 'Area Chart (Connect Nulls)'; -TinyAreaChart.storyName = 'Tiny Area Chart'; -AreaChartWithReferenceLine.storyName = 'Area Chart with Reference Line'; -MultipleAreaChart.storyName = 'Multiple Area Chart'; -AreaTypeVariations.storyName = 'Area Type Variations'; \ No newline at end of file diff --git a/packages/blade/src/components/Charts/AreaCharts/AreaCharts.tsx b/packages/blade/src/components/Charts/AreaCharts/AreaCharts.tsx deleted file mode 100644 index 99d89bcc2a7..00000000000 --- a/packages/blade/src/components/Charts/AreaCharts/AreaCharts.tsx +++ /dev/null @@ -1,280 +0,0 @@ -import React from 'react'; -import type { ComponentProps } from 'react'; -import styled from 'styled-components'; -import { - AreaChart as RechartsAreaChart, - Area as RechartsArea, - XAxis as RechartsXAxis, - YAxis as RechartsYAxis, - CartesianGrid as RechartsCartesianGrid, - Tooltip as RechartsTooltip, - Legend as RechartsLegend, - ResponsiveContainer as RechartsResponsiveContainer, - ReferenceLine as RechartsReferenceLine, -} from 'recharts'; -import { useTheme } from '~components/BladeProvider'; -import type { Theme } from '~components/BladeProvider'; -import type { StyledPropsBlade } from '~components/Box/styledProps'; -import { getStyledProps } from '~components/Box/styledProps'; -import { metaAttribute } from '~utils/metaAttribute'; -import BaseBox from '~components/Box/BaseBox'; -import { castWebType } from '~utils'; - -// BladeColorToken type for charts -export type BladeColorToken = - | 'surface.text.gray.normal' - | 'surface.text.gray.muted' - | 'interactive.background.primary.default' - | 'feedback.text.positive.subtle' - | 'feedback.text.negative.subtle' - | 'feedback.text.notice.subtle' - | 'feedback.text.information.subtle' - | string; - -// Area Chart specific interfaces -export interface AreaProps { - dataKey: string; - name: string; - type: 'step' | 'stepAfter' | 'stepBefore' | 'linear' | 'monotone'; - stackId?: string | number; - connectNulls?: boolean; - color?: BladeColorToken; - // Additional common props - fill?: string; - stroke?: string; - strokeWidth?: number; - fillOpacity?: number; - strokeOpacity?: number; -} - -export interface ReferenceLineProps { - y: number; - label: string; - color?: BladeColorToken; -} - -// Predefined chart colors using Blade tokens -const getChartColors = (theme: Theme): Record => ({ - primary: theme.colors.interactive.background.primary.default, - secondary: theme.colors.surface.text.gray.normal, - tertiary: theme.colors.feedback.text.positive.subtle, - quaternary: theme.colors.feedback.text.notice.subtle, - success: theme.colors.feedback.text.positive.subtle, - warning: theme.colors.feedback.text.notice.subtle, - error: theme.colors.feedback.text.negative.subtle, - info: theme.colors.feedback.text.information.subtle, - neutral: theme.colors.surface.text.gray.muted, - grid: theme.colors.surface.border.gray.muted, - background: theme.colors.surface.background.gray.intense, -}); - -// Curated palette for auto-assignment -const getCuratedPalette = (theme: Theme): string[] => [ - theme.colors.interactive.background.primary.default, - theme.colors.feedback.text.positive.subtle, - theme.colors.feedback.text.notice.subtle, - theme.colors.feedback.text.information.subtle, - theme.colors.surface.text.gray.muted, -]; - -// Helper function to resolve color tokens -const resolveColorToken = (color: BladeColorToken | undefined, theme: Theme): string => { - if (!color) return getChartColors(theme).primary; - - if ( - color.startsWith('surface.') || - color.startsWith('feedback.') || - color.startsWith('interactive.') - ) { - const parts = color.split('.'); - let value: Record = theme.colors; - for (const part of parts) { - value = value[part]; - } - return (value as unknown as string) || getChartColors(theme).primary; - } - - return color; -}; - -// Auto-assign colors from curated palette -let colorIndex = 0; -const getAutoAssignedColor = (theme: Theme): string => { - const palette = getCuratedPalette(theme); - const color = palette[colorIndex % palette.length]; - colorIndex++; - return color; -}; - -// TypeScript prop types -export type AreaChartProps = Omit, 'margin'> & - StyledPropsBlade & { - children?: React.ReactNode; - }; - -export type XAxisProps = ComponentProps; -export type YAxisProps = ComponentProps; -export type CartesianGridProps = ComponentProps; -export type TooltipProps = ComponentProps; -export type LegendProps = ComponentProps; -export type ResponsiveContainerProps = ComponentProps; - -// Styled wrapper for AreaChart with predefined margins -const StyledAreaChart = styled(RechartsAreaChart)<{ theme: Theme }>` - font-family: ${(props) => props.theme.typography.fonts.family.text}; -`; - -// Main components -export const AreaChart: React.FC = ({ children, ...props }) => { - const { theme } = useTheme(); - const styledProps = getStyledProps(props); - - // Predefined margins - not exposed to user - const defaultMargin = { - top: 16, - right: 16, - bottom: 16, - left: 16, - }; - - // Reset color index for each chart render - colorIndex = 0; - - return ( - - - {children} - - - ); -}; - -export const Area: React.FC = ({ - color, - type = 'monotone', - connectNulls = false, - fillOpacity = 0.6, - strokeWidth = 2, - ...props -}) => { - const { theme } = useTheme(); - const resolvedColor = color ? resolveColorToken(color, theme) : getAutoAssignedColor(theme); - - return ( - - ); -}; - -export const ReferenceLine: React.FC = ({ color, label, ...props }) => { - const { theme } = useTheme(); - const resolvedColor = resolveColorToken(color, theme); - - return ( - - ); -}; - -export const XAxis: React.FC = (props) => { - const { theme } = useTheme(); - - return ( - - ); -}; - -export const YAxis: React.FC = (props) => { - const { theme } = useTheme(); - - return ( - - ); -}; - -export const CartesianGrid: React.FC = (props) => { - const { theme } = useTheme(); - - return ( - - ); -}; - -export const Tooltip: React.FC = (props) => { - const { theme } = useTheme(); - - return ( - - ); -}; - -export const Legend: React.FC = (props) => { - const { theme } = useTheme(); - - return ( - - ); -}; - -export const ResponsiveContainer: React.FC = (props) => { - return ; -}; - -// Custom ChartTooltip component -export const ChartTooltip: React.FC = (props) => { - return ; -}; \ No newline at end of file diff --git a/packages/blade/src/components/Charts/AreaCharts/index.ts b/packages/blade/src/components/Charts/AreaCharts/index.ts deleted file mode 100644 index f43a02ae45d..00000000000 --- a/packages/blade/src/components/Charts/AreaCharts/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -export { AreaChart, Area } from './AreaCharts'; - -export type { AreaChartProps, AreaProps } from './AreaCharts'; - -// Re-export common components from LineCharts to avoid duplication -export { - XAxis, - YAxis, - CartesianGrid, - Tooltip, - Legend, - ResponsiveContainer, - ReferenceLine, - ChartTooltip, -} from '../LineCharts'; - -export type { - XAxisProps, - YAxisProps, - CartesianGridProps, - TooltipProps, - LegendProps, - ResponsiveContainerProps, - BladeColorToken, -} from '../LineCharts'; - -// Re-export ReferenceLine types -export type { ReferenceLineProps } from './AreaCharts'; \ No newline at end of file diff --git a/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx b/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx index 99feeef2375..9d915e7330a 100644 --- a/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx +++ b/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx @@ -34,8 +34,12 @@ export type BladeColorToken = // Chart-specific interfaces based on user specifications export interface LineProps { type?: 'step' | 'stepAfter' | 'stepBefore' | 'linear' | 'monotone'; - dot?: boolean | { r?: number; fill?: string; stroke?: string; strokeWidth?: number; [key: string]: any }; - activeDot?: boolean | { r?: number; fill?: string; stroke?: string; strokeWidth?: number; [key: string]: any }; + dot?: + | boolean + | { r?: number; fill?: string; stroke?: string; strokeWidth?: number; [key: string]: any }; + activeDot?: + | boolean + | { r?: number; fill?: string; stroke?: string; strokeWidth?: number; [key: string]: any }; connectNulls?: boolean; legendType?: 'none' | 'line' | 'square' | 'diamond' | 'circle' | 'cross' | 'triangle' | 'wye'; dataKey: string; @@ -67,7 +71,7 @@ const getChartColors = (theme: Theme): Record => ({ // Helper function to resolve color tokens const resolveColorToken = (color: BladeColorToken | undefined, theme: Theme): string => { if (!color) return getChartColors(theme).primary; - + if ( color.startsWith('surface.') || color.startsWith('feedback.') || @@ -78,9 +82,10 @@ const resolveColorToken = (color: BladeColorToken | undefined, theme: Theme): st for (const part of parts) { value = value[part]; } + //@ts-expect-error return value || getChartColors(theme).primary; } - + return color; }; @@ -117,6 +122,7 @@ export const LineChart: React.FC = ({ children, ...props }) => { return ( + //@ts-ignore {children} diff --git a/packages/blade/src/components/Charts/PieCharts/PieCharts.stories.tsx b/packages/blade/src/components/Charts/PieCharts/PieCharts.stories.tsx deleted file mode 100644 index 727fc945791..00000000000 --- a/packages/blade/src/components/Charts/PieCharts/PieCharts.stories.tsx +++ /dev/null @@ -1,296 +0,0 @@ -import type { StoryFn, Meta } from '@storybook/react'; -import React from 'react'; -import { - PieChart, - Pie, - Cell, - Tooltip, - Legend, - ResponsiveContainer, - Label, - ChartTooltip, -} from './PieCharts'; -import { Heading } from '~components/Typography/Heading'; -import { Text } from '~components/Typography/Text'; -import { Sandbox } from '~utils/storybook/Sandbox'; -import StoryPageWrapper from '~utils/storybook/StoryPageWrapper'; -import { getStyledPropsArgTypes } from '~components/Box/BaseBox/storybookArgTypes'; - -const Page = (): React.ReactElement => { - return ( - - Usage - - {` - import { - PieChart, - Pie, - Cell, - Tooltip, - Legend, - ResponsiveContainer - } from '@razorpay/blade/components'; - - function App() { - const data = [ - { name: 'Group A', value: 400 }, - { name: 'Group B', value: 300 }, - { name: 'Group C', value: 300 }, - { name: 'Group D', value: 200 }, - ]; - - const COLORS = ['interactive.background.primary.default', 'feedback.text.positive.subtle', 'feedback.text.notice.subtle', 'surface.text.gray.muted']; - - return ( - - - - {data.map((entry, index) => ( - - ))} - - - - - - ) - } - - export default App; - `} - - - ); -}; - -export default { - title: 'Components/Charts/PieChart', - component: PieChart, - tags: ['autodocs'], - argTypes: { - ...getStyledPropsArgTypes(), - }, - parameters: { - docs: { - page: Page, - }, - }, -} as Meta; - -// Sample data for charts -const pieData = [ - { name: 'Group A', value: 400 }, - { name: 'Group B', value: 300 }, - { name: 'Group C', value: 300 }, - { name: 'Group D', value: 200 }, -]; - -const COLORS = [ - 'interactive.background.primary.default', - 'feedback.text.positive.subtle', - 'feedback.text.notice.subtle', - 'surface.text.gray.muted', -]; - -// 1. Simple Pie Chart -export const SimplePieChart: StoryFn = () => { - return ( -
- - - }> - {pieData.map((entry, index) => ( - - ))} - - - - - -
- ); -}; - -// 2. Half Pie Chart (StraightAngleChart) -export const HalfPieChart: StoryFn = () => { - return ( -
- - - - {pieData.map((entry, index) => ( - - ))} - - - - - -
- ); -}; - -// 3. Simple Donut Chart -export const DonutChart: StoryFn = () => { - return ( -
- - - - {pieData.map((entry, index) => ( - - ))} - - - - - -
- ); -}; - -// 4. Donut with Text in Center -export const DonutWithCenterText: StoryFn = () => { - const totalValue = pieData.reduce((sum, entry) => sum + entry.value, 0); - - return ( -
- - - - {pieData.map((entry, index) => ( - - ))} - - - - - -
- ); -}; - -// 5. Donut Size Variations -export const DonutSizeVariations: StoryFn = () => { - return ( -
- {(['small', 'medium', 'large', 'extraLarge'] as const).map((size) => ( -
- - {size.charAt(0).toUpperCase() + size.slice(1)} Donut - - - - - {pieData.slice(0, 3).map((entry, index) => ( - - ))} - - - - -
- ))} -
- ); -}; - -// 6. Circle Type Variations -export const CircleTypeVariations: StoryFn = () => { - return ( -
- {(['full', 'half', 'quarter'] as const).map((type) => ( -
- - {type.charAt(0).toUpperCase() + type.slice(1)} Circle - - - - - {pieData.map((entry, index) => ( - - ))} - - - - -
- ))} -
- ); -}; - -SimplePieChart.storyName = 'Simple Pie Chart'; -HalfPieChart.storyName = 'Half Pie Chart'; -DonutChart.storyName = 'Donut Chart'; -DonutWithCenterText.storyName = 'Donut with Center Text'; -DonutSizeVariations.storyName = 'Donut Size Variations'; -CircleTypeVariations.storyName = 'Circle Type Variations'; diff --git a/packages/blade/src/components/Charts/PieCharts/PieCharts.tsx b/packages/blade/src/components/Charts/PieCharts/PieCharts.tsx deleted file mode 100644 index 339fad6a034..00000000000 --- a/packages/blade/src/components/Charts/PieCharts/PieCharts.tsx +++ /dev/null @@ -1,256 +0,0 @@ -import React from 'react'; -import type { ComponentProps } from 'react'; -import styled from 'styled-components'; -import { - PieChart as RechartsPieChart, - Pie as RechartsPie, - Cell as RechartsCell, - Tooltip as RechartsTooltip, - Legend as RechartsLegend, - ResponsiveContainer as RechartsResponsiveContainer, - Label as RechartsLabel, -} from 'recharts'; -import { useTheme } from '~components/BladeProvider'; -import type { Theme } from '~components/BladeProvider'; -import type { StyledPropsBlade } from '~components/Box/styledProps'; -import { getStyledProps } from '~components/Box/styledProps'; -import { metaAttribute } from '~utils/metaAttribute'; -import BaseBox from '~components/Box/BaseBox'; -import { castWebType } from '~utils'; - -// BladeColorToken type for charts -export type BladeColorToken = - | 'surface.text.gray.normal' - | 'surface.text.gray.muted' - | 'interactive.background.primary.default' - | 'feedback.text.positive.subtle' - | 'feedback.text.negative.subtle' - | 'feedback.text.notice.subtle' - | 'feedback.text.information.subtle' - | string; - -// Donut radius configurations -type DonutRadius = 'small' | 'medium' | 'large' | 'extraLarge' | 'none'; -type CircleType = 'full' | 'half' | 'quarter'; -type PaddingAngle = 'none' | 'small' | 'medium' | 'large' | 'extraLarge'; - -// Pie Chart specific interfaces -export interface PieProps extends Omit, 'innerRadius' | 'outerRadius' | 'startAngle' | 'endAngle' | 'paddingAngle'> { - dataKey: string; - nameKey?: string; - data: { [key: string]: string | number }[]; - cx?: string | number; - cy?: string | number; - donutRadius?: DonutRadius; - circleType?: CircleType; - paddingAngle?: PaddingAngle; - activeShape?: React.ReactElement | ((props: any) => React.ReactNode); -} - -export interface CellProps extends ComponentProps { - fill?: BladeColorToken; -} - -// Chart color tokens -const getChartColors = (theme: Theme): Record => ({ - primary: theme.colors.interactive.background.primary.default, - secondary: theme.colors.surface.text.gray.normal, - tertiary: theme.colors.feedback.text.positive.subtle, - quaternary: theme.colors.feedback.text.notice.subtle, - success: theme.colors.feedback.text.positive.subtle, - warning: theme.colors.feedback.text.notice.subtle, - error: theme.colors.feedback.text.negative.subtle, - info: theme.colors.feedback.text.information.subtle, - neutral: theme.colors.surface.text.gray.muted, -}); - -// Helper function to resolve color tokens -const resolveColorToken = (color: BladeColorToken | undefined, theme: Theme): string => { - if (!color) return getChartColors(theme).primary; - - if ( - color.startsWith('surface.') || - color.startsWith('feedback.') || - color.startsWith('interactive.') - ) { - const parts = color.split('.'); - let value: Record = theme.colors; - for (const part of parts) { - value = value[part]; - } - return (value as string) || getChartColors(theme).primary; - } - - return color; -}; - -// Helper functions for radius and angle calculations -const getDonutRadiusValues = (donutRadius: DonutRadius): { innerRadius: number; outerRadius: number } => { - switch (donutRadius) { - case 'small': - return { innerRadius: 40, outerRadius: 80 }; - case 'medium': - return { innerRadius: 50, outerRadius: 90 }; - case 'large': - return { innerRadius: 60, outerRadius: 100 }; - case 'extraLarge': - return { innerRadius: 70, outerRadius: 110 }; - case 'none': - default: - return { innerRadius: 0, outerRadius: 80 }; - } -}; - -const getCircleTypeAngles = (circleType: CircleType): { startAngle: number; endAngle: number } => { - switch (circleType) { - case 'half': - return { startAngle: 180, endAngle: 0 }; - case 'quarter': - return { startAngle: 270, endAngle: 0 }; - case 'full': - default: - return { startAngle: 0, endAngle: 360 }; - } -}; - -const getPaddingAngleValue = (paddingAngle: PaddingAngle): number => { - switch (paddingAngle) { - case 'small': - return 1; - case 'medium': - return 2; - case 'large': - return 4; - case 'extraLarge': - return 6; - case 'none': - default: - return 0; - } -}; - -// TypeScript prop types -export type PieChartProps = ComponentProps & StyledPropsBlade & { - children?: React.ReactNode; -}; - -export type TooltipProps = ComponentProps; -export type LegendProps = ComponentProps; -export type ResponsiveContainerProps = ComponentProps; -export type LabelProps = ComponentProps; - -// Styled wrapper for PieChart -const StyledPieChart = styled(RechartsPieChart)<{ theme: Theme }>` - font-family: ${(props) => props.theme.typography.fonts.family.text}; -`; - -// Main components -export const PieChart: React.FC = ({ children, ...props }) => { - const { theme } = useTheme(); - const styledProps = getStyledProps(props); - - return ( - - - {children} - - - ); -}; - -export const Pie: React.FC = ({ - donutRadius = 'none', - circleType = 'full', - paddingAngle = 'none', - cx = '50%', - cy = '50%', - ...props -}) => { - const radiusValues = getDonutRadiusValues(donutRadius); - const angleValues = getCircleTypeAngles(circleType); - const paddingValue = getPaddingAngleValue(paddingAngle); - - return ( - - ); -}; - -export const Cell: React.FC = ({ fill, ...props }) => { - const { theme } = useTheme(); - const resolvedColor = fill ? resolveColorToken(fill, theme) : undefined; - - return ( - - ); -}; - -export const Tooltip: React.FC = (props) => { - const { theme } = useTheme(); - - return ( - - ); -}; - -export const Legend: React.FC = (props) => { - const { theme } = useTheme(); - - return ( - - ); -}; - -export const ResponsiveContainer: React.FC = (props) => { - return ; -}; - -export const Label: React.FC = (props) => { - const { theme } = useTheme(); - - return ( - - ); -}; - -// Custom ChartTooltip component -export const ChartTooltip: React.FC = (props) => { - return ; -}; diff --git a/packages/blade/src/components/Charts/PieCharts/index.ts b/packages/blade/src/components/Charts/PieCharts/index.ts deleted file mode 100644 index 41b3cf11a4a..00000000000 --- a/packages/blade/src/components/Charts/PieCharts/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -export { - PieChart, - Pie, - Cell, - Label, -} from './PieCharts'; - -export type { - PieChartProps, - PieProps, - CellProps, - LabelProps, -} from './PieCharts'; - -// Re-export common components from LineCharts to avoid duplication -export { - Tooltip, - Legend, - ResponsiveContainer, - ChartTooltip, -} from '../LineCharts'; - -export type { - TooltipProps, - LegendProps, - ResponsiveContainerProps, - BladeColorToken, -} from '../LineCharts'; diff --git a/packages/blade/src/components/Charts/index.ts b/packages/blade/src/components/Charts/index.ts index 17efa1759f8..4a33551e2f2 100644 --- a/packages/blade/src/components/Charts/index.ts +++ b/packages/blade/src/components/Charts/index.ts @@ -1,10 +1,2 @@ // Export LineCharts (includes shared components) export * from './LineCharts'; - -// Export PieChart specific components only -export { PieChart, Pie, Cell, Label } from './PieCharts'; -export type { PieChartProps, PieProps, CellProps, LabelProps } from './PieCharts'; - -// Export AreaChart specific components only -export { AreaChart, Area } from './AreaCharts'; -export type { AreaChartProps, AreaProps, ReferenceLineProps } from './AreaCharts'; \ No newline at end of file From 26c822b64e2aefd4baff6d888938f699e8e66936 Mon Sep 17 00:00:00 2001 From: tewarig Date: Tue, 26 Aug 2025 23:11:06 +0530 Subject: [PATCH 006/105] refactor: charts and base components --- .../Charts/BaseChartComponents/axis.tsx | 82 +++++ .../Charts/BaseChartComponents/index.ts | 2 + .../src/components/Charts/LineCharts/index.ts | 6 - .../Charts/LineCharts/lineCharts.tsx | 69 ---- .../blade/src/components/Charts/README.md | 326 ------------------ packages/blade/src/components/Charts/index.ts | 1 + 6 files changed, 85 insertions(+), 401 deletions(-) create mode 100644 packages/blade/src/components/Charts/BaseChartComponents/axis.tsx create mode 100644 packages/blade/src/components/Charts/BaseChartComponents/index.ts delete mode 100644 packages/blade/src/components/Charts/README.md diff --git a/packages/blade/src/components/Charts/BaseChartComponents/axis.tsx b/packages/blade/src/components/Charts/BaseChartComponents/axis.tsx new file mode 100644 index 00000000000..792d538d472 --- /dev/null +++ b/packages/blade/src/components/Charts/BaseChartComponents/axis.tsx @@ -0,0 +1,82 @@ +import React from 'react'; +import type { ComponentProps } from 'react'; +import { + LineChart as RechartsLineChart, + Line as RechartsLine, + XAxis as RechartsXAxis, + YAxis as RechartsYAxis, + CartesianGrid as RechartsCartesianGrid, + Tooltip as RechartsTooltip, + Legend as RechartsLegend, + ResponsiveContainer as RechartsResponsiveContainer, + ReferenceLine as RechartsReferenceLine, +} from 'recharts'; +import { useTheme } from '~components/BladeProvider'; +// BladeColorToken type for charts +export type BladeColorToken = + | 'surface.text.gray.normal' + | 'surface.text.gray.muted' + | 'interactive.background.primary.default' + | 'feedback.text.positive.subtle' + | 'feedback.text.negative.subtle' + | 'feedback.text.notice.subtle' + | 'feedback.text.information.subtle' + | string; + +export interface ReferenceLineProps { + y?: number; + x?: number; + label?: string; + color?: BladeColorToken; +} + +export type XAxisProps = ComponentProps; +export type YAxisProps = ComponentProps; +export type CartesianGridProps = ComponentProps; + +export const XAxis: React.FC = (props) => { + const { theme } = useTheme(); + + return ( + + ); +}; + +export const YAxis: React.FC = (props) => { + const { theme } = useTheme(); + + return ( + + ); +}; + +export const CartesianGrid: React.FC = (props) => { + const { theme } = useTheme(); + + return ( + + ); +}; diff --git a/packages/blade/src/components/Charts/BaseChartComponents/index.ts b/packages/blade/src/components/Charts/BaseChartComponents/index.ts new file mode 100644 index 00000000000..e3cb13428be --- /dev/null +++ b/packages/blade/src/components/Charts/BaseChartComponents/index.ts @@ -0,0 +1,2 @@ +export type { XAxisProps, YAxisProps, CartesianGridProps } from './axis'; +export { XAxis, YAxis, CartesianGrid } from './axis'; diff --git a/packages/blade/src/components/Charts/LineCharts/index.ts b/packages/blade/src/components/Charts/LineCharts/index.ts index 9e07e2616e7..f6c7720260d 100644 --- a/packages/blade/src/components/Charts/LineCharts/index.ts +++ b/packages/blade/src/components/Charts/LineCharts/index.ts @@ -1,9 +1,6 @@ export { LineChart, Line, - XAxis, - YAxis, - CartesianGrid, Tooltip, Legend, ResponsiveContainer, @@ -14,9 +11,6 @@ export { export type { LineChartProps, LineProps, - XAxisProps, - YAxisProps, - CartesianGridProps, TooltipProps, LegendProps, ResponsiveContainerProps, diff --git a/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx b/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx index 9d915e7330a..ad589e5d58e 100644 --- a/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx +++ b/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx @@ -48,13 +48,6 @@ export interface LineProps { strokeStyle?: 'dotted' | 'dashed' | 'solid'; } -export interface ReferenceLineProps { - y?: number; - x?: number; - label?: string; - color?: BladeColorToken; -} - // Predefined chart colors using Blade tokens const getChartColors = (theme: Theme): Record => ({ primary: theme.colors.interactive.background.primary.default, @@ -168,68 +161,6 @@ export const Line: React.FC = ({ ); }; -export const ReferenceLine: React.FC = ({ color, label, ...props }) => { - const { theme } = useTheme(); - const resolvedColor = resolveColorToken(color, theme); - - return ( - - ); -}; - -export const XAxis: React.FC = (props) => { - const { theme } = useTheme(); - - return ( - - ); -}; - -export const YAxis: React.FC = (props) => { - const { theme } = useTheme(); - - return ( - - ); -}; - -export const CartesianGrid: React.FC = (props) => { - const { theme } = useTheme(); - - return ( - - ); -}; - export const Tooltip: React.FC = (props) => { const { theme } = useTheme(); diff --git a/packages/blade/src/components/Charts/README.md b/packages/blade/src/components/Charts/README.md deleted file mode 100644 index cafe478d046..00000000000 --- a/packages/blade/src/components/Charts/README.md +++ /dev/null @@ -1,326 +0,0 @@ -# Charts Components - -This directory contains Blade's chart components built on top of Recharts with design system styling. - -## Components - -### LineChart Components - -- **LineChart**: Main container component with predefined margins -- **Line**: Chart line with custom styling options (solid, dashed, dotted) -- **ReferenceLine**: Reference lines for markers -- **XAxis/YAxis**: Styled axis components -- **CartesianGrid**: Styled grid component -- **Tooltip**: Themed tooltip -- **Legend**: Themed legend -- **ResponsiveContainer**: Responsive wrapper -- **ChartTooltip**: Custom tooltip for forecast charts - -### PieChart Components - -- **PieChart**: Main pie chart container component -- **Pie**: Pie/donut chart with configurable radius and angles -- **Cell**: Individual pie slice with color tokens -- **Label**: Styled labels for center text in donuts -- **Tooltip/Legend/ResponsiveContainer**: Shared styled components - -### AreaChart Components - -- **AreaChart**: Main area chart container component with predefined margins -- **Area**: Area component with auto-color assignment and stacking support -- **ReferenceLine**: Reference lines for area charts -- **XAxis/YAxis/CartesianGrid/Tooltip/Legend/ResponsiveContainer**: Shared styled components - -## Usage Examples - -### Simple Line Chart -```tsx - - - - - - - - - - - - -``` - -### Simple Area Chart -```tsx - - - - - - - - - -``` - -### Stacked Area Chart -```tsx - - - - - - - - - - - - -``` - -### Area Chart with Null Connections -```tsx - - - - - - - - - -``` - -### Tiny Area Chart (Sparkline) -```tsx - - - - - -``` - -### Simple Pie Chart -```tsx -const COLORS = ['interactive.background.primary.default', 'feedback.text.positive.subtle', 'feedback.text.notice.subtle', 'surface.text.gray.muted']; - - - - - {data.map((entry, index) => ( - - ))} - - - - - -``` - -### Donut Chart -```tsx - - - - {data.map((entry, index) => ( - - ))} - - - - - -``` - -### Donut with Center Text -```tsx - - - - {data.map((entry, index) => ( - - ))} - - - - - -``` - -### Half Pie Chart -```tsx - - - - {data.map((entry, index) => ( - - ))} - - - - - -``` - -## Line Component Props - -### Dot Configuration Options -The `Line` component supports flexible dot configuration: - -```tsx -// No dots for clean minimal charts - - -// Default themed dots - - -// Custom dot styling with size and colors - - -// Only active dots on hover (cleaner look) - - -// Large custom dots - -``` - -### Dot Configuration Properties -When using custom dot objects, you can configure: -- `r`: Radius of the dot -- `fill`: Fill color of the dot -- `stroke`: Stroke color of the dot -- `strokeWidth`: Width of the stroke -- Any other SVG circle properties - -### Use Cases -- **Tiny Charts**: `dot={false}` for minimal clutter -- **Dense Data**: Remove dots to prevent overlap -- **Brand Colors**: Custom colors that match your design -- **Interactive Focus**: Show only `activeDot` for hover states -- **Data Emphasis**: Larger dots for important data points - -## Area Component Props - -### Required Props -- `dataKey`: string - Key to identify data value in dataset (required) -- `name`: string - Display name for legend and tooltips (required) -- `type`: 'step' | 'stepAfter' | 'stepBefore' | 'linear' | 'monotone' - Interpolation type (required) - -### Optional Props -- `stackId`: string | number - Groups areas into stacks (required for multiple areas >2) -- `connectNulls`: boolean - Connect area over null data points (default: false) -- `color`: BladeColorToken - Color token for area fill (auto-assigned if not provided) -- `fillOpacity`: number - Opacity of area fill (default: 0.6) -- `strokeWidth`: number - Width of area border (default: 2) - -### Stacking Behavior -- For **2 or fewer areas**: No stackId needed, areas will naturally overlay -- For **3+ areas**: Must provide `stackId` to each area for proper stacking -- Areas with the same `stackId` will be stacked together - -### Auto-Color Assignment -- Colors are automatically assigned from a curated palette of Blade tokens -- **Curated Palette**: primary → success → warning → info → neutral -- Colors cycle through the palette for multiple areas -- Override with `color` prop for specific branding needs - -## Pie Component Props - -### Donut Radius Options -- `none`: Regular pie chart (innerRadius: 0) -- `small`: Small donut hole (innerRadius: 40, outerRadius: 80) -- `medium`: Medium donut hole (innerRadius: 50, outerRadius: 90) -- `large`: Large donut hole (innerRadius: 60, outerRadius: 100) -- `extraLarge`: Extra large donut hole (innerRadius: 70, outerRadius: 110) - -### Circle Type Options -- `full`: Complete 360° circle (default) -- `half`: Semi-circle (180°) -- `quarter`: Quarter circle (90°) - -### Padding Angle Options -- `none`: No spacing between slices (default) -- `small`: 1px spacing -- `medium`: 2px spacing -- `large`: 4px spacing -- `extraLarge`: 6px spacing - -## Color Tokens - -The components use Blade color tokens: -- `surface.text.gray.normal` -- `surface.text.gray.muted` -- `interactive.background.primary.default` -- `feedback.text.positive.subtle` -- `feedback.text.negative.subtle` -- `feedback.text.notice.subtle` -- `feedback.text.information.subtle` - -## Implementation Status - -✅ **Completed:** -- LineChart component with predefined margins -- Line component with custom color and stroke style support -- **Optional dots and activeDots for Line component** -- ReferenceLine component -- **PieChart component with Blade styling** -- **Pie component with donutRadius, circleType, and paddingAngle** -- **Cell component with color token support** -- **Label component for center text in donuts** -- **AreaChart component with predefined margins** -- **Area component with auto-color assignment and stacking** -- **connectNulls support for handling null data points** -- **Curated color palette with automatic assignment** -- Styled XAxis, YAxis, CartesianGrid, Tooltip, Legend -- ResponsiveContainer re-export -- ChartTooltip component -- TypeScript interfaces -- Comprehensive Storybook examples for all chart types - -⚠️ **Known Issues:** -- Some TypeScript type conflicts between React and Recharts types -- Need to refine color token resolution for all theme modes - -## Next Steps - -1. Fix TypeScript type issues with margin props across all chart types -2. Add more chart types (Bar, Scatter, etc.) -3. Add more comprehensive color token support -4. Add accessibility features -5. Add animation presets \ No newline at end of file diff --git a/packages/blade/src/components/Charts/index.ts b/packages/blade/src/components/Charts/index.ts index 4a33551e2f2..b7843c269e7 100644 --- a/packages/blade/src/components/Charts/index.ts +++ b/packages/blade/src/components/Charts/index.ts @@ -1,2 +1,3 @@ // Export LineCharts (includes shared components) export * from './LineCharts'; +export * from './BaseChartComponents'; \ No newline at end of file From 8310cb232dd3e92bc1ae19641c12e3477e82d22d Mon Sep 17 00:00:00 2001 From: tewarig Date: Tue, 26 Aug 2025 23:22:14 +0530 Subject: [PATCH 007/105] chore: code refactor --- .../Charts/BaseChartComponents/axis.tsx | 12 ++++++++++++ .../Charts/BaseChartComponents/index.ts | 4 ++-- .../Charts/LineCharts/LineCharts.stories.tsx | 16 +++------------- .../components/Charts/LineCharts/lineCharts.tsx | 1 - 4 files changed, 17 insertions(+), 16 deletions(-) diff --git a/packages/blade/src/components/Charts/BaseChartComponents/axis.tsx b/packages/blade/src/components/Charts/BaseChartComponents/axis.tsx index 792d538d472..cd2f3ab16a0 100644 --- a/packages/blade/src/components/Charts/BaseChartComponents/axis.tsx +++ b/packages/blade/src/components/Charts/BaseChartComponents/axis.tsx @@ -80,3 +80,15 @@ export const CartesianGrid: React.FC = (props) => { /> ); }; + +export const ReferenceLine: React.FC = ({ color, label, ...props }) => { + return ( + + ); +}; diff --git a/packages/blade/src/components/Charts/BaseChartComponents/index.ts b/packages/blade/src/components/Charts/BaseChartComponents/index.ts index e3cb13428be..c42ee46aece 100644 --- a/packages/blade/src/components/Charts/BaseChartComponents/index.ts +++ b/packages/blade/src/components/Charts/BaseChartComponents/index.ts @@ -1,2 +1,2 @@ -export type { XAxisProps, YAxisProps, CartesianGridProps } from './axis'; -export { XAxis, YAxis, CartesianGrid } from './axis'; +export type { XAxisProps, YAxisProps, CartesianGridProps, ReferenceLineProps } from './axis'; +export { XAxis, YAxis, CartesianGrid, ReferenceLine } from './axis'; diff --git a/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx b/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx index 2c687398fda..898232e37c9 100644 --- a/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx +++ b/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx @@ -1,17 +1,7 @@ import type { StoryFn, Meta } from '@storybook/react'; import React from 'react'; -import { - LineChart, - Line, - XAxis, - YAxis, - CartesianGrid, - Tooltip, - Legend, - ResponsiveContainer, - ReferenceLine, - ChartTooltip, -} from './lineCharts'; +import { XAxis, YAxis, ReferenceLine, CartesianGrid } from '../BaseChartComponents'; +import { LineChart, Line, Tooltip, Legend, ResponsiveContainer, ChartTooltip } from './lineCharts'; import { Heading } from '~components/Typography/Heading'; import { Sandbox } from '~utils/storybook/Sandbox'; import StoryPageWrapper from '~utils/storybook/StoryPageWrapper'; @@ -244,4 +234,4 @@ SimpleLineChart.storyName = 'Simple Line Chart'; TinyLineChart.storyName = 'Tiny Line Chart'; LineChartWithCustomDots.storyName = 'Line Chart with Custom Dots'; DotConfigurationShowcase.storyName = 'Dot Configuration Showcase'; -ForecastLineChart.storyName = 'Forecast Line Chart'; \ No newline at end of file +ForecastLineChart.storyName = 'Forecast Line Chart'; diff --git a/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx b/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx index ad589e5d58e..2fcf7ebd8d3 100644 --- a/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx +++ b/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx @@ -115,7 +115,6 @@ export const LineChart: React.FC = ({ children, ...props }) => { return ( - //@ts-ignore {children} From b14f469ab260005ad6d6311ca76fa9e743eabae1 Mon Sep 17 00:00:00 2001 From: tewarig Date: Wed, 27 Aug 2025 22:33:27 +0530 Subject: [PATCH 008/105] fix: line chart styling and offset colors --- .../Charts/BaseChartComponents/axis.tsx | 47 +++++++++++++++++-- .../Charts/LineCharts/lineCharts.tsx | 17 ++----- 2 files changed, 47 insertions(+), 17 deletions(-) diff --git a/packages/blade/src/components/Charts/BaseChartComponents/axis.tsx b/packages/blade/src/components/Charts/BaseChartComponents/axis.tsx index cd2f3ab16a0..09f4f5ba89e 100644 --- a/packages/blade/src/components/Charts/BaseChartComponents/axis.tsx +++ b/packages/blade/src/components/Charts/BaseChartComponents/axis.tsx @@ -12,6 +12,7 @@ import { ReferenceLine as RechartsReferenceLine, } from 'recharts'; import { useTheme } from '~components/BladeProvider'; +import type { Theme } from '~components/BladeProvider'; // BladeColorToken type for charts export type BladeColorToken = | 'surface.text.gray.normal' @@ -28,12 +29,34 @@ export interface ReferenceLineProps { x?: number; label?: string; color?: BladeColorToken; + labelPosition?: 'left' | 'right' | 'top' | 'bottom'; + labelOffset?: number; } export type XAxisProps = ComponentProps; export type YAxisProps = ComponentProps; export type CartesianGridProps = ComponentProps; +// Helper function to resolve color tokens +const resolveColorToken = (color: BladeColorToken | undefined, theme: Theme): string => { + if (!color) return theme.colors.interactive.background.primary.default; + + if ( + color.startsWith('surface.') || + color.startsWith('feedback.') || + color.startsWith('interactive.') + ) { + const parts = color.split('.'); + let value: Record = theme.colors; + for (const part of parts) { + value = value[part] as Record; + } + return ((value as unknown) as string) || theme.colors.interactive.background.primary.default; + } + + return color; +}; + export const XAxis: React.FC = (props) => { const { theme } = useTheme(); @@ -81,13 +104,31 @@ export const CartesianGrid: React.FC = (props) => { ); }; -export const ReferenceLine: React.FC = ({ color, label, ...props }) => { +export const ReferenceLine: React.FC = ({ + color, + label, + labelPosition = 'right', + labelOffset = 10, + ...props +}) => { + const { theme } = useTheme(); + const resolvedColor = color + ? resolveColorToken(color, theme) + : theme.colors.surface.text.gray.normal; + return ( ); diff --git a/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx b/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx index 2fcf7ebd8d3..08789413587 100644 --- a/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx +++ b/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx @@ -136,25 +136,14 @@ export const Line: React.FC = ({ const strokeDasharray = strokeStyle === 'dashed' ? '5 5' : strokeStyle === 'dotted' ? '2 2' : undefined; - // Configure dot and activeDot based on props - const dotConfig = - dot === false ? false : dot === true ? { fill: resolvedColor, strokeWidth: 0, r: 4 } : dot; - - const activeDotConfig = - activeDot === false - ? false - : activeDot === true - ? { r: 6, strokeWidth: 0, fill: resolvedColor } - : activeDot; - return ( ); From 9e0f88f107f203aaccda1c17e75c79e03a389010 Mon Sep 17 00:00:00 2001 From: tewarig Date: Wed, 27 Aug 2025 23:11:43 +0530 Subject: [PATCH 009/105] fix: legend color in recharts --- .../Charts/LineCharts/lineCharts.tsx | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx b/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx index 08789413587..3608ecddcaf 100644 --- a/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx +++ b/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx @@ -19,6 +19,8 @@ import { getStyledProps } from '~components/Box/styledProps'; import { metaAttribute } from '~utils/metaAttribute'; import BaseBox from '~components/Box/BaseBox'; import { castWebType } from '~utils'; +import { Text } from '~components/Typography'; +import { Box } from '~components/Box'; // BladeColorToken type for charts export type BladeColorToken = @@ -169,6 +171,44 @@ export const Tooltip: React.FC = (props) => { ); }; +const CustomSquareLegend = (props: any) => { + const { payload } = props; + + return ( +
    + {payload.map((entry, index) => ( +
  • + + + {/* Legend text with custom color and size */} + {/* {entry.value}{' '} */} + + {entry.value} + + + + {/* Changed text color to a dark gray */} +
  • + ))} +
+ ); +}; + export const Legend: React.FC = (props) => { const { theme } = useTheme(); @@ -179,6 +219,7 @@ export const Legend: React.FC = (props) => { fontSize: theme.typography.fonts.size[100], color: theme.colors.surface.text.gray.normal, }} + content={} {...props} /> ); From de5d214907a41f2393fffa62bf544f08156580cc Mon Sep 17 00:00:00 2001 From: tewarig Date: Wed, 27 Aug 2025 23:16:45 +0530 Subject: [PATCH 010/105] chore: update legend styles --- .../src/components/Charts/LineCharts/lineCharts.tsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx b/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx index 3608ecddcaf..33277f43d6c 100644 --- a/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx +++ b/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx @@ -175,7 +175,15 @@ const CustomSquareLegend = (props: any) => { const { payload } = props; return ( -
    +
      {payload.map((entry, index) => (
    • Date: Thu, 28 Aug 2025 00:16:43 +0530 Subject: [PATCH 011/105] fix: add color in reference line --- .../blade/src/components/Charts/BaseChartComponents/axis.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/blade/src/components/Charts/BaseChartComponents/axis.tsx b/packages/blade/src/components/Charts/BaseChartComponents/axis.tsx index 09f4f5ba89e..6f4e28af24f 100644 --- a/packages/blade/src/components/Charts/BaseChartComponents/axis.tsx +++ b/packages/blade/src/components/Charts/BaseChartComponents/axis.tsx @@ -119,8 +119,8 @@ export const ReferenceLine: React.FC = ({ return ( Date: Thu, 28 Aug 2025 00:38:15 +0530 Subject: [PATCH 012/105] chore: update color tokens --- .../src/components/Charts/BaseChartComponents/axis.tsx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/blade/src/components/Charts/BaseChartComponents/axis.tsx b/packages/blade/src/components/Charts/BaseChartComponents/axis.tsx index 6f4e28af24f..1f46e601ce5 100644 --- a/packages/blade/src/components/Charts/BaseChartComponents/axis.tsx +++ b/packages/blade/src/components/Charts/BaseChartComponents/axis.tsx @@ -62,13 +62,12 @@ export const XAxis: React.FC = (props) => { return ( ); @@ -79,13 +78,14 @@ export const YAxis: React.FC = (props) => { return ( ); @@ -96,7 +96,6 @@ export const CartesianGrid: React.FC = (props) => { return ( Date: Thu, 28 Aug 2025 17:44:18 +0530 Subject: [PATCH 013/105] chore: add lable in chart --- .../Charts/BaseChartComponents/axis.tsx | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/packages/blade/src/components/Charts/BaseChartComponents/axis.tsx b/packages/blade/src/components/Charts/BaseChartComponents/axis.tsx index 1f46e601ce5..b83b10c1e44 100644 --- a/packages/blade/src/components/Charts/BaseChartComponents/axis.tsx +++ b/packages/blade/src/components/Charts/BaseChartComponents/axis.tsx @@ -68,6 +68,22 @@ export const XAxis: React.FC = (props) => { fontFamily: theme.typography.fonts.family.text, }} stroke="#c5d0db" + label={{ + //TODO: remove this hardcoding Value + value: 'Months', + position: 'insideBottom', + //TODO: need to change this offset. + offset: -10, + style: { + textAnchor: 'middle', + fill: theme.colors.surface.text.gray.subtle, + fontSize: theme.typography.fonts.size[75], + fontWeight: theme.typography.fonts.weight.medium, + fontFamily: theme.typography.fonts.family.text, + letterSpacing: theme.typography.letterSpacings[100], + lineHeight: theme.typography.lineHeights[500], + }, + }} {...props} /> ); @@ -86,6 +102,24 @@ export const YAxis: React.FC = (props) => { fontFamily: theme.typography.fonts.family.text, }} stroke="#c5d0db" + label={{ + //TODO: remove this hardcoding Value + value: 'Active Users', + position: 'insideLeft', + //TODO: need to change this offset. + offset: -10, + style: { + textAnchor: 'middle', + fill: theme.colors.surface.text.gray.subtle, + fontSize: theme.typography.fonts.size[75], + fontWeight: theme.typography.fonts.weight.medium, + fontFamily: theme.typography.fonts.family.text, + letterSpacing: theme.typography.letterSpacings[100], + lineHeight: theme.typography.lineHeights[500], + }, + angle: -90, + fill: theme.colors.surface.text.gray.subtle, + }} {...props} /> ); From 9b45b94cd4e4fae293639fe851be522b59ee1114 Mon Sep 17 00:00:00 2001 From: tewarig Date: Thu, 28 Aug 2025 17:52:42 +0530 Subject: [PATCH 014/105] fix: spacing --- packages/blade/src/components/Charts/LineCharts/lineCharts.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx b/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx index 33277f43d6c..48181fb04d7 100644 --- a/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx +++ b/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx @@ -226,6 +226,7 @@ export const Legend: React.FC = (props) => { fontFamily: theme.typography.fonts.family.text, fontSize: theme.typography.fonts.size[100], color: theme.colors.surface.text.gray.normal, + paddingTop: '16px', }} content={} {...props} From 5f2530e05f9ae1552b23099e410dff2575b88770 Mon Sep 17 00:00:00 2001 From: tewarig Date: Thu, 28 Aug 2025 18:41:49 +0530 Subject: [PATCH 015/105] fix: add custom label --- .../Charts/BaseChartComponents/axis.tsx | 48 ++++++++++++++++--- 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/packages/blade/src/components/Charts/BaseChartComponents/axis.tsx b/packages/blade/src/components/Charts/BaseChartComponents/axis.tsx index b83b10c1e44..3da4edf04b5 100644 --- a/packages/blade/src/components/Charts/BaseChartComponents/axis.tsx +++ b/packages/blade/src/components/Charts/BaseChartComponents/axis.tsx @@ -137,6 +137,38 @@ export const CartesianGrid: React.FC = (props) => { ); }; +const CustomReferenceLabel = ({ viewBox, value }) => { + const { x, y, width } = viewBox; + const { theme } = useTheme(); + + return ( + + + + Avg: {value} + + + ); +}; + export const ReferenceLine: React.FC = ({ color, label, @@ -145,6 +177,7 @@ export const ReferenceLine: React.FC = ({ ...props }) => { const { theme } = useTheme(); + const [isHover, setIsHover] = React.useState(false); const resolvedColor = color ? resolveColorToken(color, theme) : theme.colors.surface.text.gray.normal; @@ -154,14 +187,15 @@ export const ReferenceLine: React.FC = ({ stroke={resolvedColor} strokeWidth={2} strokeDasharray="4 4" - label={{ - position: labelPosition, - offset: labelOffset, - fill: resolvedColor, - fontSize: theme.typography.fonts.size[75], - fontFamily: theme.typography.fonts.family.text, - value: label, + onMouseEnter={() => { + console.log('onMouseEnter'); + setIsHover(true); + }} + onMouseLeave={() => { + console.log('onMouseLeave'); + setIsHover(false); }} + label={} {...props} /> ); From 1aa02c5bc906fbeee0a55f9a4f8939de7195b482 Mon Sep 17 00:00:00 2001 From: tewarig Date: Thu, 28 Aug 2025 21:52:41 +0530 Subject: [PATCH 016/105] feat: add colors --- packages/blade/src/tokens/theme/bladeTheme.ts | 350 ++++++++++++++++++ packages/blade/src/tokens/theme/theme.ts | 28 ++ 2 files changed, 378 insertions(+) diff --git a/packages/blade/src/tokens/theme/bladeTheme.ts b/packages/blade/src/tokens/theme/bladeTheme.ts index 2bad55e8349..e21ada2cc4a 100644 --- a/packages/blade/src/tokens/theme/bladeTheme.ts +++ b/packages/blade/src/tokens/theme/bladeTheme.ts @@ -452,6 +452,181 @@ const colors: ColorsWithModes = { intense: globalColors.neutral.blueGrayLight[900], }, }, + chart: { + background: { + categorical: { + azure: { + subtle: globalColors.chromatic.azure[50], + moderate: globalColors.chromatic.azure[200], + intense: globalColors.chromatic.azure[500], + faint: globalColors.chromatic.azure[600], + strong: globalColors.chromatic.azure[800], + }, + emerald: { + subtle: globalColors.chromatic.emerald[50], + moderate: globalColors.chromatic.emerald[200], + intense: globalColors.chromatic.emerald[500], + faint: globalColors.chromatic.emerald[600], + strong: globalColors.chromatic.emerald[800], + }, + crimson: { + subtle: globalColors.chromatic.crimson[50], + moderate: globalColors.chromatic.crimson[200], + intense: globalColors.chromatic.crimson[500], + faint: globalColors.chromatic.crimson[600], + strong: globalColors.chromatic.crimson[800], + }, + cider: { + subtle: globalColors.chromatic.cider[50], + moderate: globalColors.chromatic.cider[200], + intense: globalColors.chromatic.cider[500], + faint: globalColors.chromatic.cider[600], + strong: globalColors.chromatic.cider[800], + }, + sapphire: { + subtle: globalColors.chromatic.sapphire[50], + moderate: globalColors.chromatic.sapphire[200], + intense: globalColors.chromatic.sapphire[500], + faint: globalColors.chromatic.sapphire[600], + strong: globalColors.chromatic.sapphire[800], + }, + orchid: { + subtle: globalColors.chromatic.orchid[50], + moderate: globalColors.chromatic.orchid[200], + intense: globalColors.chromatic.orchid[500], + faint: globalColors.chromatic.orchid[600], + strong: globalColors.chromatic.orchid[800], + }, + magenta: { + subtle: globalColors.chromatic.magenta[50], + moderate: globalColors.chromatic.magenta[200], + intense: globalColors.chromatic.magenta[500], + faint: globalColors.chromatic.magenta[600], + strong: globalColors.chromatic.magenta[800], + }, + topaz: { + subtle: globalColors.chromatic.topaz[50], + moderate: globalColors.chromatic.topaz[200], + intense: globalColors.chromatic.topaz[500], + faint: globalColors.chromatic.topaz[600], + strong: globalColors.chromatic.topaz[800], + }, + gray: { + subtle: globalColors.neutral.blueGrayDark.a50, + moderate: globalColors.neutral.blueGrayDark[100], + intense: globalColors.neutral.blueGrayDark[300], + faint: globalColors.neutral.blueGrayDark[500], + strong: globalColors.neutral.blueGrayDark[700], + }, + }, + sequential: { + azure: { + 50: globalColors.chromatic.azure[50], + 100: globalColors.chromatic.azure[100], + 200: globalColors.chromatic.azure[200], + 300: globalColors.chromatic.azure[300], + 400: globalColors.chromatic.azure[400], + 500: globalColors.chromatic.azure[500], + 600: globalColors.chromatic.azure[600], + 700: globalColors.chromatic.azure[700], + 800: globalColors.chromatic.azure[800], + 900: globalColors.chromatic.azure[900], + 1000: globalColors.chromatic.azure[1000], + }, + emerald: { + 50: globalColors.chromatic.emerald[50], + 100: globalColors.chromatic.emerald[100], + 200: globalColors.chromatic.emerald[200], + 300: globalColors.chromatic.emerald[300], + 400: globalColors.chromatic.emerald[400], + 500: globalColors.chromatic.emerald[500], + 600: globalColors.chromatic.emerald[600], + 700: globalColors.chromatic.emerald[700], + 800: globalColors.chromatic.emerald[800], + 900: globalColors.chromatic.emerald[900], + 1000: globalColors.chromatic.emerald[1000], + }, + crimson: { + 50: globalColors.chromatic.crimson[50], + 100: globalColors.chromatic.crimson[100], + 200: globalColors.chromatic.crimson[200], + 300: globalColors.chromatic.crimson[300], + 400: globalColors.chromatic.crimson[400], + 500: globalColors.chromatic.crimson[500], + 600: globalColors.chromatic.crimson[600], + 700: globalColors.chromatic.crimson[700], + 800: globalColors.chromatic.crimson[800], + 900: globalColors.chromatic.crimson[900], + 1000: globalColors.chromatic.crimson[1000], + }, + cider: { + 50: globalColors.chromatic.cider[50], + 100: globalColors.chromatic.cider[100], + 200: globalColors.chromatic.cider[200], + 300: globalColors.chromatic.cider[300], + 400: globalColors.chromatic.cider[400], + 500: globalColors.chromatic.cider[500], + 600: globalColors.chromatic.cider[600], + 700: globalColors.chromatic.cider[700], + 800: globalColors.chromatic.cider[800], + 900: globalColors.chromatic.cider[900], + 1000: globalColors.chromatic.cider[1000], + }, + sapphire: { + 50: globalColors.chromatic.sapphire[50], + 100: globalColors.chromatic.sapphire[100], + 200: globalColors.chromatic.sapphire[200], + 300: globalColors.chromatic.sapphire[300], + 400: globalColors.chromatic.sapphire[400], + 500: globalColors.chromatic.sapphire[500], + 600: globalColors.chromatic.sapphire[600], + 700: globalColors.chromatic.sapphire[700], + 800: globalColors.chromatic.sapphire[800], + 900: globalColors.chromatic.sapphire[900], + 1000: globalColors.chromatic.sapphire[1000], + }, + orchid: { + 50: globalColors.chromatic.orchid[50], + 100: globalColors.chromatic.orchid[100], + 200: globalColors.chromatic.orchid[200], + 300: globalColors.chromatic.orchid[300], + 400: globalColors.chromatic.orchid[400], + 500: globalColors.chromatic.orchid[500], + 600: globalColors.chromatic.orchid[600], + 700: globalColors.chromatic.orchid[700], + 800: globalColors.chromatic.orchid[800], + 900: globalColors.chromatic.orchid[900], + 1000: globalColors.chromatic.orchid[1000], + }, + magenta: { + 50: globalColors.chromatic.magenta[50], + 100: globalColors.chromatic.magenta[100], + 200: globalColors.chromatic.magenta[200], + 300: globalColors.chromatic.magenta[300], + 400: globalColors.chromatic.magenta[400], + 500: globalColors.chromatic.magenta[500], + 600: globalColors.chromatic.magenta[600], + 700: globalColors.chromatic.magenta[700], + 800: globalColors.chromatic.magenta[800], + 900: globalColors.chromatic.magenta[900], + 1000: globalColors.chromatic.magenta[1000], + }, + topaz: { + 50: globalColors.chromatic.topaz[50], + 100: globalColors.chromatic.topaz[100], + 200: globalColors.chromatic.topaz[200], + 300: globalColors.chromatic.topaz[300], + 400: globalColors.chromatic.topaz[400], + 500: globalColors.chromatic.topaz[500], + 600: globalColors.chromatic.topaz[600], + 700: globalColors.chromatic.topaz[700], + 800: globalColors.chromatic.topaz[800], + 900: globalColors.chromatic.topaz[900], + 1000: globalColors.chromatic.topaz[1000], + }, + }, + }, + }, transparent: `hsla(0, 0%, 100%, ${opacity[0]})`, }, onDark: { @@ -895,6 +1070,181 @@ const colors: ColorsWithModes = { intense: globalColors.neutral.blueGrayDark.a100, }, }, + chart: { + background: { + categorical: { + azure: { + faint: globalColors.chromatic.azure[900], + subtle: globalColors.chromatic.azure[700], + moderate: globalColors.chromatic.azure[500], + intense: globalColors.chromatic.azure[300], + strong: globalColors.chromatic.azure[200], + }, + emerald: { + faint: globalColors.chromatic.emerald[1000], + subtle: globalColors.chromatic.emerald[800], + moderate: globalColors.chromatic.emerald[700], + intense: globalColors.chromatic.emerald[300], + strong: globalColors.chromatic.emerald[200], + }, + crimson: { + faint: globalColors.chromatic.crimson[1000], + subtle: globalColors.chromatic.crimson[800], + moderate: globalColors.chromatic.crimson[700], + intense: globalColors.chromatic.crimson[300], + strong: globalColors.chromatic.crimson[200], + }, + cider: { + faint: globalColors.chromatic.cider[1000], + subtle: globalColors.chromatic.cider[800], + moderate: globalColors.chromatic.cider[700], + intense: globalColors.chromatic.cider[300], + strong: globalColors.chromatic.cider[200], + }, + sapphire: { + faint: globalColors.chromatic.sapphire[1000], + subtle: globalColors.chromatic.sapphire[800], + moderate: globalColors.chromatic.sapphire[700], + intense: globalColors.chromatic.sapphire[300], + strong: globalColors.chromatic.sapphire[200], + }, + orchid: { + faint: globalColors.chromatic.orchid[1000], + subtle: globalColors.chromatic.orchid[800], + moderate: globalColors.chromatic.orchid[700], + intense: globalColors.chromatic.orchid[300], + strong: globalColors.chromatic.orchid[200], + }, + magenta: { + faint: globalColors.chromatic.magenta[1000], + subtle: globalColors.chromatic.magenta[800], + moderate: globalColors.chromatic.magenta[700], + intense: globalColors.chromatic.magenta[300], + strong: globalColors.chromatic.magenta[200], + }, + topaz: { + faint: globalColors.chromatic.topaz[1000], + subtle: globalColors.chromatic.topaz[800], + moderate: globalColors.chromatic.topaz[700], + intense: globalColors.chromatic.topaz[300], + strong: globalColors.chromatic.topaz[200], + }, + gray: { + faint: globalColors.neutral.blueGrayDark[1300], + subtle: globalColors.neutral.blueGrayDark[1100], + moderate: globalColors.neutral.blueGrayDark[900], + intense: globalColors.neutral.blueGrayDark[700], + strong: globalColors.neutral.blueGrayDark[500], + }, + }, + sequential: { + azure: { + 50: globalColors.chromatic.azure[1000], + 100: globalColors.chromatic.azure[900], + 200: globalColors.chromatic.azure[800], + 300: globalColors.chromatic.azure[700], + 400: globalColors.chromatic.azure[600], + 500: globalColors.chromatic.azure[500], + 600: globalColors.chromatic.azure[400], + 700: globalColors.chromatic.azure[300], + 800: globalColors.chromatic.azure[200], + 900: globalColors.chromatic.azure[100], + 1000: globalColors.chromatic.azure[50], + }, + emerald: { + 50: globalColors.chromatic.emerald[1000], + 100: globalColors.chromatic.emerald[900], + 200: globalColors.chromatic.emerald[800], + 300: globalColors.chromatic.emerald[700], + 400: globalColors.chromatic.emerald[600], + 500: globalColors.chromatic.emerald[500], + 600: globalColors.chromatic.emerald[400], + 700: globalColors.chromatic.emerald[300], + 800: globalColors.chromatic.emerald[200], + 900: globalColors.chromatic.emerald[100], + 1000: globalColors.chromatic.emerald[50], + }, + crimson: { + 50: globalColors.chromatic.crimson[1000], + 100: globalColors.chromatic.crimson[900], + 200: globalColors.chromatic.crimson[800], + 300: globalColors.chromatic.crimson[700], + 400: globalColors.chromatic.crimson[600], + 500: globalColors.chromatic.crimson[500], + 600: globalColors.chromatic.crimson[400], + 700: globalColors.chromatic.crimson[300], + 800: globalColors.chromatic.crimson[200], + 900: globalColors.chromatic.crimson[100], + 1000: globalColors.chromatic.crimson[50], + }, + cider: { + 50: globalColors.chromatic.cider[1000], + 100: globalColors.chromatic.cider[900], + 200: globalColors.chromatic.cider[800], + 300: globalColors.chromatic.cider[700], + 400: globalColors.chromatic.cider[600], + 500: globalColors.chromatic.cider[500], + 600: globalColors.chromatic.cider[400], + 700: globalColors.chromatic.cider[300], + 800: globalColors.chromatic.cider[200], + 900: globalColors.chromatic.cider[100], + 1000: globalColors.chromatic.cider[50], + }, + sapphire: { + 50: globalColors.chromatic.sapphire[1000], + 100: globalColors.chromatic.sapphire[900], + 200: globalColors.chromatic.sapphire[800], + 300: globalColors.chromatic.sapphire[700], + 400: globalColors.chromatic.sapphire[600], + 500: globalColors.chromatic.sapphire[500], + 600: globalColors.chromatic.sapphire[400], + 700: globalColors.chromatic.sapphire[300], + 800: globalColors.chromatic.sapphire[200], + 900: globalColors.chromatic.sapphire[100], + 1000: globalColors.chromatic.sapphire[50], + }, + orchid: { + 50: globalColors.chromatic.orchid[1000], + 100: globalColors.chromatic.orchid[900], + 200: globalColors.chromatic.orchid[800], + 300: globalColors.chromatic.orchid[700], + 400: globalColors.chromatic.orchid[600], + 500: globalColors.chromatic.orchid[500], + 600: globalColors.chromatic.orchid[400], + 700: globalColors.chromatic.orchid[300], + 800: globalColors.chromatic.orchid[200], + 900: globalColors.chromatic.orchid[100], + 1000: globalColors.chromatic.orchid[50], + }, + magenta: { + 50: globalColors.chromatic.magenta[1000], + 100: globalColors.chromatic.magenta[900], + 200: globalColors.chromatic.magenta[800], + 300: globalColors.chromatic.magenta[700], + 400: globalColors.chromatic.magenta[600], + 500: globalColors.chromatic.magenta[500], + 600: globalColors.chromatic.magenta[400], + 700: globalColors.chromatic.magenta[300], + 800: globalColors.chromatic.magenta[200], + 900: globalColors.chromatic.magenta[100], + 1000: globalColors.chromatic.magenta[50], + }, + topaz: { + 50: globalColors.chromatic.topaz[1000], + 100: globalColors.chromatic.topaz[900], + 200: globalColors.chromatic.topaz[800], + 300: globalColors.chromatic.topaz[700], + 400: globalColors.chromatic.topaz[600], + 500: globalColors.chromatic.topaz[500], + 600: globalColors.chromatic.topaz[400], + 700: globalColors.chromatic.topaz[300], + 800: globalColors.chromatic.topaz[200], + 900: globalColors.chromatic.topaz[100], + 1000: globalColors.chromatic.topaz[50], + }, + }, + }, + }, transparent: `hsla(217, 27%, 15%, ${opacity[0]})`, }, }; diff --git a/packages/blade/src/tokens/theme/theme.ts b/packages/blade/src/tokens/theme/theme.ts index e9331fdf3b4..2debee62bcd 100644 --- a/packages/blade/src/tokens/theme/theme.ts +++ b/packages/blade/src/tokens/theme/theme.ts @@ -7,6 +7,7 @@ import type { TypographyWithPlatforms, ElevationWithColorModes, } from '~tokens/global'; +import type { ColorChromaticScale } from '~tokens/global/colors'; export type ColorSchemeNames = 'dark' | 'light'; export type ColorSchemeNamesInput = ColorSchemeNames | 'system'; @@ -24,6 +25,27 @@ export type Emphasis = { disabled: string; }; +export type ChartCategoricalEmphasis = Pick & { + faint: string; + strong: string; +}; + +export type ChartSequentialEmphasis = Omit< + ColorChromaticScale, + 'a50' | 'a150' | 'a100' | 'a200' | 'a400' +>; + +export type ChartColorCategories = + | 'azure' + | 'emerald' + | 'crimson' + | 'cider' + | 'sapphire' + | 'orchid' + | 'magenta' + | 'topaz' + | 'gray'; + type SubtleOrIntenseEmphasis = Pick; // Exporting this for usage in other components export type SubtleOrIntense = keyof SubtleOrIntenseEmphasis; @@ -91,6 +113,12 @@ export type Colors = { border: SubtleOrIntenseEmphasis; }; transparent: string; + chart: { + background: { + categorical: Record; + sequential: Record; + }; + }; }; export type ColorsWithModes = Record; From 7efc234dffde7b1c455a370d61032fa8962f5af0 Mon Sep 17 00:00:00 2001 From: tewarig Date: Thu, 28 Aug 2025 22:43:32 +0530 Subject: [PATCH 017/105] feat: support new colors in line charts & colors update --- .../Charts/BaseChartComponents/axis.tsx | 2 +- .../Charts/LineCharts/LineCharts.stories.tsx | 10 +++++-- .../Charts/LineCharts/lineCharts.tsx | 29 ++++--------------- packages/blade/src/tokens/theme/bladeTheme.ts | 28 +++++++++--------- packages/blade/src/tokens/theme/theme.ts | 2 +- 5 files changed, 30 insertions(+), 41 deletions(-) diff --git a/packages/blade/src/components/Charts/BaseChartComponents/axis.tsx b/packages/blade/src/components/Charts/BaseChartComponents/axis.tsx index 3da4edf04b5..93e5fd3cd09 100644 --- a/packages/blade/src/components/Charts/BaseChartComponents/axis.tsx +++ b/packages/blade/src/components/Charts/BaseChartComponents/axis.tsx @@ -163,7 +163,7 @@ const CustomReferenceLabel = ({ viewBox, value }) => { fontWeight={theme.typography.fonts.weight.medium} letterSpacing={theme.typography.letterSpacings[100]} > - Avg: {value} + {value} ); diff --git a/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx b/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx index 898232e37c9..230fa9ddc0f 100644 --- a/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx +++ b/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx @@ -105,9 +105,15 @@ export const SimpleLineChart: StoryFn = () => { dataKey="teamA" name="Team A" strokeStyle="solid" - color="interactive.background.primary.default" + color="chart.background.categorical.azure.moderate" + /> + - +
diff --git a/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx b/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx index 48181fb04d7..010116b18b4 100644 --- a/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx +++ b/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx @@ -21,27 +21,16 @@ import BaseBox from '~components/Box/BaseBox'; import { castWebType } from '~utils'; import { Text } from '~components/Typography'; import { Box } from '~components/Box'; +import type { ChartColorCategories, ChartCategoricalEmphasis } from '~tokens/theme/theme'; -// BladeColorToken type for charts -export type BladeColorToken = - | 'surface.text.gray.normal' - | 'surface.text.gray.muted' - | 'interactive.background.primary.default' - | 'feedback.text.positive.subtle' - | 'feedback.text.negative.subtle' - | 'feedback.text.notice.subtle' - | 'feedback.text.information.subtle' - | string; +// BladeColorToken type for charts - only allows categorical chart colors for line charts +export type BladeColorToken = `chart.background.categorical.${ChartColorCategories}.${keyof ChartCategoricalEmphasis}`; // Chart-specific interfaces based on user specifications export interface LineProps { type?: 'step' | 'stepAfter' | 'stepBefore' | 'linear' | 'monotone'; - dot?: - | boolean - | { r?: number; fill?: string; stroke?: string; strokeWidth?: number; [key: string]: any }; - activeDot?: - | boolean - | { r?: number; fill?: string; stroke?: string; strokeWidth?: number; [key: string]: any }; + dot?: React.ReactNode; + activeDot?: React.ReactNode; connectNulls?: boolean; legendType?: 'none' | 'line' | 'square' | 'diamond' | 'circle' | 'cross' | 'triangle' | 'wye'; dataKey: string; @@ -65,13 +54,7 @@ const getChartColors = (theme: Theme): Record => ({ // Helper function to resolve color tokens const resolveColorToken = (color: BladeColorToken | undefined, theme: Theme): string => { - if (!color) return getChartColors(theme).primary; - - if ( - color.startsWith('surface.') || - color.startsWith('feedback.') || - color.startsWith('interactive.') - ) { + if (color.startsWith('chart.background.categorical.')) { const parts = color.split('.'); let value: Record = theme.colors; for (const part of parts) { diff --git a/packages/blade/src/tokens/theme/bladeTheme.ts b/packages/blade/src/tokens/theme/bladeTheme.ts index e21ada2cc4a..94652c6b396 100644 --- a/packages/blade/src/tokens/theme/bladeTheme.ts +++ b/packages/blade/src/tokens/theme/bladeTheme.ts @@ -456,37 +456,37 @@ const colors: ColorsWithModes = { background: { categorical: { azure: { - subtle: globalColors.chromatic.azure[50], - moderate: globalColors.chromatic.azure[200], - intense: globalColors.chromatic.azure[500], - faint: globalColors.chromatic.azure[600], + faint: globalColors.chromatic.azure[50], + subtle: globalColors.chromatic.azure[200], + moderate: globalColors.chromatic.azure[500], + intense: globalColors.chromatic.azure[600], strong: globalColors.chromatic.azure[800], }, emerald: { - subtle: globalColors.chromatic.emerald[50], - moderate: globalColors.chromatic.emerald[200], - intense: globalColors.chromatic.emerald[500], - faint: globalColors.chromatic.emerald[600], + faint: globalColors.chromatic.emerald[50], + subtle: globalColors.chromatic.emerald[200], + moderate: globalColors.chromatic.emerald[500], + intense: globalColors.chromatic.emerald[600], strong: globalColors.chromatic.emerald[800], }, crimson: { - subtle: globalColors.chromatic.crimson[50], - moderate: globalColors.chromatic.crimson[200], - intense: globalColors.chromatic.crimson[500], - faint: globalColors.chromatic.crimson[600], + faint: globalColors.chromatic.crimson[50], + subtle: globalColors.chromatic.crimson[200], + moderate: globalColors.chromatic.crimson[600], + intense: globalColors.chromatic.crimson[600], strong: globalColors.chromatic.crimson[800], }, cider: { subtle: globalColors.chromatic.cider[50], moderate: globalColors.chromatic.cider[200], - intense: globalColors.chromatic.cider[500], + intense: globalColors.chromatic.cider[600], faint: globalColors.chromatic.cider[600], strong: globalColors.chromatic.cider[800], }, sapphire: { subtle: globalColors.chromatic.sapphire[50], moderate: globalColors.chromatic.sapphire[200], - intense: globalColors.chromatic.sapphire[500], + intense: globalColors.chromatic.sapphire[600], faint: globalColors.chromatic.sapphire[600], strong: globalColors.chromatic.sapphire[800], }, diff --git a/packages/blade/src/tokens/theme/theme.ts b/packages/blade/src/tokens/theme/theme.ts index 2debee62bcd..98057d0e9bd 100644 --- a/packages/blade/src/tokens/theme/theme.ts +++ b/packages/blade/src/tokens/theme/theme.ts @@ -116,7 +116,7 @@ export type Colors = { chart: { background: { categorical: Record; - sequential: Record; + sequential: Record, ChartSequentialEmphasis>; }; }; }; From 263731a4a3ae5120ff1f5b054af6f2abecfcb4b9 Mon Sep 17 00:00:00 2001 From: tewarig Date: Thu, 28 Aug 2025 23:12:07 +0530 Subject: [PATCH 018/105] chore: update color logic & exmaple color values --- .../Charts/LineCharts/LineCharts.stories.tsx | 14 +++--- .../Charts/LineCharts/lineCharts.tsx | 47 +++---------------- 2 files changed, 14 insertions(+), 47 deletions(-) diff --git a/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx b/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx index 230fa9ddc0f..b10363315c2 100644 --- a/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx +++ b/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx @@ -129,7 +129,7 @@ export const TinyLineChart: StoryFn = () => { @@ -154,7 +154,7 @@ export const LineChartWithCustomDots: StoryFn = () => { dataKey="teamA" name="Team A (No Dots)" strokeStyle="solid" - color="interactive.background.primary.default" + color="chart.background.categorical.azure.moderate" dot={false} activeDot={true} /> @@ -162,7 +162,7 @@ export const LineChartWithCustomDots: StoryFn = () => { dataKey="teamB" name="Team B (Custom Dots)" strokeStyle="solid" - color="feedback.text.positive.subtle" + color="chart.background.categorical.crimson.moderate" dot={{ r: 6, fill: '#22c55e', stroke: '#16a34a', strokeWidth: 2 }} activeDot={{ r: 8, fill: '#22c55e', stroke: '#ffffff', strokeWidth: 3 }} /> @@ -187,7 +187,7 @@ export const DotConfigurationShowcase: StoryFn = () => { @@ -195,7 +195,7 @@ export const DotConfigurationShowcase: StoryFn = () => { @@ -220,7 +220,7 @@ export const ForecastLineChart: StoryFn = () => { dataKey="historical" name="Historical Data" connectNulls={true} - color="interactive.background.primary.default" + color="chart.background.categorical.topaz.moderate" /> = () => { strokeStyle="dashed" connectNulls={true} legendType="none" - color="surface.text.gray.muted" + color="chart.background.categorical.magenta.moderate" /> diff --git a/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx b/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx index 010116b18b4..1148209c67d 100644 --- a/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx +++ b/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx @@ -4,9 +4,6 @@ import styled from 'styled-components'; import { LineChart as RechartsLineChart, Line as RechartsLine, - XAxis as RechartsXAxis, - YAxis as RechartsYAxis, - CartesianGrid as RechartsCartesianGrid, Tooltip as RechartsTooltip, Legend as RechartsLegend, ResponsiveContainer as RechartsResponsiveContainer, @@ -22,6 +19,7 @@ import { castWebType } from '~utils'; import { Text } from '~components/Typography'; import { Box } from '~components/Box'; import type { ChartColorCategories, ChartCategoricalEmphasis } from '~tokens/theme/theme'; +import getIn from '~utils/lodashButBetter/get'; // BladeColorToken type for charts - only allows categorical chart colors for line charts export type BladeColorToken = `chart.background.categorical.${ChartColorCategories}.${keyof ChartCategoricalEmphasis}`; @@ -35,47 +33,16 @@ export interface LineProps { legendType?: 'none' | 'line' | 'square' | 'diamond' | 'circle' | 'cross' | 'triangle' | 'wye'; dataKey: string; name?: string; - color?: BladeColorToken; + color: BladeColorToken; strokeStyle?: 'dotted' | 'dashed' | 'solid'; } -// Predefined chart colors using Blade tokens -const getChartColors = (theme: Theme): Record => ({ - primary: theme.colors.interactive.background.primary.default, - secondary: theme.colors.surface.text.gray.normal, - success: theme.colors.feedback.text.positive.subtle, - warning: theme.colors.feedback.text.notice.subtle, - error: theme.colors.feedback.text.negative.subtle, - info: theme.colors.feedback.text.information.subtle, - neutral: theme.colors.surface.text.gray.muted, - grid: theme.colors.surface.border.gray.muted, - background: theme.colors.surface.background.gray.intense, -}); - -// Helper function to resolve color tokens -const resolveColorToken = (color: BladeColorToken | undefined, theme: Theme): string => { - if (color.startsWith('chart.background.categorical.')) { - const parts = color.split('.'); - let value: Record = theme.colors; - for (const part of parts) { - value = value[part]; - } - //@ts-expect-error - return value || getChartColors(theme).primary; - } - - return color; -}; - // TypeScript prop types export type LineChartProps = Omit, 'margin'> & StyledPropsBlade & { children?: React.ReactNode; }; -export type XAxisProps = ComponentProps; -export type YAxisProps = ComponentProps; -export type CartesianGridProps = ComponentProps; export type TooltipProps = ComponentProps; export type LegendProps = ComponentProps; export type ResponsiveContainerProps = ComponentProps; @@ -111,19 +78,19 @@ export const Line: React.FC = ({ color, strokeStyle = 'solid', type = 'monotone', - dot = true, - activeDot = true, + dot, + activeDot, ...props }) => { const { theme } = useTheme(); - const resolvedColor = resolveColorToken(color, theme); + const colorToken = getIn(theme.colors, color); const strokeDasharray = strokeStyle === 'dashed' ? '5 5' : strokeStyle === 'dotted' ? '2 2' : undefined; return ( = (props) => { ); }; -const CustomSquareLegend = (props: any) => { +const CustomSquareLegend = (props: unknown) => { const { payload } = props; return ( From ab4bd23e2c348ab740d697e3e98ae95bfa5cbc06 Mon Sep 17 00:00:00 2001 From: tewarig Date: Thu, 28 Aug 2025 23:12:43 +0530 Subject: [PATCH 019/105] chore: update blade color theme --- packages/blade/src/tokens/theme/bladeTheme.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/blade/src/tokens/theme/bladeTheme.ts b/packages/blade/src/tokens/theme/bladeTheme.ts index 94652c6b396..38b87045c7b 100644 --- a/packages/blade/src/tokens/theme/bladeTheme.ts +++ b/packages/blade/src/tokens/theme/bladeTheme.ts @@ -500,7 +500,7 @@ const colors: ColorsWithModes = { magenta: { subtle: globalColors.chromatic.magenta[50], moderate: globalColors.chromatic.magenta[200], - intense: globalColors.chromatic.magenta[500], + intense: globalColors.chromatic.magenta[600], faint: globalColors.chromatic.magenta[600], strong: globalColors.chromatic.magenta[800], }, @@ -1097,21 +1097,21 @@ const colors: ColorsWithModes = { cider: { faint: globalColors.chromatic.cider[1000], subtle: globalColors.chromatic.cider[800], - moderate: globalColors.chromatic.cider[700], + moderate: globalColors.chromatic.cider[600], intense: globalColors.chromatic.cider[300], strong: globalColors.chromatic.cider[200], }, sapphire: { faint: globalColors.chromatic.sapphire[1000], subtle: globalColors.chromatic.sapphire[800], - moderate: globalColors.chromatic.sapphire[700], + moderate: globalColors.chromatic.sapphire[600], intense: globalColors.chromatic.sapphire[300], strong: globalColors.chromatic.sapphire[200], }, orchid: { faint: globalColors.chromatic.orchid[1000], subtle: globalColors.chromatic.orchid[800], - moderate: globalColors.chromatic.orchid[700], + moderate: globalColors.chromatic.orchid[600], intense: globalColors.chromatic.orchid[300], strong: globalColors.chromatic.orchid[200], }, @@ -1125,7 +1125,7 @@ const colors: ColorsWithModes = { topaz: { faint: globalColors.chromatic.topaz[1000], subtle: globalColors.chromatic.topaz[800], - moderate: globalColors.chromatic.topaz[700], + moderate: globalColors.chromatic.topaz[600], intense: globalColors.chromatic.topaz[300], strong: globalColors.chromatic.topaz[200], }, From b60d9a9b656d28a542c06b3766da9f2ac780f8b6 Mon Sep 17 00:00:00 2001 From: tewarig Date: Thu, 28 Aug 2025 23:24:55 +0530 Subject: [PATCH 020/105] refactor: reference line props --- .../Charts/BaseChartComponents/axis.tsx | 102 ------------------ .../Charts/BaseChartComponents/index.ts | 4 +- .../Charts/LineCharts/lineCharts.tsx | 64 +++++++++++ 3 files changed, 66 insertions(+), 104 deletions(-) diff --git a/packages/blade/src/components/Charts/BaseChartComponents/axis.tsx b/packages/blade/src/components/Charts/BaseChartComponents/axis.tsx index 93e5fd3cd09..b84772dc4b0 100644 --- a/packages/blade/src/components/Charts/BaseChartComponents/axis.tsx +++ b/packages/blade/src/components/Charts/BaseChartComponents/axis.tsx @@ -1,34 +1,16 @@ import React from 'react'; import type { ComponentProps } from 'react'; import { - LineChart as RechartsLineChart, - Line as RechartsLine, XAxis as RechartsXAxis, YAxis as RechartsYAxis, CartesianGrid as RechartsCartesianGrid, - Tooltip as RechartsTooltip, - Legend as RechartsLegend, - ResponsiveContainer as RechartsResponsiveContainer, - ReferenceLine as RechartsReferenceLine, } from 'recharts'; import { useTheme } from '~components/BladeProvider'; -import type { Theme } from '~components/BladeProvider'; -// BladeColorToken type for charts -export type BladeColorToken = - | 'surface.text.gray.normal' - | 'surface.text.gray.muted' - | 'interactive.background.primary.default' - | 'feedback.text.positive.subtle' - | 'feedback.text.negative.subtle' - | 'feedback.text.notice.subtle' - | 'feedback.text.information.subtle' - | string; export interface ReferenceLineProps { y?: number; x?: number; label?: string; - color?: BladeColorToken; labelPosition?: 'left' | 'right' | 'top' | 'bottom'; labelOffset?: number; } @@ -37,26 +19,6 @@ export type XAxisProps = ComponentProps; export type YAxisProps = ComponentProps; export type CartesianGridProps = ComponentProps; -// Helper function to resolve color tokens -const resolveColorToken = (color: BladeColorToken | undefined, theme: Theme): string => { - if (!color) return theme.colors.interactive.background.primary.default; - - if ( - color.startsWith('surface.') || - color.startsWith('feedback.') || - color.startsWith('interactive.') - ) { - const parts = color.split('.'); - let value: Record = theme.colors; - for (const part of parts) { - value = value[part] as Record; - } - return ((value as unknown) as string) || theme.colors.interactive.background.primary.default; - } - - return color; -}; - export const XAxis: React.FC = (props) => { const { theme } = useTheme(); @@ -136,67 +98,3 @@ export const CartesianGrid: React.FC = (props) => { /> ); }; - -const CustomReferenceLabel = ({ viewBox, value }) => { - const { x, y, width } = viewBox; - const { theme } = useTheme(); - - return ( - - - - {value} - - - ); -}; - -export const ReferenceLine: React.FC = ({ - color, - label, - labelPosition = 'right', - labelOffset = 10, - ...props -}) => { - const { theme } = useTheme(); - const [isHover, setIsHover] = React.useState(false); - const resolvedColor = color - ? resolveColorToken(color, theme) - : theme.colors.surface.text.gray.normal; - - return ( - { - console.log('onMouseEnter'); - setIsHover(true); - }} - onMouseLeave={() => { - console.log('onMouseLeave'); - setIsHover(false); - }} - label={} - {...props} - /> - ); -}; diff --git a/packages/blade/src/components/Charts/BaseChartComponents/index.ts b/packages/blade/src/components/Charts/BaseChartComponents/index.ts index c42ee46aece..e3cb13428be 100644 --- a/packages/blade/src/components/Charts/BaseChartComponents/index.ts +++ b/packages/blade/src/components/Charts/BaseChartComponents/index.ts @@ -1,2 +1,2 @@ -export type { XAxisProps, YAxisProps, CartesianGridProps, ReferenceLineProps } from './axis'; -export { XAxis, YAxis, CartesianGrid, ReferenceLine } from './axis'; +export type { XAxisProps, YAxisProps, CartesianGridProps } from './axis'; +export { XAxis, YAxis, CartesianGrid } from './axis'; diff --git a/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx b/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx index 1148209c67d..8d3ba74bc32 100644 --- a/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx +++ b/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx @@ -47,6 +47,15 @@ export type TooltipProps = ComponentProps; export type LegendProps = ComponentProps; export type ResponsiveContainerProps = ComponentProps; +export interface ReferenceLineProps { + y?: number; + x?: number; + label?: string; + color?: BladeColorToken; + labelPosition?: 'left' | 'right' | 'top' | 'bottom'; + labelOffset?: number; +} + // Styled wrapper for LineChart with predefined margins const StyledLineChart = styled(RechartsLineChart)<{ theme: Theme }>` font-family: ${(props) => props.theme.typography.fonts.family.text}; @@ -192,3 +201,58 @@ export const ResponsiveContainer: React.FC = (props) = export const ChartTooltip: React.FC = (props) => { return ; }; + +const CustomReferenceLabel = ({ viewBox, value }) => { + const { x, y, width } = viewBox; + const { theme } = useTheme(); + + return ( + + + + {value} + + + ); +}; + +export const ReferenceLine: React.FC = ({ + color, + label, + labelPosition = 'right', + labelOffset = 10, + ...props +}) => { + const { theme } = useTheme(); + const resolvedColor = color + ? resolveColorToken(color, theme) + : theme.colors.surface.text.gray.normal; + + return ( + } + {...props} + /> + ); +}; From ee3d0627cada6cd4015d9b5861af6e1a66e1e54e Mon Sep 17 00:00:00 2001 From: tewarig Date: Thu, 28 Aug 2025 23:28:22 +0530 Subject: [PATCH 021/105] fix: more color props --- .../components/Charts/LineCharts/lineCharts.tsx | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx b/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx index 8d3ba74bc32..56d059414d4 100644 --- a/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx +++ b/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx @@ -76,7 +76,7 @@ export const LineChart: React.FC = ({ children, ...props }) => { return ( - + {children} @@ -234,21 +234,12 @@ const CustomReferenceLabel = ({ viewBox, value }) => { ); }; -export const ReferenceLine: React.FC = ({ - color, - label, - labelPosition = 'right', - labelOffset = 10, - ...props -}) => { +export const ReferenceLine: React.FC = ({ color, label, ...props }) => { const { theme } = useTheme(); - const resolvedColor = color - ? resolveColorToken(color, theme) - : theme.colors.surface.text.gray.normal; return ( } From 13116525b1e8295e8e0cb8ddcab8d4725d1c0b46 Mon Sep 17 00:00:00 2001 From: tewarig Date: Thu, 28 Aug 2025 23:34:01 +0530 Subject: [PATCH 022/105] fix: line chart errors --- .../Charts/LineCharts/lineCharts.tsx | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx b/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx index 56d059414d4..25ce86e9493 100644 --- a/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx +++ b/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx @@ -130,9 +130,15 @@ export const Tooltip: React.FC = (props) => { ); }; -const CustomSquareLegend = (props: unknown) => { +const CustomSquareLegend = (props: { + payload?: Array<{ value: string; color: string }>; +}): JSX.Element | null => { const { payload } = props; + if (!payload || payload.length === 0) { + return null; + } + return (
    = (props) => { return ; }; -const CustomReferenceLabel = ({ viewBox, value }) => { - const { x, y, width } = viewBox; +const CustomReferenceLabel = ({ + viewBox, + value, +}: { + viewBox?: { x: number; y: number; width: number }; + value: string | undefined; +}): JSX.Element => { + const { x, y, width } = viewBox ?? { x: 0, y: 0, width: 0 }; const { theme } = useTheme(); return ( From 67d551813cc6a8de1f136129f1ebefb431c8c0cf Mon Sep 17 00:00:00 2001 From: tewarig Date: Thu, 28 Aug 2025 23:36:51 +0530 Subject: [PATCH 023/105] fix: ts errors --- .../Charts/LineCharts/LineCharts.stories.tsx | 14 +++++++++++--- .../components/Charts/LineCharts/lineCharts.tsx | 5 +++-- packages/blade/src/components/Charts/index.ts | 2 +- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx b/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx index b10363315c2..51680a8dab8 100644 --- a/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx +++ b/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx @@ -1,7 +1,15 @@ import type { StoryFn, Meta } from '@storybook/react'; import React from 'react'; -import { XAxis, YAxis, ReferenceLine, CartesianGrid } from '../BaseChartComponents'; -import { LineChart, Line, Tooltip, Legend, ResponsiveContainer, ChartTooltip } from './lineCharts'; +import { XAxis, YAxis, CartesianGrid } from '../BaseChartComponents'; +import { + LineChart, + Line, + Tooltip, + Legend, + ResponsiveContainer, + ChartTooltip, + ReferenceLine, +} from './lineCharts'; import { Heading } from '~components/Typography/Heading'; import { Sandbox } from '~utils/storybook/Sandbox'; import StoryPageWrapper from '~utils/storybook/StoryPageWrapper'; @@ -113,7 +121,7 @@ export const SimpleLineChart: StoryFn = () => { strokeStyle="solid" color="chart.background.categorical.emerald.moderate" /> - + diff --git a/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx b/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx index 25ce86e9493..736bda4d8b6 100644 --- a/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx +++ b/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx @@ -9,6 +9,7 @@ import { ResponsiveContainer as RechartsResponsiveContainer, ReferenceLine as RechartsReferenceLine, } from 'recharts'; +import type { LineProps as RechartsLineProps } from 'recharts'; import { useTheme } from '~components/BladeProvider'; import type { Theme } from '~components/BladeProvider'; import type { StyledPropsBlade } from '~components/Box/styledProps'; @@ -27,8 +28,8 @@ export type BladeColorToken = `chart.background.categorical.${ChartColorCategori // Chart-specific interfaces based on user specifications export interface LineProps { type?: 'step' | 'stepAfter' | 'stepBefore' | 'linear' | 'monotone'; - dot?: React.ReactNode; - activeDot?: React.ReactNode; + dot?: RechartsLineProps['dot']; + activeDot?: RechartsLineProps['activeDot']; connectNulls?: boolean; legendType?: 'none' | 'line' | 'square' | 'diamond' | 'circle' | 'cross' | 'triangle' | 'wye'; dataKey: string; diff --git a/packages/blade/src/components/Charts/index.ts b/packages/blade/src/components/Charts/index.ts index b7843c269e7..f4fb40526a1 100644 --- a/packages/blade/src/components/Charts/index.ts +++ b/packages/blade/src/components/Charts/index.ts @@ -1,3 +1,3 @@ // Export LineCharts (includes shared components) export * from './LineCharts'; -export * from './BaseChartComponents'; \ No newline at end of file +export * from './BaseChartComponents'; From 201f28d5c7edbbfbf10341143c68546ba7365c7f Mon Sep 17 00:00:00 2001 From: tewarig Date: Fri, 29 Aug 2025 00:23:50 +0530 Subject: [PATCH 024/105] chore: more update --- .../Charts/BaseChartComponents/axis.tsx | 22 ++++++++++++------- .../Charts/LineCharts/LineCharts.stories.tsx | 6 ++--- .../Charts/LineCharts/lineCharts.tsx | 2 +- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/packages/blade/src/components/Charts/BaseChartComponents/axis.tsx b/packages/blade/src/components/Charts/BaseChartComponents/axis.tsx index b84772dc4b0..ca100a9f961 100644 --- a/packages/blade/src/components/Charts/BaseChartComponents/axis.tsx +++ b/packages/blade/src/components/Charts/BaseChartComponents/axis.tsx @@ -15,8 +15,14 @@ export interface ReferenceLineProps { labelOffset?: number; } -export type XAxisProps = ComponentProps; -export type YAxisProps = ComponentProps; +export type XAxisProps = { + label?: string; + dataKey?: string; +}; +export type YAxisProps = { + label?: string; + dataKey?: string; +}; export type CartesianGridProps = ComponentProps; export const XAxis: React.FC = (props) => { @@ -29,10 +35,10 @@ export const XAxis: React.FC = (props) => { fontSize: theme.typography.fonts.size[75], fontFamily: theme.typography.fonts.family.text, }} - stroke="#c5d0db" + stroke={theme.colors.surface.border.gray.muted} label={{ //TODO: remove this hardcoding Value - value: 'Months', + value: props?.label, position: 'insideBottom', //TODO: need to change this offset. offset: -10, @@ -46,7 +52,7 @@ export const XAxis: React.FC = (props) => { lineHeight: theme.typography.lineHeights[500], }, }} - {...props} + dataKey={props?.dataKey} /> ); }; @@ -63,10 +69,10 @@ export const YAxis: React.FC = (props) => { fontSize: theme.typography.fonts.size[75], fontFamily: theme.typography.fonts.family.text, }} - stroke="#c5d0db" + stroke={theme.colors.surface.border.gray.muted} label={{ //TODO: remove this hardcoding Value - value: 'Active Users', + value: props?.label, position: 'insideLeft', //TODO: need to change this offset. offset: -10, @@ -82,7 +88,7 @@ export const YAxis: React.FC = (props) => { angle: -90, fill: theme.colors.surface.text.gray.subtle, }} - {...props} + dataKey={props?.dataKey} /> ); }; diff --git a/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx b/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx index 51680a8dab8..b6e8142860c 100644 --- a/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx +++ b/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx @@ -137,7 +137,7 @@ export const TinyLineChart: StoryFn = () => { @@ -187,8 +187,8 @@ export const DotConfigurationShowcase: StoryFn = () => { - - + + {/* No dots at all */} diff --git a/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx b/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx index 736bda4d8b6..9504c9ca18c 100644 --- a/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx +++ b/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx @@ -252,7 +252,7 @@ export const ReferenceLine: React.FC = ({ color, label, ...p return ( } From 535ca536c27a639c8e5c7f856339a775847d2ddb Mon Sep 17 00:00:00 2001 From: tewarig Date: Fri, 29 Aug 2025 01:28:45 +0530 Subject: [PATCH 025/105] chore: more style changes --- .../Charts/BaseChartComponents/axis.tsx | 40 +++++++++---------- .../Charts/LineCharts/lineCharts.tsx | 4 +- 2 files changed, 21 insertions(+), 23 deletions(-) diff --git a/packages/blade/src/components/Charts/BaseChartComponents/axis.tsx b/packages/blade/src/components/Charts/BaseChartComponents/axis.tsx index ca100a9f961..e2ae212da97 100644 --- a/packages/blade/src/components/Charts/BaseChartComponents/axis.tsx +++ b/packages/blade/src/components/Charts/BaseChartComponents/axis.tsx @@ -34,24 +34,25 @@ export const XAxis: React.FC = (props) => { fill: theme.colors.surface.text.gray.normal, fontSize: theme.typography.fonts.size[75], fontFamily: theme.typography.fonts.family.text, + fontWeight: theme.typography.fonts.weight.regular, + letterSpacing: theme.typography.letterSpacings[100], }} stroke={theme.colors.surface.border.gray.muted} - label={{ - //TODO: remove this hardcoding Value - value: props?.label, - position: 'insideBottom', - //TODO: need to change this offset. - offset: -10, - style: { - textAnchor: 'middle', - fill: theme.colors.surface.text.gray.subtle, - fontSize: theme.typography.fonts.size[75], - fontWeight: theme.typography.fonts.weight.medium, - fontFamily: theme.typography.fonts.family.text, - letterSpacing: theme.typography.letterSpacings[100], - lineHeight: theme.typography.lineHeights[500], - }, - }} + // eslint-disable-next-line react/no-unused-prop-types + label={({ viewBox }: { viewBox: { x: number; y: number; width: number } }) => ( + + {props?.label} + + )} dataKey={props?.dataKey} /> ); @@ -62,20 +63,17 @@ export const YAxis: React.FC = (props) => { return ( @@ -183,7 +182,7 @@ const CustomSquareLegend = (props: { ); }; -export const Legend: React.FC = (props) => { +export const Legend: React.FC = (props) => { const { theme } = useTheme(); return ( @@ -194,6 +193,7 @@ export const Legend: React.FC = (props) => { color: theme.colors.surface.text.gray.normal, paddingTop: '16px', }} + align="center" content={} {...props} /> From 5d23d9c57bcd37b40abfd4fd099e1fa18bbd8383 Mon Sep 17 00:00:00 2001 From: tewarig Date: Fri, 29 Aug 2025 02:17:03 +0530 Subject: [PATCH 026/105] chore: change vars --- .../Charts/LineCharts/lineCharts.tsx | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx b/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx index 38c2b1e5398..1b32e91d87f 100644 --- a/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx +++ b/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx @@ -219,21 +219,34 @@ const CustomReferenceLabel = ({ const { x, y, width } = viewBox ?? { x: 0, y: 0, width: 0 }; const { theme } = useTheme(); + const RECT_WIDTH = 80; + const RECT_HEIGHT = 30; + const rectX = x + width - RECT_WIDTH; + const rectY = y - 15; + + // Padding for text inside the rectangle (4px vertical, 8px horizontal) + const PADDING_VERTICAL = 4; + const PADDING_HORIZONTAL = 8; + + // Text position with padding inside the rectangle + const textX = rectX + PADDING_HORIZONTAL + (RECT_WIDTH - PADDING_HORIZONTAL * 2) / 2; + const textY = rectY + PADDING_VERTICAL + 15; // +15 for text baseline + return ( Date: Fri, 29 Aug 2025 02:48:14 +0530 Subject: [PATCH 027/105] feat: add line charts --- .../Charts/LineCharts/LineCharts.stories.tsx | 22 ++++++------------ .../Charts/LineCharts/lineCharts.tsx | 23 +++++++++++++------ 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx b/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx index b6e8142860c..90ab54da72d 100644 --- a/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx +++ b/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx @@ -93,8 +93,8 @@ const forecastData = [ { date: 'Jan', historical: 4000, forecast: null }, { date: 'Feb', historical: 3000, forecast: null }, { date: 'Mar', historical: 2000, forecast: null }, - { date: 'Apr', historical: null, forecast: 2780 }, - { date: 'May', historical: null, forecast: 1890 }, + { date: 'Apr', historical: 2500, forecast: 2500 }, + { date: 'May', historical: null, forecast: 4000 }, { date: 'Jun', historical: null, forecast: 2390 }, ]; @@ -137,7 +137,7 @@ export const TinyLineChart: StoryFn = () => { @@ -191,17 +191,9 @@ export const DotConfigurationShowcase: StoryFn = () => { - {/* No dots at all */} - {/* Default dots */} = () => { dataKey="historical" name="Historical Data" connectNulls={true} - color="chart.background.categorical.topaz.moderate" + color="chart.background.categorical.azure.moderate" /> diff --git a/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx b/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx index 1b32e91d87f..540f52348f4 100644 --- a/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx +++ b/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx @@ -31,7 +31,7 @@ export interface LineProps { dot?: RechartsLineProps['dot']; activeDot?: RechartsLineProps['activeDot']; connectNulls?: boolean; - legendType?: 'none' | 'line' | 'square' | 'diamond' | 'circle' | 'cross' | 'triangle' | 'wye'; + showLegend?: boolean; dataKey: string; name?: string; color: BladeColorToken; @@ -88,8 +88,9 @@ export const Line: React.FC = ({ color, strokeStyle = 'solid', type = 'monotone', - dot, - activeDot, + dot = false, + activeDot = false, + showLegend = true, ...props }) => { const { theme } = useTheme(); @@ -104,8 +105,9 @@ export const Line: React.FC = ({ strokeWidth={3} strokeDasharray={strokeDasharray} type={type} - activeDot={false} - dot={false} + activeDot={activeDot} + dot={dot} + legendType={showLegend ? 'line' : 'none'} {...props} /> ); @@ -132,13 +134,20 @@ export const Tooltip: React.FC = (props) => { }; const CustomSquareLegend = (props: { - payload?: Array<{ value: string; color: string }>; + payload?: Array<{ + payload: { + legendType: 'none' | 'line'; + }; + value: string; + color: string; + }>; }): JSX.Element | null => { const { payload } = props; if (!payload || payload.length === 0) { return null; } + const filteredPayload = payload.filter((entry) => entry?.payload?.legendType !== 'none'); return (
      - {payload.map((entry, index) => ( + {filteredPayload.map((entry, index) => (
    • Date: Fri, 29 Aug 2025 02:59:29 +0530 Subject: [PATCH 028/105] chore: update cursor values --- packages/blade/src/components/Charts/LineCharts/lineCharts.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx b/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx index 540f52348f4..07bd6cae1b9 100644 --- a/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx +++ b/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx @@ -127,7 +127,7 @@ export const Tooltip: React.FC = (props) => { fontSize: theme.typography.fonts.size[100], color: theme.colors.surface.text.gray.normal, }} - cursor={{ fill: 'rgba(0, 0, 0, 0.1)' }} + cursor={{ fill: 'rgba(0, 0, 0, 0.1)', stroke: theme.colors.surface.border.gray.muted }} {...props} /> ); From 528203013f0651aebd3dabab35ccb6a6831998ac Mon Sep 17 00:00:00 2001 From: tewarig Date: Fri, 29 Aug 2025 12:53:57 +0530 Subject: [PATCH 029/105] chore: revert changes --- .../blade/src/components/Carousel/Carousel.stories.tsx | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/blade/src/components/Carousel/Carousel.stories.tsx b/packages/blade/src/components/Carousel/Carousel.stories.tsx index 613c8e579b8..e152043d551 100644 --- a/packages/blade/src/components/Carousel/Carousel.stories.tsx +++ b/packages/blade/src/components/Carousel/Carousel.stories.tsx @@ -582,7 +582,7 @@ export const WithInteractiveCards = InteractiveCarouselTestimonialTemplate.bind( export const WithPeek: StoryFn = (props) => { return ( - + Active card is centered with adjacent cards peeking from the sides. @@ -591,10 +591,7 @@ export const WithPeek: StoryFn = (props) => { snapAlign to "center", and adding gap for spacing between items. - + Date: Fri, 29 Aug 2025 12:56:39 +0530 Subject: [PATCH 030/105] chore: update spacing --- packages/blade/src/components/Carousel/Carousel.stories.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/blade/src/components/Carousel/Carousel.stories.tsx b/packages/blade/src/components/Carousel/Carousel.stories.tsx index e152043d551..0a7a53be173 100644 --- a/packages/blade/src/components/Carousel/Carousel.stories.tsx +++ b/packages/blade/src/components/Carousel/Carousel.stories.tsx @@ -582,7 +582,7 @@ export const WithInteractiveCards = InteractiveCarouselTestimonialTemplate.bind( export const WithPeek: StoryFn = (props) => { return ( - + Active card is centered with adjacent cards peeking from the sides. From 261e1232d8dafdfa7e32545843704719315882e6 Mon Sep 17 00:00:00 2001 From: tewarig Date: Sun, 31 Aug 2025 22:49:47 +0530 Subject: [PATCH 031/105] feat: inital code for area charts --- .../Charts/AreaChart/AreaChart.stories.tsx | 286 ++++++++++++++++++ .../Charts/AreaChart/AreaCharts.tsx | 93 ++++++ .../src/components/Charts/AreaChart/index.ts | 3 + 3 files changed, 382 insertions(+) create mode 100644 packages/blade/src/components/Charts/AreaChart/AreaChart.stories.tsx create mode 100644 packages/blade/src/components/Charts/AreaChart/AreaCharts.tsx create mode 100644 packages/blade/src/components/Charts/AreaChart/index.ts diff --git a/packages/blade/src/components/Charts/AreaChart/AreaChart.stories.tsx b/packages/blade/src/components/Charts/AreaChart/AreaChart.stories.tsx new file mode 100644 index 00000000000..be860ea6208 --- /dev/null +++ b/packages/blade/src/components/Charts/AreaChart/AreaChart.stories.tsx @@ -0,0 +1,286 @@ +import type { StoryFn, Meta } from '@storybook/react'; +import React from 'react'; +import { XAxis, YAxis, CartesianGrid } from '../BaseChartComponents/axis'; +import { Tooltip, Legend, ResponsiveContainer, ReferenceLine } from '../LineCharts'; +import { AreaChart, Area } from './AreaCharts'; +import { Heading } from '~components/Typography/Heading'; +import { Sandbox } from '~utils/storybook/Sandbox'; +import StoryPageWrapper from '~utils/storybook/StoryPageWrapper'; +import { getStyledPropsArgTypes } from '~components/Box/BaseBox/storybookArgTypes'; + +const Page = (): React.ReactElement => { + return ( + + Usage + + {` + import { + AreaChart, + Area, + XAxis, + YAxis, + CartesianGrid, + Tooltip, + Legend, + ResponsiveContainer + } from '@razorpay/blade/components'; + + function App() { + const data = [ + { month: 'Jan', teamA: 4000 }, + { month: 'Feb', teamA: 3000 }, + { month: 'Mar', teamA: 2000 }, + { month: 'Apr', teamA: 2780 }, + { month: 'May', teamA: 1890 }, + { month: 'Jun', teamA: 2390 }, + ]; + + return ( + + + + + + + + + + ) + } + + export default App; + `} + + + ); +}; + +export default { + title: 'Components/Charts/AreaChart', + component: AreaChart, + tags: ['autodocs'], + argTypes: { + ...getStyledPropsArgTypes(), + }, + parameters: { + docs: { + page: Page, + }, + }, +} as Meta; + +// Sample data for charts +const chartData = [ + { month: 'Jan', teamA: 4000, teamB: 2400, teamC: 1800 }, + { month: 'Feb', teamA: 3000, teamB: 1398, teamC: 2200 }, + { month: 'Mar', teamA: 2000, teamB: 9800, teamC: 1500 }, + { month: 'Apr', teamA: 2780, teamB: 3908, teamC: 2800 }, + { month: 'May', teamA: 1890, teamB: 4800, teamC: 2100 }, + { month: 'Jun', teamA: 2390, teamB: 3800, teamC: 2500 }, +]; + +const data = [ + { + name: 'Page A', + uv: 4000, + pv: 2400, + amt: 2400, + }, + { + name: 'Page B', + uv: 3000, + pv: 1398, + amt: 2210, + }, + { + name: 'Page C', + uv: 2000, + pv: 9800, + amt: 2290, + }, + { + name: 'Page D', + pv: 3908, + amt: 2000, + uv: null, + }, + { + name: 'Page E', + uv: 1890, + pv: 4800, + amt: 2181, + }, + { + name: 'Page F', + uv: 2390, + pv: 3800, + amt: 2500, + }, + { + name: 'Page G', + uv: 3490, + pv: 4300, + amt: 2100, + }, +]; + +// 2.2.a - Simple Area Chart +export const SimpleAreaChart: StoryFn = () => { + return ( +
      + + + + + + + + + +
      + ); +}; + +// 2.2.b - Stacked Area Chart +export const StackedAreaChart: StoryFn = () => { + return ( +
      + + + + + + + + + + + +
      + ); +}; + +// 2.2.c - Area Chart that Connects Nulls +export const AreaChartConnectNulls: StoryFn = () => { + return ( +
      + + + + + + + + + + + + + + + + + + + + +
      + ); +}; + +// 2.2.d - Tiny Area Chart (Sparkline) +export const TinyAreaChart: StoryFn = () => { + return ( +
      + + + + + +
      + ); +}; + +// Area Chart with Reference Line +export const AreaChartWithReferenceLine: StoryFn = () => { + return ( +
      + + + + + + + + + + + +
      + ); +}; + +SimpleAreaChart.storyName = 'Simple Area Chart'; +StackedAreaChart.storyName = 'Stacked Area Chart'; +AreaChartConnectNulls.storyName = 'Area Chart (Connect Nulls)'; +TinyAreaChart.storyName = 'Tiny Area Chart'; +AreaChartWithReferenceLine.storyName = 'Area Chart with Reference Line'; diff --git a/packages/blade/src/components/Charts/AreaChart/AreaCharts.tsx b/packages/blade/src/components/Charts/AreaChart/AreaCharts.tsx new file mode 100644 index 00000000000..85613f4879e --- /dev/null +++ b/packages/blade/src/components/Charts/AreaChart/AreaCharts.tsx @@ -0,0 +1,93 @@ +import React from 'react'; +import type { ComponentProps } from 'react'; +import styled from 'styled-components'; +import { AreaChart as RechartsAreaChart, Area as RechartsArea } from 'recharts'; +import type { AreaProps as RechartAreaProps } from 'recharts'; + +import { useTheme } from '~components/BladeProvider'; +import type { Theme } from '~components/BladeProvider'; +import type { StyledPropsBlade } from '~components/Box/styledProps'; +import { getStyledProps } from '~components/Box/styledProps'; +import { metaAttribute } from '~utils/metaAttribute'; +import BaseBox from '~components/Box/BaseBox'; +import type { ChartColorCategories, ChartCategoricalEmphasis } from '~tokens/theme/theme'; +import getIn from '~utils/lodashButBetter/get'; + +// BladeColorToken type for charts - only allows categorical chart colors for area charts +export type BladeColorToken = `chart.background.categorical.${ChartColorCategories}.${keyof ChartCategoricalEmphasis}`; + +// Chart-specific interfaces based on user specifications +export interface AreaProps { + type?: 'step' | 'stepAfter' | 'stepBefore' | 'linear' | 'monotone'; + connectNulls?: boolean; + showLegend?: boolean; + dataKey: string; + name: string; + stackId?: string | number; + color?: BladeColorToken; + dot?: RechartAreaProps['dot']; + activeDot?: RechartAreaProps['activeDot']; +} + +// TypeScript prop types +export type AreaChartProps = Omit, 'margin'> & + StyledPropsBlade & { + children?: React.ReactNode; + }; + +// Styled wrapper for AreaChart with predefined margins +const StyledAreaChart = styled(RechartsAreaChart)<{ theme: Theme }>` + font-family: ${(props) => props.theme.typography.fonts.family.text}; +`; + +// Main components +export const AreaChart: React.FC = ({ children, ...props }) => { + const { theme } = useTheme(); + const styledProps = getStyledProps(props); + + // Predefined margins - not exposed to user + const defaultMargin = { + top: 16, + right: 16, + bottom: 16, + left: 16, + }; + + return ( + + + {children} + + + ); +}; + +export const Area: React.FC = ({ + color, + type = 'monotone', + connectNulls = false, + showLegend = true, + stackId = 1, + dot = false, + activeDot = false, + ...props +}) => { + const { theme } = useTheme(); + const colorToken = color ? getIn(theme.colors, color) : undefined; + + return ( + + ); +}; diff --git a/packages/blade/src/components/Charts/AreaChart/index.ts b/packages/blade/src/components/Charts/AreaChart/index.ts new file mode 100644 index 00000000000..d1462e94a50 --- /dev/null +++ b/packages/blade/src/components/Charts/AreaChart/index.ts @@ -0,0 +1,3 @@ +export { AreaChart, Area } from './AreaCharts'; + +export type { AreaChartProps, AreaProps } from './AreaCharts'; From 133825358f433dad58932154801b53cce0d0a430 Mon Sep 17 00:00:00 2001 From: tewarig Date: Sun, 31 Aug 2025 22:51:48 +0530 Subject: [PATCH 032/105] chore: area charts --- .../blade/src/components/Charts/AreaChart/AreaChart.stories.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/blade/src/components/Charts/AreaChart/AreaChart.stories.tsx b/packages/blade/src/components/Charts/AreaChart/AreaChart.stories.tsx index be860ea6208..ef14954b0d7 100644 --- a/packages/blade/src/components/Charts/AreaChart/AreaChart.stories.tsx +++ b/packages/blade/src/components/Charts/AreaChart/AreaChart.stories.tsx @@ -186,6 +186,7 @@ export const StackedAreaChart: StoryFn = () => { export const AreaChartConnectNulls: StoryFn = () => { return (
      + Area Chart that does not Connects Nulls :- @@ -207,6 +208,7 @@ export const AreaChartConnectNulls: StoryFn = () => { /> + Area Chart that Connects Nulls :- From 8524a8cd5d03c5b406c8fbfc02c6a87aa4725a9f Mon Sep 17 00:00:00 2001 From: tewarig Date: Sun, 31 Aug 2025 23:03:11 +0530 Subject: [PATCH 033/105] chore: code refactor --- .../BaseChartComponents.tsx | 282 ++++++++++++++++++ .../Charts/BaseChartComponents/axis.tsx | 104 ------- .../Charts/BaseChartComponents/index.ts | 20 +- .../Charts/LineCharts/LineCharts.stories.tsx | 20 +- .../src/components/Charts/LineCharts/index.ts | 20 +- .../Charts/LineCharts/lineCharts.tsx | 186 +----------- 6 files changed, 313 insertions(+), 319 deletions(-) create mode 100644 packages/blade/src/components/Charts/BaseChartComponents/BaseChartComponents.tsx delete mode 100644 packages/blade/src/components/Charts/BaseChartComponents/axis.tsx diff --git a/packages/blade/src/components/Charts/BaseChartComponents/BaseChartComponents.tsx b/packages/blade/src/components/Charts/BaseChartComponents/BaseChartComponents.tsx new file mode 100644 index 00000000000..7ae4c332102 --- /dev/null +++ b/packages/blade/src/components/Charts/BaseChartComponents/BaseChartComponents.tsx @@ -0,0 +1,282 @@ +import React from 'react'; +import type { ComponentProps } from 'react'; +import { + XAxis as RechartsXAxis, + YAxis as RechartsYAxis, + CartesianGrid as RechartsCartesianGrid, + Tooltip as RechartsTooltip, + Legend as RechartsLegend, + ResponsiveContainer as RechartsResponsiveContainer, + ReferenceLine as RechartsReferenceLine, +} from 'recharts'; +import { castWebType } from '~utils'; +import { Text } from '~components/Typography'; +import { Box } from '~components/Box'; +import { useTheme } from '~components/BladeProvider'; +import getIn from '~utils/lodashButBetter/get'; + +export interface ReferenceLineProps { + y?: number; + x?: number; + label?: string; + labelPosition?: 'left' | 'right' | 'top' | 'bottom'; + labelOffset?: number; +} + +export type XAxisProps = { + label?: string; + dataKey?: string; +}; +export type YAxisProps = { + label?: string; + dataKey?: string; +}; + +export type ChartToolTipProps = ComponentProps; +export type LegendProps = ComponentProps; +export type ResponsiveContainerProps = ComponentProps; + +export type CartesianGridProps = ComponentProps; + +export const XAxis: React.FC = (props) => { + const { theme } = useTheme(); + + return ( + ( + + {props?.label} + + )} + dataKey={props?.dataKey} + /> + ); +}; + +export const YAxis: React.FC = (props) => { + const { theme } = useTheme(); + + return ( + + ); +}; + +export const CartesianGrid: React.FC = (props) => { + const { theme } = useTheme(); + + return ( + + ); +}; + +export const ChartToolTip: React.FC = (props) => { + const { theme } = useTheme(); + + return ( + + ); +}; + +const CustomSquareLegend = (props: { + payload?: Array<{ + payload: { + legendType: 'none' | 'line'; + }; + value: string; + color: string; + }>; +}): JSX.Element | null => { + const { payload } = props; + + if (!payload || payload.length === 0) { + return null; + } + const filteredPayload = payload.filter((entry) => entry?.payload?.legendType !== 'none'); + + return ( +
        + {filteredPayload.map((entry, index) => ( +
      • + + + {/* Legend text with custom color and size */} + {/* {entry.value}{' '} */} + + {entry.value} + + + + {/* Changed text color to a dark gray */} +
      • + ))} +
      + ); +}; + +export const Legend: React.FC = (props) => { + const { theme } = useTheme(); + + return ( + } + {...props} + /> + ); +}; + +export const ResponsiveContainer: React.FC = (props) => { + return ; +}; + +const CustomReferenceLabel = ({ + viewBox, + value, +}: { + viewBox?: { x: number; y: number; width: number }; + value: string | undefined; +}): JSX.Element => { + const { x, y, width } = viewBox ?? { x: 0, y: 0, width: 0 }; + const { theme } = useTheme(); + + const RECT_WIDTH = 80; + const RECT_HEIGHT = 30; + const rectX = x + width - RECT_WIDTH; + const rectY = y - 15; + + // Padding for text inside the rectangle (4px vertical, 8px horizontal) + const PADDING_VERTICAL = 4; + const PADDING_HORIZONTAL = 8; + + // Text position with padding inside the rectangle + const textX = rectX + PADDING_HORIZONTAL + (RECT_WIDTH - PADDING_HORIZONTAL * 2) / 2; + const textY = rectY + PADDING_VERTICAL + 15; // +15 for text baseline + + return ( + + + + {value} + + + ); +}; + +export const ReferenceLine: React.FC = ({ label, ...props }) => { + const { theme } = useTheme(); + + return ( + } + {...props} + /> + ); +}; diff --git a/packages/blade/src/components/Charts/BaseChartComponents/axis.tsx b/packages/blade/src/components/Charts/BaseChartComponents/axis.tsx deleted file mode 100644 index e2ae212da97..00000000000 --- a/packages/blade/src/components/Charts/BaseChartComponents/axis.tsx +++ /dev/null @@ -1,104 +0,0 @@ -import React from 'react'; -import type { ComponentProps } from 'react'; -import { - XAxis as RechartsXAxis, - YAxis as RechartsYAxis, - CartesianGrid as RechartsCartesianGrid, -} from 'recharts'; -import { useTheme } from '~components/BladeProvider'; - -export interface ReferenceLineProps { - y?: number; - x?: number; - label?: string; - labelPosition?: 'left' | 'right' | 'top' | 'bottom'; - labelOffset?: number; -} - -export type XAxisProps = { - label?: string; - dataKey?: string; -}; -export type YAxisProps = { - label?: string; - dataKey?: string; -}; -export type CartesianGridProps = ComponentProps; - -export const XAxis: React.FC = (props) => { - const { theme } = useTheme(); - - return ( - ( - - {props?.label} - - )} - dataKey={props?.dataKey} - /> - ); -}; - -export const YAxis: React.FC = (props) => { - const { theme } = useTheme(); - - return ( - - ); -}; - -export const CartesianGrid: React.FC = (props) => { - const { theme } = useTheme(); - - return ( - - ); -}; diff --git a/packages/blade/src/components/Charts/BaseChartComponents/index.ts b/packages/blade/src/components/Charts/BaseChartComponents/index.ts index e3cb13428be..e5dc691baff 100644 --- a/packages/blade/src/components/Charts/BaseChartComponents/index.ts +++ b/packages/blade/src/components/Charts/BaseChartComponents/index.ts @@ -1,2 +1,18 @@ -export type { XAxisProps, YAxisProps, CartesianGridProps } from './axis'; -export { XAxis, YAxis, CartesianGrid } from './axis'; +export type { + XAxisProps, + YAxisProps, + CartesianGridProps, + ChartToolTipProps, + LegendProps, + ResponsiveContainerProps, + ReferenceLineProps, +} from './BaseChartComponents'; +export { + XAxis, + YAxis, + CartesianGrid, + ChartToolTip, + Legend, + ResponsiveContainer, + ReferenceLine, +} from './BaseChartComponents'; diff --git a/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx b/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx index 90ab54da72d..19e9a46ffd9 100644 --- a/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx +++ b/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx @@ -1,15 +1,15 @@ import type { StoryFn, Meta } from '@storybook/react'; import React from 'react'; -import { XAxis, YAxis, CartesianGrid } from '../BaseChartComponents'; import { - LineChart, - Line, - Tooltip, + XAxis, + YAxis, + CartesianGrid, + ChartToolTip, Legend, ResponsiveContainer, - ChartTooltip, ReferenceLine, -} from './lineCharts'; +} from '../BaseChartComponents'; +import { LineChart, Line } from './lineCharts'; import { Heading } from '~components/Typography/Heading'; import { Sandbox } from '~utils/storybook/Sandbox'; import StoryPageWrapper from '~utils/storybook/StoryPageWrapper'; @@ -107,7 +107,7 @@ export const SimpleLineChart: StoryFn = () => { - + = () => { - + = () => { - + {/* Default dots */} = () => { - + , 'mar children?: React.ReactNode; }; -export type TooltipProps = ComponentProps; -export type LegendProps = ComponentProps; -export type ResponsiveContainerProps = ComponentProps; - export interface ReferenceLineProps { y?: number; x?: number; @@ -112,173 +98,3 @@ export const Line: React.FC = ({ /> ); }; - -export const Tooltip: React.FC = (props) => { - const { theme } = useTheme(); - - return ( - - ); -}; - -const CustomSquareLegend = (props: { - payload?: Array<{ - payload: { - legendType: 'none' | 'line'; - }; - value: string; - color: string; - }>; -}): JSX.Element | null => { - const { payload } = props; - - if (!payload || payload.length === 0) { - return null; - } - const filteredPayload = payload.filter((entry) => entry?.payload?.legendType !== 'none'); - - return ( -
        - {filteredPayload.map((entry, index) => ( -
      • - - - {/* Legend text with custom color and size */} - {/* {entry.value}{' '} */} - - {entry.value} - - - - {/* Changed text color to a dark gray */} -
      • - ))} -
      - ); -}; - -export const Legend: React.FC = (props) => { - const { theme } = useTheme(); - - return ( - } - {...props} - /> - ); -}; - -export const ResponsiveContainer: React.FC = (props) => { - return ; -}; - -// Custom ChartTooltip component for forecast charts -export const ChartTooltip: React.FC = (props) => { - return ; -}; - -const CustomReferenceLabel = ({ - viewBox, - value, -}: { - viewBox?: { x: number; y: number; width: number }; - value: string | undefined; -}): JSX.Element => { - const { x, y, width } = viewBox ?? { x: 0, y: 0, width: 0 }; - const { theme } = useTheme(); - - const RECT_WIDTH = 80; - const RECT_HEIGHT = 30; - const rectX = x + width - RECT_WIDTH; - const rectY = y - 15; - - // Padding for text inside the rectangle (4px vertical, 8px horizontal) - const PADDING_VERTICAL = 4; - const PADDING_HORIZONTAL = 8; - - // Text position with padding inside the rectangle - const textX = rectX + PADDING_HORIZONTAL + (RECT_WIDTH - PADDING_HORIZONTAL * 2) / 2; - const textY = rectY + PADDING_VERTICAL + 15; // +15 for text baseline - - return ( - - - - {value} - - - ); -}; - -export const ReferenceLine: React.FC = ({ color, label, ...props }) => { - const { theme } = useTheme(); - - return ( - } - {...props} - /> - ); -}; From fe8a5e8d90552753315ed26251f114d617b49825 Mon Sep 17 00:00:00 2001 From: tewarig Date: Sun, 31 Aug 2025 23:16:00 +0530 Subject: [PATCH 034/105] chore: refactor responsive container --- .../Charts/LineCharts/LineCharts.stories.tsx | 189 ++++++++---------- .../Charts/LineCharts/lineCharts.tsx | 39 +--- 2 files changed, 98 insertions(+), 130 deletions(-) diff --git a/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx b/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx index 19e9a46ffd9..f4d57744341 100644 --- a/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx +++ b/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx @@ -6,7 +6,6 @@ import { CartesianGrid, ChartToolTip, Legend, - ResponsiveContainer, ReferenceLine, } from '../BaseChartComponents'; import { LineChart, Line } from './lineCharts'; @@ -45,7 +44,6 @@ const Page = (): React.ReactElement => { ]; return ( - @@ -54,7 +52,6 @@ const Page = (): React.ReactElement => { - ) } @@ -102,28 +99,26 @@ const forecastData = [ export const SimpleLineChart: StoryFn = () => { return (
      - - - - - - - - - - - - + + + + + + + + + +
      ); }; @@ -132,17 +127,15 @@ export const SimpleLineChart: StoryFn = () => { export const TinyLineChart: StoryFn = () => { return (
      - - - - - + + +
      ); }; @@ -151,31 +144,29 @@ export const TinyLineChart: StoryFn = () => { export const LineChartWithCustomDots: StoryFn = () => { return (
      - - - - - - - - - - - + + + + + + + + +
      ); }; @@ -184,23 +175,21 @@ export const LineChartWithCustomDots: StoryFn = () => { export const DotConfigurationShowcase: StoryFn = () => { return (
      - - - - - - - - {/* Default dots */} - - - + + + + + + + {/* Default dots */} + +
      ); }; @@ -209,29 +198,27 @@ export const DotConfigurationShowcase: StoryFn = () => { export const ForecastLineChart: StoryFn = () => { return (
      - - - - - - - - - - - + + + + + + + + +
      ); }; diff --git a/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx b/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx index 539641a1f22..9c2ef2b87a4 100644 --- a/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx +++ b/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx @@ -1,12 +1,12 @@ import React from 'react'; import type { ComponentProps } from 'react'; -import styled from 'styled-components'; -import { LineChart as RechartsLineChart, Line as RechartsLine } from 'recharts'; +import { + LineChart as RechartsLineChart, + Line as RechartsLine, + ResponsiveContainer as RechartsResponsiveContainer, +} from 'recharts'; import type { LineProps as RechartsLineProps } from 'recharts'; import { useTheme } from '~components/BladeProvider'; -import type { Theme } from '~components/BladeProvider'; -import type { StyledPropsBlade } from '~components/Box/styledProps'; -import { getStyledProps } from '~components/Box/styledProps'; import { metaAttribute } from '~utils/metaAttribute'; import BaseBox from '~components/Box/BaseBox'; import type { ChartColorCategories, ChartCategoricalEmphasis } from '~tokens/theme/theme'; @@ -29,10 +29,7 @@ export interface LineProps { } // TypeScript prop types -export type LineChartProps = Omit, 'margin'> & - StyledPropsBlade & { - children?: React.ReactNode; - }; +export type LineChartProps = ComponentProps; export interface ReferenceLineProps { y?: number; @@ -43,29 +40,13 @@ export interface ReferenceLineProps { labelOffset?: number; } -// Styled wrapper for LineChart with predefined margins -const StyledLineChart = styled(RechartsLineChart)<{ theme: Theme }>` - font-family: ${(props) => props.theme.typography.fonts.family.text}; -`; - // Main components export const LineChart: React.FC = ({ children, ...props }) => { - const { theme } = useTheme(); - const styledProps = getStyledProps(props); - - // Predefined margins - not exposed to user - const defaultMargin = { - top: 16, - right: 16, - bottom: 16, - left: 16, - }; - return ( - - - {children} - + + + {children} + ); }; From 82b5f1ae13ed9ad2a71daee3c79df12fbb0f46d7 Mon Sep 17 00:00:00 2001 From: tewarig Date: Sun, 31 Aug 2025 23:23:49 +0530 Subject: [PATCH 035/105] chore: added more example --- .../Charts/LineCharts/LineCharts.stories.tsx | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx b/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx index f4d57744341..bf57882ee1c 100644 --- a/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx +++ b/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx @@ -95,6 +95,26 @@ const forecastData = [ { date: 'Jun', historical: null, forecast: 2390 }, ]; +// Data with null values for connectNulls example +const dataWithNulls = [ + { month: 'Jan', sales: 4000 }, + { month: 'Feb', sales: 3000 }, + { month: 'Mar', sales: 5000 }, + { month: 'Apr', sales: null }, + { month: 'May', sales: 1890 }, + { month: 'Jun', sales: 2390 }, +]; + +// Data for stepped line chart example +const steppedData = [ + { month: 'Jan', value: 100 }, + { month: 'Feb', value: 150 }, + { month: 'Mar', value: 120 }, + { month: 'Apr', value: 200 }, + { month: 'May', value: 180 }, + { month: 'Jun', value: 250 }, +]; + // Simple Line Chart Example export const SimpleLineChart: StoryFn = () => { return ( @@ -223,8 +243,66 @@ export const ForecastLineChart: StoryFn = () => { ); }; +// Line Chart that Connects Nulls +export const LineChartConnectNulls: StoryFn = () => { + return ( +
      + Line Chart that do not Connects Nulls (default) + + + + + + + + + Line Chart that Connects Nulls + + + + + + + + +
      + ); +}; + +// Stepped Line Chart Example +export const SteppedLineChart: StoryFn = () => { + return ( +
      + + + + + + + + +
      + ); +}; + SimpleLineChart.storyName = 'Simple Line Chart'; TinyLineChart.storyName = 'Tiny Line Chart'; LineChartWithCustomDots.storyName = 'Line Chart with Custom Dots'; DotConfigurationShowcase.storyName = 'Dot Configuration Showcase'; ForecastLineChart.storyName = 'Forecast Line Chart'; +LineChartConnectNulls.storyName = 'Line Chart (Connect Nulls)'; +SteppedLineChart.storyName = 'Stepped Line Chart'; From 0a48c8353451ec3ec76caaec3c9da74592730ed6 Mon Sep 17 00:00:00 2001 From: tewarig Date: Sun, 31 Aug 2025 23:50:15 +0530 Subject: [PATCH 036/105] chore: update types --- .../BaseChartComponents.tsx | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/packages/blade/src/components/Charts/BaseChartComponents/BaseChartComponents.tsx b/packages/blade/src/components/Charts/BaseChartComponents/BaseChartComponents.tsx index 7ae4c332102..d87ee98167c 100644 --- a/packages/blade/src/components/Charts/BaseChartComponents/BaseChartComponents.tsx +++ b/packages/blade/src/components/Charts/BaseChartComponents/BaseChartComponents.tsx @@ -9,25 +9,24 @@ import { ResponsiveContainer as RechartsResponsiveContainer, ReferenceLine as RechartsReferenceLine, } from 'recharts'; +import type { XAxisProps as RechartsXAxisProps, YAxisProps as RechartsYAxisProps } from 'recharts'; import { castWebType } from '~utils'; import { Text } from '~components/Typography'; import { Box } from '~components/Box'; import { useTheme } from '~components/BladeProvider'; import getIn from '~utils/lodashButBetter/get'; -export interface ReferenceLineProps { - y?: number; - x?: number; - label?: string; - labelPosition?: 'left' | 'right' | 'top' | 'bottom'; - labelOffset?: number; -} +export type ReferenceLineProps = { + y: number; + x: number; + label: string; +}; -export type XAxisProps = { +export type XAxisProps = Omit & { label?: string; dataKey?: string; }; -export type YAxisProps = { +export type YAxisProps = Omit & { label?: string; dataKey?: string; }; @@ -43,6 +42,7 @@ export const XAxis: React.FC = (props) => { return ( = (props) => { letterSpacing: theme.typography.letterSpacings[100], }} stroke={theme.colors.surface.border.gray.muted} - // eslint-disable-next-line react/no-unused-prop-types label={({ viewBox }: { viewBox: { x: number; y: number; width: number } }) => ( = (props) => { return ( = ({ label, ...props }) => { const { theme } = useTheme(); - return ( } - {...props} /> ); }; From 68899eab8cb31727fe043b7c25edb1d1d1902eea Mon Sep 17 00:00:00 2001 From: tewarig Date: Sun, 31 Aug 2025 23:50:59 +0530 Subject: [PATCH 037/105] chore: make x and y optional --- .../Charts/BaseChartComponents/BaseChartComponents.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/blade/src/components/Charts/BaseChartComponents/BaseChartComponents.tsx b/packages/blade/src/components/Charts/BaseChartComponents/BaseChartComponents.tsx index d87ee98167c..d6b7f6d4ac2 100644 --- a/packages/blade/src/components/Charts/BaseChartComponents/BaseChartComponents.tsx +++ b/packages/blade/src/components/Charts/BaseChartComponents/BaseChartComponents.tsx @@ -17,8 +17,8 @@ import { useTheme } from '~components/BladeProvider'; import getIn from '~utils/lodashButBetter/get'; export type ReferenceLineProps = { - y: number; - x: number; + y?: number; + x?: number; label: string; }; From 3a7547db960aa9e3f5668260fb46f70291159805 Mon Sep 17 00:00:00 2001 From: tewarig Date: Sun, 31 Aug 2025 23:51:50 +0530 Subject: [PATCH 038/105] chore: add x and y props --- .../Charts/BaseChartComponents/BaseChartComponents.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/blade/src/components/Charts/BaseChartComponents/BaseChartComponents.tsx b/packages/blade/src/components/Charts/BaseChartComponents/BaseChartComponents.tsx index d6b7f6d4ac2..2eead17ad39 100644 --- a/packages/blade/src/components/Charts/BaseChartComponents/BaseChartComponents.tsx +++ b/packages/blade/src/components/Charts/BaseChartComponents/BaseChartComponents.tsx @@ -267,7 +267,7 @@ const CustomReferenceLabel = ({ ); }; -export const ReferenceLine: React.FC = ({ label, ...props }) => { +export const ReferenceLine: React.FC = ({ label, x, y }) => { const { theme } = useTheme(); return ( = ({ label, ...props }) strokeWidth={2} strokeDasharray="4 4" label={} + x={x} + y={y} /> ); }; From 41f667f1201edbb0695987c2393b219688845b46 Mon Sep 17 00:00:00 2001 From: tewarig Date: Mon, 1 Sep 2025 00:13:28 +0530 Subject: [PATCH 039/105] chore: update area charts --- .../Charts/AreaChart/AreaChart.stories.tsx | 206 ++++++++---------- .../Charts/AreaChart/AreaCharts.tsx | 16 +- 2 files changed, 106 insertions(+), 116 deletions(-) diff --git a/packages/blade/src/components/Charts/AreaChart/AreaChart.stories.tsx b/packages/blade/src/components/Charts/AreaChart/AreaChart.stories.tsx index ef14954b0d7..bccd9d853ac 100644 --- a/packages/blade/src/components/Charts/AreaChart/AreaChart.stories.tsx +++ b/packages/blade/src/components/Charts/AreaChart/AreaChart.stories.tsx @@ -1,7 +1,7 @@ import type { StoryFn, Meta } from '@storybook/react'; import React from 'react'; import { XAxis, YAxis, CartesianGrid } from '../BaseChartComponents/axis'; -import { Tooltip, Legend, ResponsiveContainer, ReferenceLine } from '../LineCharts'; +import { Tooltip, Legend, ReferenceLine } from '../LineCharts'; import { AreaChart, Area } from './AreaCharts'; import { Heading } from '~components/Typography/Heading'; import { Sandbox } from '~utils/storybook/Sandbox'; @@ -133,20 +133,18 @@ const data = [ export const SimpleAreaChart: StoryFn = () => { return (
      - - - - - - - - - + + + + + + +
      ); }; @@ -155,29 +153,27 @@ export const SimpleAreaChart: StoryFn = () => { export const StackedAreaChart: StoryFn = () => { return (
      - - - - - - - - - - - + + + + + + + + +
      ); }; @@ -187,50 +183,46 @@ export const AreaChartConnectNulls: StoryFn = () => { return (
      Area Chart that does not Connects Nulls :- - - - - - - - - - - + + + + + + + + Area Chart that Connects Nulls :- - - - - - - - - - - + + + + + + + +
      ); }; @@ -239,16 +231,14 @@ export const AreaChartConnectNulls: StoryFn = () => { export const TinyAreaChart: StoryFn = () => { return (
      - - - - - + + +
      ); }; @@ -257,26 +247,20 @@ export const TinyAreaChart: StoryFn = () => { export const AreaChartWithReferenceLine: StoryFn = () => { return (
      - - - - - - - - - - - + + + + + + + + +
      ); }; diff --git a/packages/blade/src/components/Charts/AreaChart/AreaCharts.tsx b/packages/blade/src/components/Charts/AreaChart/AreaCharts.tsx index 85613f4879e..757e1ecfcc1 100644 --- a/packages/blade/src/components/Charts/AreaChart/AreaCharts.tsx +++ b/packages/blade/src/components/Charts/AreaChart/AreaCharts.tsx @@ -1,7 +1,11 @@ import React from 'react'; import type { ComponentProps } from 'react'; import styled from 'styled-components'; -import { AreaChart as RechartsAreaChart, Area as RechartsArea } from 'recharts'; +import { + AreaChart as RechartsAreaChart, + Area as RechartsArea, + ResponsiveContainer, +} from 'recharts'; import type { AreaProps as RechartAreaProps } from 'recharts'; import { useTheme } from '~components/BladeProvider'; @@ -54,10 +58,12 @@ export const AreaChart: React.FC = ({ children, ...props }) => { }; return ( - - - {children} - + + + + {children} + + ); }; From f246bb1f81d341b096892dba146c371f63ad49d2 Mon Sep 17 00:00:00 2001 From: tewarig Date: Mon, 1 Sep 2025 15:49:35 +0530 Subject: [PATCH 040/105] feat: initial commit --- .../Charts/BarCharts/BarCharts.stories.tsx | 118 +++++++++++ .../components/Charts/BarCharts/BarCharts.tsx | 188 ++++++++++++++++++ .../src/components/Charts/BarCharts/index.ts | 1 + packages/blade/src/components/Charts/index.ts | 1 + 4 files changed, 308 insertions(+) create mode 100644 packages/blade/src/components/Charts/BarCharts/BarCharts.stories.tsx create mode 100644 packages/blade/src/components/Charts/BarCharts/BarCharts.tsx create mode 100644 packages/blade/src/components/Charts/BarCharts/index.ts diff --git a/packages/blade/src/components/Charts/BarCharts/BarCharts.stories.tsx b/packages/blade/src/components/Charts/BarCharts/BarCharts.stories.tsx new file mode 100644 index 00000000000..169ebf82a57 --- /dev/null +++ b/packages/blade/src/components/Charts/BarCharts/BarCharts.stories.tsx @@ -0,0 +1,118 @@ +import type { StoryFn, Meta } from '@storybook/react'; +import React from 'react'; +import { XAxis, YAxis, CartesianGrid, ChartToolTip, Legend } from '../BaseChartComponents'; +import { BarChart, Bar } from './BarCharts'; + +export default { + title: 'Components/Charts/BarChart', + component: BarChart, + tags: ['autodocs'], +} as Meta; + +const chartData = [ + { name: 'Jan', seriesA: 4000, seriesB: 2400, seriesC: 1200 }, + { name: 'Feb', seriesA: 3000, seriesB: 1398, seriesC: 900 }, + { name: 'Mar', seriesA: 2000, seriesB: 9800, seriesC: 1600 }, + { name: 'Apr', seriesA: 2780, seriesB: 3908, seriesC: 2200 }, + { name: 'May', seriesA: 1890, seriesB: 4800, seriesC: 1700 }, + { name: 'Jun', seriesA: 2390, seriesB: 3800, seriesC: 2100 }, +]; + +// 2.3.a - TinyBarChart +export const TinyBarChart: StoryFn = () => { + return ( +
      + + + +
      + ); +}; + +// 2.3.b - SimpleBarChart +export const SimpleBarChart: StoryFn = () => { + return ( +
      + + + + + + + + + +
      + ); +}; + +// 2.3.c - StackedBarChart +export const StackedBarChart: StoryFn = () => { + return ( +
      + + + + + + + + + + +
      + ); +}; + +// 2.4.d - Vertical Bar Chart +export const VerticalBarChart: StoryFn = () => { + return ( +
      + + + + + + + + + +
      + ); +}; + +TinyBarChart.storyName = 'Tiny Bar Chart'; +SimpleBarChart.storyName = 'Simple Bar Chart'; +StackedBarChart.storyName = 'Stacked Bar Chart'; +VerticalBarChart.storyName = 'Vertical Bar Chart'; diff --git a/packages/blade/src/components/Charts/BarCharts/BarCharts.tsx b/packages/blade/src/components/Charts/BarCharts/BarCharts.tsx new file mode 100644 index 00000000000..38bbcb6d808 --- /dev/null +++ b/packages/blade/src/components/Charts/BarCharts/BarCharts.tsx @@ -0,0 +1,188 @@ +import React from 'react'; +import type { ComponentProps } from 'react'; +import { + BarChart as RechartsBarChart, + Bar as RechartsBar, + ResponsiveContainer as RechartsResponsiveContainer, +} from 'recharts'; +import { useTheme } from '~components/BladeProvider'; +import BaseBox from '~components/Box/BaseBox'; +import { metaAttribute } from '~utils/metaAttribute'; +import getIn from '~utils/lodashButBetter/get'; +import type { + ChartColorCategories, + ChartCategoricalEmphasis, + ChartSequentialEmphasis, +} from '~tokens/theme/theme'; + +// Color token that allows both categorical and sequential chart colors +export type BladeColorToken = + | `chart.background.categorical.${ChartColorCategories}.${keyof ChartCategoricalEmphasis}` + | `chart.background.sequential.${Exclude< + ChartColorCategories, + 'gray' + >}.${keyof ChartSequentialEmphasis}`; + +export interface BarProps + extends Omit< + ComponentProps, + 'fill' | 'dataKey' | 'name' | 'label' | 'activeBar' + > { + dataKey: string; + name?: string; // default to dataKey + color?: BladeColorToken; + stackId?: string; + activeBar?: React.ReactElement | boolean; + label?: React.ReactElement | boolean; +} + +export type BarChartProps = Omit, 'margin'> & { + // Guard to limit number of series (Bars) - can be adjusted based on design decision + maxBars?: number; + children?: React.ReactNode; +}; + +const DEFAULT_MARGIN = { top: 16, right: 16, bottom: 16, left: 16 }; + +// Default categorical palette order for auto-assignment when color isn't provided +const DEFAULT_CATEGORICAL_COLOR_TOKENS: BladeColorToken[] = [ + 'chart.background.categorical.azure.moderate', + 'chart.background.categorical.emerald.moderate', + 'chart.background.categorical.crimson.moderate', + 'chart.background.categorical.cider.moderate', + 'chart.background.categorical.sapphire.moderate', + 'chart.background.categorical.orchid.moderate', + 'chart.background.categorical.magenta.moderate', + 'chart.background.categorical.gray.moderate', +]; + +// Arbitrary sequential limit per palette (can be tuned later with design) +const MAX_SEQUENTIAL_PER_CATEGORY = 6; + +// Internal: Check and limit sequential usage; fallback to undefined to allow categorical defaulting +function enforceSequentialLimit(token?: BladeColorToken): BladeColorToken | undefined { + if (!token) return token; + const match = token.match(/^chart\.background\.sequential\.([^\.]+)\./); + if (!match) return token; + + const category = match[1] as Exclude; + // Track sequential counts on a module-level map + // Note: This resets between renders naturally; it's enforced per render/compose + (enforceSequentialLimit as any)._seq = (enforceSequentialLimit as any)._seq ?? new Map(); + const seqMap: Map = (enforceSequentialLimit as any)._seq; + + const current = (seqMap.get(category) ?? 0) + 1; + seqMap.set(category, current); + + if (current > MAX_SEQUENTIAL_PER_CATEGORY) { + // eslint-disable-next-line no-console + console.error( + `[BarChart] Exceeded sequential color limit for '${category}'. Falling back to categorical default.`, + ); + return undefined; + } + + return token; +} + +// Bar component - resolves Blade color tokens to actual colors +export const Bar: React.FC = ({ + color, + name, + dataKey, + activeBar = false, + label = false, + ...rest +}) => { + const { theme } = useTheme(); + const fill = color ? getIn(theme.colors, color) : undefined; + + return ( + + ); +}; + +// BarChart wrapper with default margin, auto-color assignment, and max bars guard +export const BarChart: React.FC = ({ children, maxBars = 8, ...props }) => { + const processed = React.useMemo(() => { + const kids = React.Children.toArray(children); + + // Count children + const barChildren = kids.filter( + (child) => React.isValidElement(child) && (child.type as any) === Bar, + ) as React.ReactElement[]; + + if (barChildren.length > maxBars) { + return { + error: `Too many bars configured. Maximum allowed is ${maxBars}.`, + children: null as React.ReactNode, + }; + } + + // Assign colors and default names + const coloredKids: React.ReactNode[] = []; + let autoColorIdx = 0; + + kids.forEach((child) => { + if (!React.isValidElement(child) || (child.type as any) !== Bar) { + coloredKids.push(child); + return; + } + + const incomingColor = child.props.color as BladeColorToken | undefined; + const limitedColor = enforceSequentialLimit(incomingColor); + const resolvedColor = + limitedColor ?? + DEFAULT_CATEGORICAL_COLOR_TOKENS[autoColorIdx++ % DEFAULT_CATEGORICAL_COLOR_TOKENS.length]; + + coloredKids.push( + React.cloneElement(child, { + color: resolvedColor, + name: child.props.name ?? child.props.dataKey, + }), + ); + }); + + return { error: null as string | null, children: coloredKids }; + }, [children, maxBars]); + + const { theme } = useTheme(); + + return ( + + + {processed.error ? ( + // Simple centered error message; non-intrusive layout +
      + {processed.error} +
      + ) : ( + + {children} + + )} +
      +
      + ); +}; diff --git a/packages/blade/src/components/Charts/BarCharts/index.ts b/packages/blade/src/components/Charts/BarCharts/index.ts new file mode 100644 index 00000000000..f110182a748 --- /dev/null +++ b/packages/blade/src/components/Charts/BarCharts/index.ts @@ -0,0 +1 @@ +export * from './BarCharts'; diff --git a/packages/blade/src/components/Charts/index.ts b/packages/blade/src/components/Charts/index.ts index f4fb40526a1..e326587b176 100644 --- a/packages/blade/src/components/Charts/index.ts +++ b/packages/blade/src/components/Charts/index.ts @@ -1,3 +1,4 @@ // Export LineCharts (includes shared components) export * from './LineCharts'; export * from './BaseChartComponents'; +export * from './BarCharts'; From baa2c224d64936b7c37ad2e922a8c826d4898f80 Mon Sep 17 00:00:00 2001 From: tewarig Date: Mon, 1 Sep 2025 17:09:11 +0530 Subject: [PATCH 041/105] chore: add spacing b/w charts --- .../Charts/BarCharts/BarCharts.stories.tsx | 8 +++--- .../components/Charts/BarCharts/BarCharts.tsx | 28 +++++++++++++++++++ 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/packages/blade/src/components/Charts/BarCharts/BarCharts.stories.tsx b/packages/blade/src/components/Charts/BarCharts/BarCharts.stories.tsx index 169ebf82a57..920687f84ec 100644 --- a/packages/blade/src/components/Charts/BarCharts/BarCharts.stories.tsx +++ b/packages/blade/src/components/Charts/BarCharts/BarCharts.stories.tsx @@ -42,12 +42,12 @@ export const SimpleBarChart: StoryFn = () => {
      @@ -68,7 +68,7 @@ export const StackedBarChart: StoryFn = () => { dataKey="seriesA" name="Series A" stackId="stack-1" - color="chart.background.sequential.crimson.300" + color="chart.background.sequential.crimson.500" /> = () => { dataKey="seriesC" name="Series C" stackId="stack-1" - color="chart.background.sequential.crimson.500" + color="chart.background.sequential.crimson.300" /> diff --git a/packages/blade/src/components/Charts/BarCharts/BarCharts.tsx b/packages/blade/src/components/Charts/BarCharts/BarCharts.tsx index 38bbcb6d808..1610e886680 100644 --- a/packages/blade/src/components/Charts/BarCharts/BarCharts.tsx +++ b/packages/blade/src/components/Charts/BarCharts/BarCharts.tsx @@ -44,6 +44,14 @@ export type BarChartProps = Omit, 'margi const DEFAULT_MARGIN = { top: 16, right: 16, bottom: 16, left: 16 }; +export type RechartsShapeProps = { + x: number; + y: number; + width: number; + height: number; + fill: string; +}; + // Default categorical palette order for auto-assignment when color isn't provided const DEFAULT_CATEGORICAL_COLOR_TOKENS: BladeColorToken[] = [ 'chart.background.categorical.azure.moderate', @@ -106,6 +114,26 @@ export const Bar: React.FC = ({ legendType="rect" activeBar={activeBar} label={label} + // https://github.com/recharts/recharts/issues/2244#issuecomment-2288572842 + shape={(props) => { + const { fill, x, y, width, height } = props; + const gap = 2; + + const isFirst = false; + const isLast = false; + + return ( + + ); + }} /> ); }; From c28022e756ed1e61d9575cf390c687025f6f5c04 Mon Sep 17 00:00:00 2001 From: tewarig Date: Mon, 1 Sep 2025 17:16:13 +0530 Subject: [PATCH 042/105] fix: gap b/w bar charts --- .../components/Charts/BarCharts/BarCharts.tsx | 34 ++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/packages/blade/src/components/Charts/BarCharts/BarCharts.tsx b/packages/blade/src/components/Charts/BarCharts/BarCharts.tsx index 1610e886680..d884bd35499 100644 --- a/packages/blade/src/components/Charts/BarCharts/BarCharts.tsx +++ b/packages/blade/src/components/Charts/BarCharts/BarCharts.tsx @@ -66,18 +66,22 @@ const DEFAULT_CATEGORICAL_COLOR_TOKENS: BladeColorToken[] = [ // Arbitrary sequential limit per palette (can be tuned later with design) const MAX_SEQUENTIAL_PER_CATEGORY = 6; +const BAR_CHART_CORNER_RADIUS = 2; +const DISTANCE_BETWEEN_STACKED_BARS = 2; // Internal: Check and limit sequential usage; fallback to undefined to allow categorical defaulting function enforceSequentialLimit(token?: BladeColorToken): BladeColorToken | undefined { if (!token) return token; - const match = token.match(/^chart\.background\.sequential\.([^\.]+)\./); + const match = token.match(/^chart\.background\.sequential\.([^.]+)\./); if (!match) return token; const category = match[1] as Exclude; // Track sequential counts on a module-level map // Note: This resets between renders naturally; it's enforced per render/compose - (enforceSequentialLimit as any)._seq = (enforceSequentialLimit as any)._seq ?? new Map(); - const seqMap: Map = (enforceSequentialLimit as any)._seq; + (enforceSequentialLimit as { _seq?: Map })._seq = + (enforceSequentialLimit as { _seq?: Map })._seq ?? new Map(); + const seqMap: Map = (enforceSequentialLimit as { _seq?: Map }) + ._seq!; const current = (seqMap.get(category) ?? 0) + 1; seqMap.set(category, current); @@ -115,22 +119,19 @@ export const Bar: React.FC = ({ activeBar={activeBar} label={label} // https://github.com/recharts/recharts/issues/2244#issuecomment-2288572842 - shape={(props) => { - const { fill, x, y, width, height } = props; - const gap = 2; - - const isFirst = false; - const isLast = false; + shape={(props: unknown) => { + const { fill, x, y, width, height } = props as RechartsShapeProps; + const gap = DISTANCE_BETWEEN_STACKED_BARS; return ( ); }} @@ -145,7 +146,8 @@ export const BarChart: React.FC = ({ children, maxBars = 8, ...pr // Count children const barChildren = kids.filter( - (child) => React.isValidElement(child) && (child.type as any) === Bar, + (child) => + React.isValidElement(child) && (child.type as React.ComponentType) === Bar, ) as React.ReactElement[]; if (barChildren.length > maxBars) { @@ -160,7 +162,7 @@ export const BarChart: React.FC = ({ children, maxBars = 8, ...pr let autoColorIdx = 0; kids.forEach((child) => { - if (!React.isValidElement(child) || (child.type as any) !== Bar) { + if (!React.isValidElement(child) || (child.type as React.ComponentType) !== Bar) { coloredKids.push(child); return; } @@ -172,7 +174,7 @@ export const BarChart: React.FC = ({ children, maxBars = 8, ...pr DEFAULT_CATEGORICAL_COLOR_TOKENS[autoColorIdx++ % DEFAULT_CATEGORICAL_COLOR_TOKENS.length]; coloredKids.push( - React.cloneElement(child, { + React.cloneElement(child as React.ReactElement, { color: resolvedColor, name: child.props.name ?? child.props.dataKey, }), From 8de429c61bb1066832966f542f65e20b4055a834 Mon Sep 17 00:00:00 2001 From: tewarig Date: Mon, 1 Sep 2025 20:57:14 +0530 Subject: [PATCH 043/105] chore: update example --- .../Charts/BarCharts/BarChartContext.ts | 9 +++ .../Charts/BarCharts/BarCharts.stories.tsx | 43 ++++++++-- .../components/Charts/BarCharts/BarCharts.tsx | 78 ++++++++++++------- 3 files changed, 98 insertions(+), 32 deletions(-) create mode 100644 packages/blade/src/components/Charts/BarCharts/BarChartContext.ts diff --git a/packages/blade/src/components/Charts/BarCharts/BarChartContext.ts b/packages/blade/src/components/Charts/BarCharts/BarChartContext.ts new file mode 100644 index 00000000000..2c0b4e1d48d --- /dev/null +++ b/packages/blade/src/components/Charts/BarCharts/BarChartContext.ts @@ -0,0 +1,9 @@ +import { createContext, useContext } from 'react'; +// Context for chart orientation +interface BarChartContextType { + layout?: 'horizontal' | 'vertical'; +} + +export const BarChartContext = createContext({ layout: 'horizontal' }); + +export const useBarChartContext = (): BarChartContextType => useContext(BarChartContext); diff --git a/packages/blade/src/components/Charts/BarCharts/BarCharts.stories.tsx b/packages/blade/src/components/Charts/BarCharts/BarCharts.stories.tsx index 920687f84ec..fd627e4a87d 100644 --- a/packages/blade/src/components/Charts/BarCharts/BarCharts.stories.tsx +++ b/packages/blade/src/components/Charts/BarCharts/BarCharts.stories.tsx @@ -16,13 +16,19 @@ const chartData = [ { name: 'Apr', seriesA: 2780, seriesB: 3908, seriesC: 2200 }, { name: 'May', seriesA: 1890, seriesB: 4800, seriesC: 1700 }, { name: 'Jun', seriesA: 2390, seriesB: 3800, seriesC: 2100 }, + { name: 'Jul', seriesA: 2390, seriesB: 3800, seriesC: 2100 }, + { name: 'Aug', seriesA: 3000, seriesB: 4800, seriesC: 3000 }, + { name: 'Sep', seriesA: 3500, seriesB: 3400, seriesC: 5300 }, + { name: 'Oct', seriesA: 2000, seriesB: 1400, seriesC: 3300 }, + { name: 'Nov', seriesA: 1400, seriesB: 5400, seriesC: 1300 }, + { name: 'Dec', seriesA: 1200, seriesB: 4600, seriesC: 2000 }, ]; // 2.3.a - TinyBarChart export const TinyBarChart: StoryFn = () => { return (
      - +
      @@ -33,7 +39,7 @@ export const TinyBarChart: StoryFn = () => { export const SimpleBarChart: StoryFn = () => { return (
      - + @@ -87,11 +93,28 @@ export const StackedBarChart: StoryFn = () => { ); }; +export const GroupedBarChart: StoryFn = () => { + return ( +
      + + + + + + + + + + +
      + ); +}; + // 2.4.d - Vertical Bar Chart export const VerticalBarChart: StoryFn = () => { return ( -
      - +
      + @@ -100,12 +123,20 @@ export const VerticalBarChart: StoryFn = () => { +
      diff --git a/packages/blade/src/components/Charts/BarCharts/BarCharts.tsx b/packages/blade/src/components/Charts/BarCharts/BarCharts.tsx index d884bd35499..10a77ae904a 100644 --- a/packages/blade/src/components/Charts/BarCharts/BarCharts.tsx +++ b/packages/blade/src/components/Charts/BarCharts/BarCharts.tsx @@ -5,6 +5,7 @@ import { Bar as RechartsBar, ResponsiveContainer as RechartsResponsiveContainer, } from 'recharts'; +import { BarChartContext, useBarChartContext } from './BarChartContext'; import { useTheme } from '~components/BladeProvider'; import BaseBox from '~components/Box/BaseBox'; import { metaAttribute } from '~utils/metaAttribute'; @@ -107,6 +108,7 @@ export const Bar: React.FC = ({ ...rest }) => { const { theme } = useTheme(); + const { layout } = useBarChartContext(); const fill = color ? getIn(theme.colors, color) : undefined; return ( @@ -122,7 +124,23 @@ export const Bar: React.FC = ({ shape={(props: unknown) => { const { fill, x, y, width, height } = props as RechartsShapeProps; const gap = DISTANCE_BETWEEN_STACKED_BARS; - + // Check if this is a vertical layout (bars going up/down) + const isVertical = layout === 'vertical'; + + if (isVertical) { + // For vertical bars: x and y stay the same, but width/height are swapped + return ( + + ); + } return ( = ({ children, maxBars = 8, ...pr return ( - - {processed.error ? ( - // Simple centered error message; non-intrusive layout -
      - {processed.error} -
      - ) : ( - - {children} - - )} -
      + + + {processed.error ? ( + // Simple centered error message; non-intrusive layout +
      + {processed.error} +
      + ) : ( + + {processed.children} + + )} +
      +
      ); }; From 36fa5d6aebbc7a19cb1c1fbd5c2ccbfb7dc42562 Mon Sep 17 00:00:00 2001 From: tewarig Date: Tue, 2 Sep 2025 14:24:22 +0530 Subject: [PATCH 044/105] chore: remove cursor from tooltip --- .../Charts/BaseChartComponents/BaseChartComponents.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/blade/src/components/Charts/BaseChartComponents/BaseChartComponents.tsx b/packages/blade/src/components/Charts/BaseChartComponents/BaseChartComponents.tsx index 2eead17ad39..b48a217ee44 100644 --- a/packages/blade/src/components/Charts/BaseChartComponents/BaseChartComponents.tsx +++ b/packages/blade/src/components/Charts/BaseChartComponents/BaseChartComponents.tsx @@ -130,7 +130,7 @@ export const ChartToolTip: React.FC = (props) => { fontSize: theme.typography.fonts.size[100], color: theme.colors.surface.text.gray.normal, }} - cursor={{ fill: 'rgba(0, 0, 0, 0.1)', stroke: theme.colors.surface.border.gray.muted }} + cursor={false} {...props} /> ); From 36f1c121e0c62474bbcedd549737753ce55ab0fa Mon Sep 17 00:00:00 2001 From: tewarig Date: Tue, 2 Sep 2025 15:58:17 +0530 Subject: [PATCH 045/105] fix: make animation work --- .../Charts/BarCharts/BarChartContext.ts | 3 ++- .../components/Charts/BarCharts/BarCharts.tsx | 24 +++++++++++++++---- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/packages/blade/src/components/Charts/BarCharts/BarChartContext.ts b/packages/blade/src/components/Charts/BarCharts/BarChartContext.ts index 2c0b4e1d48d..fe8310e63ea 100644 --- a/packages/blade/src/components/Charts/BarCharts/BarChartContext.ts +++ b/packages/blade/src/components/Charts/BarCharts/BarChartContext.ts @@ -2,8 +2,9 @@ import { createContext, useContext } from 'react'; // Context for chart orientation interface BarChartContextType { layout?: 'horizontal' | 'vertical'; + activeIndex?: number; } -export const BarChartContext = createContext({ layout: 'horizontal' }); +export const BarChartContext = createContext({ layout: 'horizontal' , activeIndex : undefined }); export const useBarChartContext = (): BarChartContextType => useContext(BarChartContext); diff --git a/packages/blade/src/components/Charts/BarCharts/BarCharts.tsx b/packages/blade/src/components/Charts/BarCharts/BarCharts.tsx index 10a77ae904a..ca6cae93f3a 100644 --- a/packages/blade/src/components/Charts/BarCharts/BarCharts.tsx +++ b/packages/blade/src/components/Charts/BarCharts/BarCharts.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useState } from 'react'; import type { ComponentProps } from 'react'; import { BarChart as RechartsBarChart, @@ -108,9 +108,8 @@ export const Bar: React.FC = ({ ...rest }) => { const { theme } = useTheme(); - const { layout } = useBarChartContext(); + const { layout, activeIndex } = useBarChartContext(); const fill = color ? getIn(theme.colors, color) : undefined; - return ( = ({ label={label} // https://github.com/recharts/recharts/issues/2244#issuecomment-2288572842 shape={(props: unknown) => { - const { fill, x, y, width, height } = props as RechartsShapeProps; + const { fill, x, y, width, height, payload, index: barIndex } = props as RechartsShapeProps; + const isActiveIndexSame = barIndex === +activeIndex; + const fillOpacity = activeIndex ? (isActiveIndexSame ? 1 : 0.4) : 1; + console.log({ + isActiveIndexSame, + barIndex, + activeIndex, + }); + console.log('payload', payload); + console.log('barIndex', barIndex); const gap = DISTANCE_BETWEEN_STACKED_BARS; // Check if this is a vertical layout (bars going up/down) const isVertical = layout === 'vertical'; @@ -138,6 +146,7 @@ export const Bar: React.FC = ({ height={height} rx={BAR_CHART_CORNER_RADIUS} ry={BAR_CHART_CORNER_RADIUS} + fillOpacity={fillOpacity} /> ); } @@ -150,6 +159,7 @@ export const Bar: React.FC = ({ height={height - gap} rx={BAR_CHART_CORNER_RADIUS} ry={BAR_CHART_CORNER_RADIUS} + fillOpacity={fillOpacity} /> ); }} @@ -159,6 +169,7 @@ export const Bar: React.FC = ({ // BarChart wrapper with default margin, auto-color assignment, and max bars guard export const BarChart: React.FC = ({ children, maxBars = 8, ...props }) => { + const [activeIndex, setActiveIndex] = useState(0); const processed = React.useMemo(() => { const kids = React.Children.toArray(children); @@ -206,7 +217,7 @@ export const BarChart: React.FC = ({ children, maxBars = 8, ...pr return ( - + {processed.error ? ( // Simple centered error message; non-intrusive layout @@ -233,6 +244,9 @@ export const BarChart: React.FC = ({ children, maxBars = 8, ...pr barSize={49} barGap={2} barCategoryGap={2} + onMouseMove={(state) => { + setActiveIndex(state?.activeIndex); + }} > {processed.children} From e8dda48ffcecd24d9bdaee47d4edb7f0a701412e Mon Sep 17 00:00:00 2001 From: tewarig Date: Tue, 2 Sep 2025 17:21:24 +0530 Subject: [PATCH 046/105] chore: update final review --- .../Charts/BarCharts/BarChartContext.ts | 5 +++- .../components/Charts/BarCharts/BarCharts.tsx | 25 ++++++++++--------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/packages/blade/src/components/Charts/BarCharts/BarChartContext.ts b/packages/blade/src/components/Charts/BarCharts/BarChartContext.ts index fe8310e63ea..3b50dee8cb0 100644 --- a/packages/blade/src/components/Charts/BarCharts/BarChartContext.ts +++ b/packages/blade/src/components/Charts/BarCharts/BarChartContext.ts @@ -5,6 +5,9 @@ interface BarChartContextType { activeIndex?: number; } -export const BarChartContext = createContext({ layout: 'horizontal' , activeIndex : undefined }); +export const BarChartContext = createContext({ + layout: 'horizontal', + activeIndex: undefined, +}); export const useBarChartContext = (): BarChartContextType => useContext(BarChartContext); diff --git a/packages/blade/src/components/Charts/BarCharts/BarCharts.tsx b/packages/blade/src/components/Charts/BarCharts/BarCharts.tsx index ca6cae93f3a..01494e9b9f4 100644 --- a/packages/blade/src/components/Charts/BarCharts/BarCharts.tsx +++ b/packages/blade/src/components/Charts/BarCharts/BarCharts.tsx @@ -15,6 +15,7 @@ import type { ChartCategoricalEmphasis, ChartSequentialEmphasis, } from '~tokens/theme/theme'; +import isNumber from '~utils/lodashButBetter/isNumber'; // Color token that allows both categorical and sequential chart colors export type BladeColorToken = @@ -51,6 +52,7 @@ export type RechartsShapeProps = { width: number; height: number; fill: string; + index: number; }; // Default categorical palette order for auto-assignment when color isn't provided @@ -121,16 +123,8 @@ export const Bar: React.FC = ({ label={label} // https://github.com/recharts/recharts/issues/2244#issuecomment-2288572842 shape={(props: unknown) => { - const { fill, x, y, width, height, payload, index: barIndex } = props as RechartsShapeProps; - const isActiveIndexSame = barIndex === +activeIndex; - const fillOpacity = activeIndex ? (isActiveIndexSame ? 1 : 0.4) : 1; - console.log({ - isActiveIndexSame, - barIndex, - activeIndex, - }); - console.log('payload', payload); - console.log('barIndex', barIndex); + const { fill, x, y, width, height, index: barIndex } = props as RechartsShapeProps; + const fillOpacity = isNumber(activeIndex) ? (barIndex === activeIndex ? 1 : 0.4) : 1; const gap = DISTANCE_BETWEEN_STACKED_BARS; // Check if this is a vertical layout (bars going up/down) const isVertical = layout === 'vertical'; @@ -147,6 +141,9 @@ export const Bar: React.FC = ({ rx={BAR_CHART_CORNER_RADIUS} ry={BAR_CHART_CORNER_RADIUS} fillOpacity={fillOpacity} + style={{ + transition: 'fill-opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1)', + }} /> ); } @@ -160,6 +157,9 @@ export const Bar: React.FC = ({ rx={BAR_CHART_CORNER_RADIUS} ry={BAR_CHART_CORNER_RADIUS} fillOpacity={fillOpacity} + style={{ + transition: 'fill-opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1)', + }} /> ); }} @@ -169,11 +169,12 @@ export const Bar: React.FC = ({ // BarChart wrapper with default margin, auto-color assignment, and max bars guard export const BarChart: React.FC = ({ children, maxBars = 8, ...props }) => { - const [activeIndex, setActiveIndex] = useState(0); + const [activeIndex, setActiveIndex] = useState(undefined); const processed = React.useMemo(() => { const kids = React.Children.toArray(children); // Count children + //TODO: Look into this logic before final code review. const barChildren = kids.filter( (child) => React.isValidElement(child) && (child.type as React.ComponentType) === Bar, @@ -245,7 +246,7 @@ export const BarChart: React.FC = ({ children, maxBars = 8, ...pr barGap={2} barCategoryGap={2} onMouseMove={(state) => { - setActiveIndex(state?.activeIndex); + setActiveIndex(state?.activeIndex ? Number(state?.activeIndex) : undefined); }} > {processed.children} From 26bbf54ca62c227df3b649184b796954c5f0b3a9 Mon Sep 17 00:00:00 2001 From: tewarig Date: Mon, 8 Sep 2025 14:29:07 +0530 Subject: [PATCH 047/105] feat: update color theme token --- .../Charts/LineCharts/LineCharts.stories.tsx | 73 +++++-------------- .../Charts/LineCharts/lineCharts.tsx | 43 +++++++---- .../src/components/Charts/utils/index.tsx | 1 + .../components/Charts/utils/useColorTheme.tsx | 27 +++++++ 4 files changed, 75 insertions(+), 69 deletions(-) create mode 100644 packages/blade/src/components/Charts/utils/index.tsx create mode 100644 packages/blade/src/components/Charts/utils/useColorTheme.tsx diff --git a/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx b/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx index bf57882ee1c..963b0ebe9a6 100644 --- a/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx +++ b/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx @@ -160,60 +160,6 @@ export const TinyLineChart: StoryFn = () => { ); }; -// Line Chart with Custom Dots -export const LineChartWithCustomDots: StoryFn = () => { - return ( -
      - - - - - - - - - -
      - ); -}; - -// Showcase Different Dot Configurations -export const DotConfigurationShowcase: StoryFn = () => { - return ( -
      - - - - - - - {/* Default dots */} - - -
      - ); -}; - // Forecast Line Chart Example export const ForecastLineChart: StoryFn = () => { return ( @@ -299,10 +245,25 @@ export const SteppedLineChart: StoryFn = () => { ); }; +export const LineChartWithDefaultColorTheme: StoryFn = () => { + return ( +
      + + + + + + + + + +
      + ); +}; + SimpleLineChart.storyName = 'Simple Line Chart'; TinyLineChart.storyName = 'Tiny Line Chart'; -LineChartWithCustomDots.storyName = 'Line Chart with Custom Dots'; -DotConfigurationShowcase.storyName = 'Dot Configuration Showcase'; ForecastLineChart.storyName = 'Forecast Line Chart'; LineChartConnectNulls.storyName = 'Line Chart (Connect Nulls)'; SteppedLineChart.storyName = 'Stepped Line Chart'; +LineChartWithDefaultColorTheme.storyName = 'Line Chart with Default Color Theme'; diff --git a/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx b/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx index 9c2ef2b87a4..2bde3dfbbbd 100644 --- a/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx +++ b/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx @@ -6,6 +6,7 @@ import { ResponsiveContainer as RechartsResponsiveContainer, } from 'recharts'; import type { LineProps as RechartsLineProps } from 'recharts'; +import { useChartsColorTheme } from '../utils'; import { useTheme } from '~components/BladeProvider'; import { metaAttribute } from '~utils/metaAttribute'; import BaseBox from '~components/Box/BaseBox'; @@ -24,8 +25,12 @@ export interface LineProps { showLegend?: boolean; dataKey: string; name?: string; - color: BladeColorToken; + color?: BladeColorToken; strokeStyle?: 'dotted' | 'dashed' | 'solid'; + /** + * @private + */ + _index?: number; // Add this for internal use } // TypeScript prop types @@ -40,17 +45,6 @@ export interface ReferenceLineProps { labelOffset?: number; } -// Main components -export const LineChart: React.FC = ({ children, ...props }) => { - return ( - - - {children} - - - ); -}; - export const Line: React.FC = ({ color, strokeStyle = 'solid', @@ -58,10 +52,12 @@ export const Line: React.FC = ({ dot = false, activeDot = false, showLegend = true, + _index, ...props }) => { const { theme } = useTheme(); - const colorToken = getIn(theme.colors, color); + const colorIndex = useChartsColorTheme(); + const colorToken = color ? getIn(theme.colors, color) : colorIndex[_index ?? 0]; const strokeDasharray = strokeStyle === 'dashed' ? '5 5' : strokeStyle === 'dotted' ? '2 2' : undefined; @@ -79,3 +75,24 @@ export const Line: React.FC = ({ /> ); }; + +// Main components +export const LineChart: React.FC = ({ children, ...props }) => { + const childrenWithIndex = React.useMemo(() => { + let LineChartIndex = 0; + return React.Children.map(children, (child) => { + if (React.isValidElement(child) && child.type === Line) { + return React.cloneElement(child, { _index: LineChartIndex++ } as Partial); + } + return child; + }); + }, [children]); + + return ( + + + {childrenWithIndex} + + + ); +}; diff --git a/packages/blade/src/components/Charts/utils/index.tsx b/packages/blade/src/components/Charts/utils/index.tsx new file mode 100644 index 00000000000..de24aca9e65 --- /dev/null +++ b/packages/blade/src/components/Charts/utils/index.tsx @@ -0,0 +1 @@ +export { default as useChartsColorTheme } from './useColorTheme'; diff --git a/packages/blade/src/components/Charts/utils/useColorTheme.tsx b/packages/blade/src/components/Charts/utils/useColorTheme.tsx new file mode 100644 index 00000000000..f4529b1907c --- /dev/null +++ b/packages/blade/src/components/Charts/utils/useColorTheme.tsx @@ -0,0 +1,27 @@ +import { useTheme } from '~components/BladeProvider'; + +const useChartsColorTheme = (): string[] => { + const { theme } = useTheme(); + const defaultColorThemeArray = [ + theme.colors.chart.background.categorical.azure.strong, + theme.colors.chart.background.categorical.topaz.strong, + theme.colors.chart.background.categorical.orchid.strong, + theme.colors.chart.background.categorical.emerald.strong, + theme.colors.chart.background.categorical.cider.strong, + theme.colors.chart.background.categorical.sapphire.strong, + theme.colors.chart.background.categorical.magenta.strong, + theme.colors.chart.background.categorical.crimson.strong, + theme.colors.chart.background.categorical.azure.intense, + theme.colors.chart.background.categorical.topaz.intense, + theme.colors.chart.background.categorical.orchid.intense, + theme.colors.chart.background.categorical.emerald.intense, + theme.colors.chart.background.categorical.sapphire.intense, + theme.colors.chart.background.categorical.magenta.intense, + theme.colors.chart.background.categorical.cider.intense, + theme.colors.chart.background.categorical.crimson.intense, + ]; + + return defaultColorThemeArray; +}; + +export default useChartsColorTheme; From 85588b64e369319e5e69fafeb00086df6c7fed57 Mon Sep 17 00:00:00 2001 From: tewarig Date: Mon, 8 Sep 2025 14:50:13 +0530 Subject: [PATCH 048/105] feat: expose color theme --- .../Charts/LineCharts/LineCharts.stories.tsx | 6 ++-- .../Charts/LineCharts/lineCharts.tsx | 24 ++++++++++++---- .../components/Charts/utils/useColorTheme.tsx | 28 +++++++++++++++++-- 3 files changed, 48 insertions(+), 10 deletions(-) diff --git a/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx b/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx index 963b0ebe9a6..518838b7d7f 100644 --- a/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx +++ b/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx @@ -248,14 +248,14 @@ export const SteppedLineChart: StoryFn = () => { export const LineChartWithDefaultColorTheme: StoryFn = () => { return (
      - + - - + +
      ); diff --git a/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx b/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx index 2bde3dfbbbd..16d13dfafa2 100644 --- a/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx +++ b/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx @@ -31,10 +31,16 @@ export interface LineProps { * @private */ _index?: number; // Add this for internal use + /** + * @private + */ + _colorTheme?: 'default' | 'informational'; } // TypeScript prop types -export type LineChartProps = ComponentProps; +export type LineChartProps = ComponentProps & { + colorTheme?: 'default' | 'informational'; +}; export interface ReferenceLineProps { y?: number; @@ -53,10 +59,11 @@ export const Line: React.FC = ({ activeDot = false, showLegend = true, _index, + _colorTheme, ...props }) => { const { theme } = useTheme(); - const colorIndex = useChartsColorTheme(); + const colorIndex = useChartsColorTheme({ colorTheme: _colorTheme ?? 'default' }); const colorToken = color ? getIn(theme.colors, color) : colorIndex[_index ?? 0]; const strokeDasharray = @@ -77,16 +84,23 @@ export const Line: React.FC = ({ }; // Main components -export const LineChart: React.FC = ({ children, ...props }) => { +export const LineChart: React.FC = ({ + children, + colorTheme = 'default', + ...props +}) => { const childrenWithIndex = React.useMemo(() => { let LineChartIndex = 0; return React.Children.map(children, (child) => { if (React.isValidElement(child) && child.type === Line) { - return React.cloneElement(child, { _index: LineChartIndex++ } as Partial); + return React.cloneElement(child, { + _index: LineChartIndex++, + _colorTheme: colorTheme, + } as Partial); } return child; }); - }, [children]); + }, [children, colorTheme]); return ( diff --git a/packages/blade/src/components/Charts/utils/useColorTheme.tsx b/packages/blade/src/components/Charts/utils/useColorTheme.tsx index f4529b1907c..eca06b1b349 100644 --- a/packages/blade/src/components/Charts/utils/useColorTheme.tsx +++ b/packages/blade/src/components/Charts/utils/useColorTheme.tsx @@ -1,6 +1,11 @@ import { useTheme } from '~components/BladeProvider'; -const useChartsColorTheme = (): string[] => { +const useChartsColorTheme = ({ + colorTheme = 'default', +}: { + colorTheme: 'default' | 'informational'; +}): string[] => { + console.log('colorTheme', colorTheme); const { theme } = useTheme(); const defaultColorThemeArray = [ theme.colors.chart.background.categorical.azure.strong, @@ -21,7 +26,26 @@ const useChartsColorTheme = (): string[] => { theme.colors.chart.background.categorical.crimson.intense, ]; - return defaultColorThemeArray; + const informationalColorThemeArray = [ + theme.colors.chart.background.categorical.emerald.strong, + theme.colors.chart.background.categorical.crimson.strong, + theme.colors.chart.background.categorical.orchid.strong, + theme.colors.chart.background.categorical.emerald.strong, + theme.colors.chart.background.categorical.cider.strong, + theme.colors.chart.background.categorical.sapphire.strong, + theme.colors.chart.background.categorical.magenta.strong, + theme.colors.chart.background.categorical.crimson.strong, + theme.colors.chart.background.categorical.azure.intense, + theme.colors.chart.background.categorical.topaz.intense, + theme.colors.chart.background.categorical.orchid.intense, + theme.colors.chart.background.categorical.emerald.intense, + theme.colors.chart.background.categorical.sapphire.intense, + theme.colors.chart.background.categorical.magenta.intense, + theme.colors.chart.background.categorical.cider.intense, + theme.colors.chart.background.categorical.crimson.intense, + ]; + + return colorTheme === 'default' ? defaultColorThemeArray : informationalColorThemeArray; }; export default useChartsColorTheme; From 0aa935f8df227deef32dda20d135677edb55c28c Mon Sep 17 00:00:00 2001 From: tewarig Date: Mon, 8 Sep 2025 15:21:27 +0530 Subject: [PATCH 049/105] feat: update line charts --- .../blade/src/components/Charts/LineCharts/lineCharts.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx b/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx index 16d13dfafa2..2f9b90eaaec 100644 --- a/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx +++ b/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx @@ -69,6 +69,12 @@ export const Line: React.FC = ({ const strokeDasharray = strokeStyle === 'dashed' ? '5 5' : strokeStyle === 'dotted' ? '2 2' : undefined; + const isLineDotted = strokeStyle === 'dashed'; + const animationBegin = isLineDotted + ? theme.motion.delay.gentle + theme.motion.duration.xgentle + : theme.motion.delay.gentle; + const animationDuration = theme.motion.duration.xgentle; + return ( = ({ activeDot={activeDot} dot={dot} legendType={showLegend ? 'line' : 'none'} + animationBegin={animationBegin} + animationDuration={animationDuration} {...props} /> ); From 016e9e3c363b2621d9b53a5a1a78c31e855d88d4 Mon Sep 17 00:00:00 2001 From: tewarig Date: Mon, 8 Sep 2025 15:25:13 +0530 Subject: [PATCH 050/105] fix: update snaps --- .../createTheme.native.test.tsx.snap | 700 ++++++++++++++++++ .../createTheme.web.test.tsx.snap | 700 ++++++++++++++++++ 2 files changed, 1400 insertions(+) diff --git a/packages/blade/src/tokens/theme/__tests__/__snapshots__/createTheme.native.test.tsx.snap b/packages/blade/src/tokens/theme/__tests__/__snapshots__/createTheme.native.test.tsx.snap index 5f9bb302e09..a1528a53478 100644 --- a/packages/blade/src/tokens/theme/__tests__/__snapshots__/createTheme.native.test.tsx.snap +++ b/packages/blade/src/tokens/theme/__tests__/__snapshots__/createTheme.native.test.tsx.snap @@ -32,6 +32,181 @@ exports[`createTheme should create a theme with the correct brand colors 1`] = ` }, "colors": { "onDark": { + "chart": { + "background": { + "categorical": { + "azure": { + "faint": "hsla(227, 69%, 25%, 1)", + "intense": "hsla(220, 100%, 73%, 1)", + "moderate": "hsla(227, 100%, 59%, 1)", + "strong": "hsla(219, 95%, 85%, 1)", + "subtle": "hsla(227, 69%, 42%, 1)", + }, + "cider": { + "faint": "hsla(25, 82%, 20%, 1)", + "intense": "hsla(25, 100%, 72%, 1)", + "moderate": "hsla(25, 90%, 48%, 1)", + "strong": "hsla(25, 100%, 80%, 1)", + "subtle": "hsla(25, 82%, 35%, 1)", + }, + "crimson": { + "faint": "hsla(0, 84%, 25%, 1)", + "intense": "hsla(4, 96%, 79%, 1)", + "moderate": "hsla(4, 77%, 40%, 1)", + "strong": "hsla(3, 97%, 88%, 1)", + "subtle": "hsla(0, 83%, 33%, 1)", + }, + "emerald": { + "faint": "hsla(150, 100%, 11%, 1)", + "intense": "hsla(150, 59%, 73%, 1)", + "moderate": "hsla(150, 100%, 27%, 1)", + "strong": "hsla(150, 59%, 82%, 1)", + "subtle": "hsla(150, 100%, 21%, 1)", + }, + "gray": { + "faint": "hsla(214, 44%, 7%, 1)", + "intense": "hsla(216, 27%, 24%, 1)", + "moderate": "hsla(218, 26%, 18%, 1)", + "strong": "hsla(216, 22%, 36%, 1)", + "subtle": "hsla(216, 30%, 13%, 1)", + }, + "magenta": { + "faint": "hsla(331, 65%, 15%, 1)", + "intense": "hsla(331, 82%, 74%, 1)", + "moderate": "hsla(331, 64%, 34%, 1)", + "strong": "hsla(330, 83%, 84%, 1)", + "subtle": "hsla(331, 65%, 27%, 1)", + }, + "orchid": { + "faint": "hsla(252, 50%, 17%, 1)", + "intense": "hsla(252, 100%, 80%, 1)", + "moderate": "hsla(252, 57%, 54%, 1)", + "strong": "hsla(252, 100%, 88%, 1)", + "subtle": "hsla(251, 49%, 32%, 1)", + }, + "sapphire": { + "faint": "hsla(199, 84%, 15%, 1)", + "intense": "hsla(200, 90%, 72%, 1)", + "moderate": "hsla(200, 84%, 44%, 1)", + "strong": "hsla(200, 89%, 82%, 1)", + "subtle": "hsla(200, 84%, 29%, 1)", + }, + "topaz": { + "faint": "hsla(50, 97%, 19%, 1)", + "intense": "hsla(51, 100%, 60%, 1)", + "moderate": "hsla(50, 100%, 40%, 1)", + "strong": "hsla(51, 100%, 68%, 1)", + "subtle": "hsla(50, 100%, 28%, 1)", + }, + }, + "sequential": { + "azure": { + "100": "hsla(227, 69%, 25%, 1)", + "1000": "hsla(222, 100%, 98%, 1)", + "200": "hsla(227, 69%, 34%, 1)", + "300": "hsla(227, 69%, 42%, 1)", + "400": "hsla(227, 71%, 51%, 1)", + "50": "hsla(227, 69%, 17%, 1)", + "500": "hsla(227, 100%, 59%, 1)", + "600": "hsla(223, 100%, 65%, 1)", + "700": "hsla(220, 100%, 73%, 1)", + "800": "hsla(219, 95%, 85%, 1)", + "900": "hsla(221, 90%, 92%, 1)", + }, + "cider": { + "100": "hsla(25, 80%, 28%, 1)", + "1000": "hsla(24, 100%, 96%, 1)", + "200": "hsla(25, 82%, 35%, 1)", + "300": "hsla(25, 85%, 42%, 1)", + "400": "hsla(25, 90%, 48%, 1)", + "50": "hsla(25, 82%, 20%, 1)", + "500": "hsla(25, 100%, 55%, 1)", + "600": "hsla(25, 100%, 63%, 1)", + "700": "hsla(25, 100%, 72%, 1)", + "800": "hsla(25, 100%, 80%, 1)", + "900": "hsla(25, 100%, 90%, 1)", + }, + "crimson": { + "100": "hsla(0, 84%, 29%, 1)", + "1000": "hsla(0, 100%, 98%, 1)", + "200": "hsla(0, 83%, 33%, 1)", + "300": "hsla(4, 77%, 40%, 1)", + "400": "hsla(4, 74%, 49%, 1)", + "50": "hsla(0, 84%, 25%, 1)", + "500": "hsla(4, 86%, 58%, 1)", + "600": "hsla(4, 93%, 68%, 1)", + "700": "hsla(4, 96%, 79%, 1)", + "800": "hsla(3, 97%, 88%, 1)", + "900": "hsla(4, 93%, 94%, 1)", + }, + "emerald": { + "100": "hsla(150, 100%, 16%, 1)", + "1000": "hsla(152, 60%, 95%, 1)", + "200": "hsla(150, 100%, 21%, 1)", + "300": "hsla(150, 100%, 27%, 1)", + "400": "hsla(150, 100%, 32%, 1)", + "50": "hsla(150, 100%, 11%, 1)", + "500": "hsla(150, 100%, 37%, 1)", + "600": "hsla(150, 59%, 55%, 1)", + "700": "hsla(150, 59%, 73%, 1)", + "800": "hsla(150, 59%, 82%, 1)", + "900": "hsla(151, 57%, 91%, 1)", + }, + "magenta": { + "100": "hsla(331, 65%, 21%, 1)", + "1000": "hsla(330, 78%, 97%, 1)", + "200": "hsla(331, 65%, 27%, 1)", + "300": "hsla(331, 64%, 34%, 1)", + "400": "hsla(331, 64%, 45%, 1)", + "50": "hsla(331, 65%, 15%, 1)", + "500": "hsla(331, 82%, 56%, 1)", + "600": "hsla(331, 82%, 65%, 1)", + "700": "hsla(331, 82%, 74%, 1)", + "800": "hsla(330, 83%, 84%, 1)", + "900": "hsla(331, 83%, 93%, 1)", + }, + "orchid": { + "100": "hsla(252, 50%, 24%, 1)", + "1000": "hsla(249, 100%, 98%, 1)", + "200": "hsla(251, 49%, 32%, 1)", + "300": "hsla(251, 49%, 41%, 1)", + "400": "hsla(252, 57%, 54%, 1)", + "50": "hsla(252, 50%, 17%, 1)", + "500": "hsla(252, 100%, 67%, 1)", + "600": "hsla(252, 100%, 74%, 1)", + "700": "hsla(252, 100%, 80%, 1)", + "800": "hsla(252, 100%, 88%, 1)", + "900": "hsla(252, 100%, 95%, 1)", + }, + "sapphire": { + "100": "hsla(200, 84%, 22%, 1)", + "1000": "hsla(201, 92%, 95%, 1)", + "200": "hsla(200, 84%, 29%, 1)", + "300": "hsla(200, 84%, 37%, 1)", + "400": "hsla(200, 84%, 44%, 1)", + "50": "hsla(199, 84%, 15%, 1)", + "500": "hsla(198, 90%, 52%, 1)", + "600": "hsla(200, 90%, 65%, 1)", + "700": "hsla(200, 90%, 72%, 1)", + "800": "hsla(200, 89%, 82%, 1)", + "900": "hsla(200, 88%, 90%, 1)", + }, + "topaz": { + "100": "hsla(50, 100%, 24%, 1)", + "1000": "hsla(51, 100%, 92%, 1)", + "200": "hsla(50, 100%, 28%, 1)", + "300": "hsla(50, 100%, 32%, 1)", + "400": "hsla(50, 100%, 40%, 1)", + "50": "hsla(50, 97%, 19%, 1)", + "500": "hsla(50, 100%, 46%, 1)", + "600": "hsla(51, 100%, 50%, 1)", + "700": "hsla(51, 100%, 60%, 1)", + "800": "hsla(51, 100%, 68%, 1)", + "900": "hsla(51, 100%, 80%, 1)", + }, + }, + }, + }, "feedback": { "background": { "information": { @@ -482,6 +657,181 @@ exports[`createTheme should create a theme with the correct brand colors 1`] = ` "transparent": "hsla(217, 27%, 15%, 0)", }, "onLight": { + "chart": { + "background": { + "categorical": { + "azure": { + "faint": "hsla(222, 100%, 98%, 1)", + "intense": "hsla(227, 71%, 51%, 1)", + "moderate": "hsla(227, 100%, 59%, 1)", + "strong": "hsla(227, 69%, 34%, 1)", + "subtle": "hsla(219, 95%, 85%, 1)", + }, + "cider": { + "faint": "hsla(25, 90%, 48%, 1)", + "intense": "hsla(25, 90%, 48%, 1)", + "moderate": "hsla(25, 100%, 80%, 1)", + "strong": "hsla(25, 82%, 35%, 1)", + "subtle": "hsla(24, 100%, 96%, 1)", + }, + "crimson": { + "faint": "hsla(0, 100%, 98%, 1)", + "intense": "hsla(4, 74%, 49%, 1)", + "moderate": "hsla(4, 74%, 49%, 1)", + "strong": "hsla(0, 83%, 33%, 1)", + "subtle": "hsla(3, 97%, 88%, 1)", + }, + "emerald": { + "faint": "hsla(152, 60%, 95%, 1)", + "intense": "hsla(150, 100%, 32%, 1)", + "moderate": "hsla(150, 100%, 37%, 1)", + "strong": "hsla(150, 100%, 21%, 1)", + "subtle": "hsla(150, 59%, 82%, 1)", + }, + "gray": { + "faint": "hsla(216, 22%, 36%, 1)", + "intense": "hsla(216, 18%, 62%, 1)", + "moderate": "hsla(214, 20%, 84%, 1)", + "strong": "hsla(216, 27%, 24%, 1)", + "subtle": "hsla(214, 20%, 84%, 0.09)", + }, + "magenta": { + "faint": "hsla(331, 64%, 45%, 1)", + "intense": "hsla(331, 64%, 45%, 1)", + "moderate": "hsla(330, 83%, 84%, 1)", + "strong": "hsla(331, 65%, 27%, 1)", + "subtle": "hsla(330, 78%, 97%, 1)", + }, + "orchid": { + "faint": "hsla(252, 57%, 54%, 1)", + "intense": "hsla(252, 100%, 67%, 1)", + "moderate": "hsla(252, 100%, 88%, 1)", + "strong": "hsla(251, 49%, 32%, 1)", + "subtle": "hsla(249, 100%, 98%, 1)", + }, + "sapphire": { + "faint": "hsla(200, 84%, 44%, 1)", + "intense": "hsla(200, 84%, 44%, 1)", + "moderate": "hsla(200, 89%, 82%, 1)", + "strong": "hsla(200, 84%, 29%, 1)", + "subtle": "hsla(201, 92%, 95%, 1)", + }, + "topaz": { + "faint": "hsla(50, 100%, 40%, 1)", + "intense": "hsla(50, 100%, 46%, 1)", + "moderate": "hsla(51, 100%, 68%, 1)", + "strong": "hsla(50, 100%, 28%, 1)", + "subtle": "hsla(51, 100%, 92%, 1)", + }, + }, + "sequential": { + "azure": { + "100": "hsla(221, 90%, 92%, 1)", + "1000": "hsla(227, 69%, 17%, 1)", + "200": "hsla(219, 95%, 85%, 1)", + "300": "hsla(220, 100%, 73%, 1)", + "400": "hsla(223, 100%, 65%, 1)", + "50": "hsla(222, 100%, 98%, 1)", + "500": "hsla(227, 100%, 59%, 1)", + "600": "hsla(227, 71%, 51%, 1)", + "700": "hsla(227, 69%, 42%, 1)", + "800": "hsla(227, 69%, 34%, 1)", + "900": "hsla(227, 69%, 25%, 1)", + }, + "cider": { + "100": "hsla(25, 100%, 90%, 1)", + "1000": "hsla(25, 82%, 20%, 1)", + "200": "hsla(25, 100%, 80%, 1)", + "300": "hsla(25, 100%, 72%, 1)", + "400": "hsla(25, 100%, 63%, 1)", + "50": "hsla(24, 100%, 96%, 1)", + "500": "hsla(25, 100%, 55%, 1)", + "600": "hsla(25, 90%, 48%, 1)", + "700": "hsla(25, 85%, 42%, 1)", + "800": "hsla(25, 82%, 35%, 1)", + "900": "hsla(25, 80%, 28%, 1)", + }, + "crimson": { + "100": "hsla(4, 93%, 94%, 1)", + "1000": "hsla(0, 84%, 25%, 1)", + "200": "hsla(3, 97%, 88%, 1)", + "300": "hsla(4, 96%, 79%, 1)", + "400": "hsla(4, 93%, 68%, 1)", + "50": "hsla(0, 100%, 98%, 1)", + "500": "hsla(4, 86%, 58%, 1)", + "600": "hsla(4, 74%, 49%, 1)", + "700": "hsla(4, 77%, 40%, 1)", + "800": "hsla(0, 83%, 33%, 1)", + "900": "hsla(0, 84%, 29%, 1)", + }, + "emerald": { + "100": "hsla(151, 57%, 91%, 1)", + "1000": "hsla(150, 100%, 11%, 1)", + "200": "hsla(150, 59%, 82%, 1)", + "300": "hsla(150, 59%, 73%, 1)", + "400": "hsla(150, 59%, 55%, 1)", + "50": "hsla(152, 60%, 95%, 1)", + "500": "hsla(150, 100%, 37%, 1)", + "600": "hsla(150, 100%, 32%, 1)", + "700": "hsla(150, 100%, 27%, 1)", + "800": "hsla(150, 100%, 21%, 1)", + "900": "hsla(150, 100%, 16%, 1)", + }, + "magenta": { + "100": "hsla(331, 83%, 93%, 1)", + "1000": "hsla(331, 65%, 15%, 1)", + "200": "hsla(330, 83%, 84%, 1)", + "300": "hsla(331, 82%, 74%, 1)", + "400": "hsla(331, 82%, 65%, 1)", + "50": "hsla(330, 78%, 97%, 1)", + "500": "hsla(331, 82%, 56%, 1)", + "600": "hsla(331, 64%, 45%, 1)", + "700": "hsla(331, 64%, 34%, 1)", + "800": "hsla(331, 65%, 27%, 1)", + "900": "hsla(331, 65%, 21%, 1)", + }, + "orchid": { + "100": "hsla(252, 100%, 95%, 1)", + "1000": "hsla(252, 50%, 17%, 1)", + "200": "hsla(252, 100%, 88%, 1)", + "300": "hsla(252, 100%, 80%, 1)", + "400": "hsla(252, 100%, 74%, 1)", + "50": "hsla(249, 100%, 98%, 1)", + "500": "hsla(252, 100%, 67%, 1)", + "600": "hsla(252, 57%, 54%, 1)", + "700": "hsla(251, 49%, 41%, 1)", + "800": "hsla(251, 49%, 32%, 1)", + "900": "hsla(252, 50%, 24%, 1)", + }, + "sapphire": { + "100": "hsla(200, 88%, 90%, 1)", + "1000": "hsla(199, 84%, 15%, 1)", + "200": "hsla(200, 89%, 82%, 1)", + "300": "hsla(200, 90%, 72%, 1)", + "400": "hsla(200, 90%, 65%, 1)", + "50": "hsla(201, 92%, 95%, 1)", + "500": "hsla(198, 90%, 52%, 1)", + "600": "hsla(200, 84%, 44%, 1)", + "700": "hsla(200, 84%, 37%, 1)", + "800": "hsla(200, 84%, 29%, 1)", + "900": "hsla(200, 84%, 22%, 1)", + }, + "topaz": { + "100": "hsla(51, 100%, 80%, 1)", + "1000": "hsla(50, 97%, 19%, 1)", + "200": "hsla(51, 100%, 68%, 1)", + "300": "hsla(51, 100%, 60%, 1)", + "400": "hsla(51, 100%, 50%, 1)", + "50": "hsla(51, 100%, 92%, 1)", + "500": "hsla(50, 100%, 46%, 1)", + "600": "hsla(50, 100%, 40%, 1)", + "700": "hsla(50, 100%, 32%, 1)", + "800": "hsla(50, 100%, 28%, 1)", + "900": "hsla(50, 100%, 24%, 1)", + }, + }, + }, + }, "feedback": { "background": { "information": { @@ -1404,6 +1754,181 @@ exports[`createTheme should create a theme with the correct brand colors 4`] = ` }, "colors": { "onDark": { + "chart": { + "background": { + "categorical": { + "azure": { + "faint": "hsla(227, 69%, 25%, 1)", + "intense": "hsla(220, 100%, 73%, 1)", + "moderate": "hsla(227, 100%, 59%, 1)", + "strong": "hsla(219, 95%, 85%, 1)", + "subtle": "hsla(227, 69%, 42%, 1)", + }, + "cider": { + "faint": "hsla(25, 82%, 20%, 1)", + "intense": "hsla(25, 100%, 72%, 1)", + "moderate": "hsla(25, 90%, 48%, 1)", + "strong": "hsla(25, 100%, 80%, 1)", + "subtle": "hsla(25, 82%, 35%, 1)", + }, + "crimson": { + "faint": "hsla(0, 84%, 25%, 1)", + "intense": "hsla(4, 96%, 79%, 1)", + "moderate": "hsla(4, 77%, 40%, 1)", + "strong": "hsla(3, 97%, 88%, 1)", + "subtle": "hsla(0, 83%, 33%, 1)", + }, + "emerald": { + "faint": "hsla(150, 100%, 11%, 1)", + "intense": "hsla(150, 59%, 73%, 1)", + "moderate": "hsla(150, 100%, 27%, 1)", + "strong": "hsla(150, 59%, 82%, 1)", + "subtle": "hsla(150, 100%, 21%, 1)", + }, + "gray": { + "faint": "hsla(214, 44%, 7%, 1)", + "intense": "hsla(216, 27%, 24%, 1)", + "moderate": "hsla(218, 26%, 18%, 1)", + "strong": "hsla(216, 22%, 36%, 1)", + "subtle": "hsla(216, 30%, 13%, 1)", + }, + "magenta": { + "faint": "hsla(331, 65%, 15%, 1)", + "intense": "hsla(331, 82%, 74%, 1)", + "moderate": "hsla(331, 64%, 34%, 1)", + "strong": "hsla(330, 83%, 84%, 1)", + "subtle": "hsla(331, 65%, 27%, 1)", + }, + "orchid": { + "faint": "hsla(252, 50%, 17%, 1)", + "intense": "hsla(252, 100%, 80%, 1)", + "moderate": "hsla(252, 57%, 54%, 1)", + "strong": "hsla(252, 100%, 88%, 1)", + "subtle": "hsla(251, 49%, 32%, 1)", + }, + "sapphire": { + "faint": "hsla(199, 84%, 15%, 1)", + "intense": "hsla(200, 90%, 72%, 1)", + "moderate": "hsla(200, 84%, 44%, 1)", + "strong": "hsla(200, 89%, 82%, 1)", + "subtle": "hsla(200, 84%, 29%, 1)", + }, + "topaz": { + "faint": "hsla(50, 97%, 19%, 1)", + "intense": "hsla(51, 100%, 60%, 1)", + "moderate": "hsla(50, 100%, 40%, 1)", + "strong": "hsla(51, 100%, 68%, 1)", + "subtle": "hsla(50, 100%, 28%, 1)", + }, + }, + "sequential": { + "azure": { + "100": "hsla(227, 69%, 25%, 1)", + "1000": "hsla(222, 100%, 98%, 1)", + "200": "hsla(227, 69%, 34%, 1)", + "300": "hsla(227, 69%, 42%, 1)", + "400": "hsla(227, 71%, 51%, 1)", + "50": "hsla(227, 69%, 17%, 1)", + "500": "hsla(227, 100%, 59%, 1)", + "600": "hsla(223, 100%, 65%, 1)", + "700": "hsla(220, 100%, 73%, 1)", + "800": "hsla(219, 95%, 85%, 1)", + "900": "hsla(221, 90%, 92%, 1)", + }, + "cider": { + "100": "hsla(25, 80%, 28%, 1)", + "1000": "hsla(24, 100%, 96%, 1)", + "200": "hsla(25, 82%, 35%, 1)", + "300": "hsla(25, 85%, 42%, 1)", + "400": "hsla(25, 90%, 48%, 1)", + "50": "hsla(25, 82%, 20%, 1)", + "500": "hsla(25, 100%, 55%, 1)", + "600": "hsla(25, 100%, 63%, 1)", + "700": "hsla(25, 100%, 72%, 1)", + "800": "hsla(25, 100%, 80%, 1)", + "900": "hsla(25, 100%, 90%, 1)", + }, + "crimson": { + "100": "hsla(0, 84%, 29%, 1)", + "1000": "hsla(0, 100%, 98%, 1)", + "200": "hsla(0, 83%, 33%, 1)", + "300": "hsla(4, 77%, 40%, 1)", + "400": "hsla(4, 74%, 49%, 1)", + "50": "hsla(0, 84%, 25%, 1)", + "500": "hsla(4, 86%, 58%, 1)", + "600": "hsla(4, 93%, 68%, 1)", + "700": "hsla(4, 96%, 79%, 1)", + "800": "hsla(3, 97%, 88%, 1)", + "900": "hsla(4, 93%, 94%, 1)", + }, + "emerald": { + "100": "hsla(150, 100%, 16%, 1)", + "1000": "hsla(152, 60%, 95%, 1)", + "200": "hsla(150, 100%, 21%, 1)", + "300": "hsla(150, 100%, 27%, 1)", + "400": "hsla(150, 100%, 32%, 1)", + "50": "hsla(150, 100%, 11%, 1)", + "500": "hsla(150, 100%, 37%, 1)", + "600": "hsla(150, 59%, 55%, 1)", + "700": "hsla(150, 59%, 73%, 1)", + "800": "hsla(150, 59%, 82%, 1)", + "900": "hsla(151, 57%, 91%, 1)", + }, + "magenta": { + "100": "hsla(331, 65%, 21%, 1)", + "1000": "hsla(330, 78%, 97%, 1)", + "200": "hsla(331, 65%, 27%, 1)", + "300": "hsla(331, 64%, 34%, 1)", + "400": "hsla(331, 64%, 45%, 1)", + "50": "hsla(331, 65%, 15%, 1)", + "500": "hsla(331, 82%, 56%, 1)", + "600": "hsla(331, 82%, 65%, 1)", + "700": "hsla(331, 82%, 74%, 1)", + "800": "hsla(330, 83%, 84%, 1)", + "900": "hsla(331, 83%, 93%, 1)", + }, + "orchid": { + "100": "hsla(252, 50%, 24%, 1)", + "1000": "hsla(249, 100%, 98%, 1)", + "200": "hsla(251, 49%, 32%, 1)", + "300": "hsla(251, 49%, 41%, 1)", + "400": "hsla(252, 57%, 54%, 1)", + "50": "hsla(252, 50%, 17%, 1)", + "500": "hsla(252, 100%, 67%, 1)", + "600": "hsla(252, 100%, 74%, 1)", + "700": "hsla(252, 100%, 80%, 1)", + "800": "hsla(252, 100%, 88%, 1)", + "900": "hsla(252, 100%, 95%, 1)", + }, + "sapphire": { + "100": "hsla(200, 84%, 22%, 1)", + "1000": "hsla(201, 92%, 95%, 1)", + "200": "hsla(200, 84%, 29%, 1)", + "300": "hsla(200, 84%, 37%, 1)", + "400": "hsla(200, 84%, 44%, 1)", + "50": "hsla(199, 84%, 15%, 1)", + "500": "hsla(198, 90%, 52%, 1)", + "600": "hsla(200, 90%, 65%, 1)", + "700": "hsla(200, 90%, 72%, 1)", + "800": "hsla(200, 89%, 82%, 1)", + "900": "hsla(200, 88%, 90%, 1)", + }, + "topaz": { + "100": "hsla(50, 100%, 24%, 1)", + "1000": "hsla(51, 100%, 92%, 1)", + "200": "hsla(50, 100%, 28%, 1)", + "300": "hsla(50, 100%, 32%, 1)", + "400": "hsla(50, 100%, 40%, 1)", + "50": "hsla(50, 97%, 19%, 1)", + "500": "hsla(50, 100%, 46%, 1)", + "600": "hsla(51, 100%, 50%, 1)", + "700": "hsla(51, 100%, 60%, 1)", + "800": "hsla(51, 100%, 68%, 1)", + "900": "hsla(51, 100%, 80%, 1)", + }, + }, + }, + }, "feedback": { "background": { "information": { @@ -1854,6 +2379,181 @@ exports[`createTheme should create a theme with the correct brand colors 4`] = ` "transparent": "hsla(217, 27%, 15%, 0)", }, "onLight": { + "chart": { + "background": { + "categorical": { + "azure": { + "faint": "hsla(222, 100%, 98%, 1)", + "intense": "hsla(227, 71%, 51%, 1)", + "moderate": "hsla(227, 100%, 59%, 1)", + "strong": "hsla(227, 69%, 34%, 1)", + "subtle": "hsla(219, 95%, 85%, 1)", + }, + "cider": { + "faint": "hsla(25, 90%, 48%, 1)", + "intense": "hsla(25, 90%, 48%, 1)", + "moderate": "hsla(25, 100%, 80%, 1)", + "strong": "hsla(25, 82%, 35%, 1)", + "subtle": "hsla(24, 100%, 96%, 1)", + }, + "crimson": { + "faint": "hsla(0, 100%, 98%, 1)", + "intense": "hsla(4, 74%, 49%, 1)", + "moderate": "hsla(4, 74%, 49%, 1)", + "strong": "hsla(0, 83%, 33%, 1)", + "subtle": "hsla(3, 97%, 88%, 1)", + }, + "emerald": { + "faint": "hsla(152, 60%, 95%, 1)", + "intense": "hsla(150, 100%, 32%, 1)", + "moderate": "hsla(150, 100%, 37%, 1)", + "strong": "hsla(150, 100%, 21%, 1)", + "subtle": "hsla(150, 59%, 82%, 1)", + }, + "gray": { + "faint": "hsla(216, 22%, 36%, 1)", + "intense": "hsla(216, 18%, 62%, 1)", + "moderate": "hsla(214, 20%, 84%, 1)", + "strong": "hsla(216, 27%, 24%, 1)", + "subtle": "hsla(214, 20%, 84%, 0.09)", + }, + "magenta": { + "faint": "hsla(331, 64%, 45%, 1)", + "intense": "hsla(331, 64%, 45%, 1)", + "moderate": "hsla(330, 83%, 84%, 1)", + "strong": "hsla(331, 65%, 27%, 1)", + "subtle": "hsla(330, 78%, 97%, 1)", + }, + "orchid": { + "faint": "hsla(252, 57%, 54%, 1)", + "intense": "hsla(252, 100%, 67%, 1)", + "moderate": "hsla(252, 100%, 88%, 1)", + "strong": "hsla(251, 49%, 32%, 1)", + "subtle": "hsla(249, 100%, 98%, 1)", + }, + "sapphire": { + "faint": "hsla(200, 84%, 44%, 1)", + "intense": "hsla(200, 84%, 44%, 1)", + "moderate": "hsla(200, 89%, 82%, 1)", + "strong": "hsla(200, 84%, 29%, 1)", + "subtle": "hsla(201, 92%, 95%, 1)", + }, + "topaz": { + "faint": "hsla(50, 100%, 40%, 1)", + "intense": "hsla(50, 100%, 46%, 1)", + "moderate": "hsla(51, 100%, 68%, 1)", + "strong": "hsla(50, 100%, 28%, 1)", + "subtle": "hsla(51, 100%, 92%, 1)", + }, + }, + "sequential": { + "azure": { + "100": "hsla(221, 90%, 92%, 1)", + "1000": "hsla(227, 69%, 17%, 1)", + "200": "hsla(219, 95%, 85%, 1)", + "300": "hsla(220, 100%, 73%, 1)", + "400": "hsla(223, 100%, 65%, 1)", + "50": "hsla(222, 100%, 98%, 1)", + "500": "hsla(227, 100%, 59%, 1)", + "600": "hsla(227, 71%, 51%, 1)", + "700": "hsla(227, 69%, 42%, 1)", + "800": "hsla(227, 69%, 34%, 1)", + "900": "hsla(227, 69%, 25%, 1)", + }, + "cider": { + "100": "hsla(25, 100%, 90%, 1)", + "1000": "hsla(25, 82%, 20%, 1)", + "200": "hsla(25, 100%, 80%, 1)", + "300": "hsla(25, 100%, 72%, 1)", + "400": "hsla(25, 100%, 63%, 1)", + "50": "hsla(24, 100%, 96%, 1)", + "500": "hsla(25, 100%, 55%, 1)", + "600": "hsla(25, 90%, 48%, 1)", + "700": "hsla(25, 85%, 42%, 1)", + "800": "hsla(25, 82%, 35%, 1)", + "900": "hsla(25, 80%, 28%, 1)", + }, + "crimson": { + "100": "hsla(4, 93%, 94%, 1)", + "1000": "hsla(0, 84%, 25%, 1)", + "200": "hsla(3, 97%, 88%, 1)", + "300": "hsla(4, 96%, 79%, 1)", + "400": "hsla(4, 93%, 68%, 1)", + "50": "hsla(0, 100%, 98%, 1)", + "500": "hsla(4, 86%, 58%, 1)", + "600": "hsla(4, 74%, 49%, 1)", + "700": "hsla(4, 77%, 40%, 1)", + "800": "hsla(0, 83%, 33%, 1)", + "900": "hsla(0, 84%, 29%, 1)", + }, + "emerald": { + "100": "hsla(151, 57%, 91%, 1)", + "1000": "hsla(150, 100%, 11%, 1)", + "200": "hsla(150, 59%, 82%, 1)", + "300": "hsla(150, 59%, 73%, 1)", + "400": "hsla(150, 59%, 55%, 1)", + "50": "hsla(152, 60%, 95%, 1)", + "500": "hsla(150, 100%, 37%, 1)", + "600": "hsla(150, 100%, 32%, 1)", + "700": "hsla(150, 100%, 27%, 1)", + "800": "hsla(150, 100%, 21%, 1)", + "900": "hsla(150, 100%, 16%, 1)", + }, + "magenta": { + "100": "hsla(331, 83%, 93%, 1)", + "1000": "hsla(331, 65%, 15%, 1)", + "200": "hsla(330, 83%, 84%, 1)", + "300": "hsla(331, 82%, 74%, 1)", + "400": "hsla(331, 82%, 65%, 1)", + "50": "hsla(330, 78%, 97%, 1)", + "500": "hsla(331, 82%, 56%, 1)", + "600": "hsla(331, 64%, 45%, 1)", + "700": "hsla(331, 64%, 34%, 1)", + "800": "hsla(331, 65%, 27%, 1)", + "900": "hsla(331, 65%, 21%, 1)", + }, + "orchid": { + "100": "hsla(252, 100%, 95%, 1)", + "1000": "hsla(252, 50%, 17%, 1)", + "200": "hsla(252, 100%, 88%, 1)", + "300": "hsla(252, 100%, 80%, 1)", + "400": "hsla(252, 100%, 74%, 1)", + "50": "hsla(249, 100%, 98%, 1)", + "500": "hsla(252, 100%, 67%, 1)", + "600": "hsla(252, 57%, 54%, 1)", + "700": "hsla(251, 49%, 41%, 1)", + "800": "hsla(251, 49%, 32%, 1)", + "900": "hsla(252, 50%, 24%, 1)", + }, + "sapphire": { + "100": "hsla(200, 88%, 90%, 1)", + "1000": "hsla(199, 84%, 15%, 1)", + "200": "hsla(200, 89%, 82%, 1)", + "300": "hsla(200, 90%, 72%, 1)", + "400": "hsla(200, 90%, 65%, 1)", + "50": "hsla(201, 92%, 95%, 1)", + "500": "hsla(198, 90%, 52%, 1)", + "600": "hsla(200, 84%, 44%, 1)", + "700": "hsla(200, 84%, 37%, 1)", + "800": "hsla(200, 84%, 29%, 1)", + "900": "hsla(200, 84%, 22%, 1)", + }, + "topaz": { + "100": "hsla(51, 100%, 80%, 1)", + "1000": "hsla(50, 97%, 19%, 1)", + "200": "hsla(51, 100%, 68%, 1)", + "300": "hsla(51, 100%, 60%, 1)", + "400": "hsla(51, 100%, 50%, 1)", + "50": "hsla(51, 100%, 92%, 1)", + "500": "hsla(50, 100%, 46%, 1)", + "600": "hsla(50, 100%, 40%, 1)", + "700": "hsla(50, 100%, 32%, 1)", + "800": "hsla(50, 100%, 28%, 1)", + "900": "hsla(50, 100%, 24%, 1)", + }, + }, + }, + }, "feedback": { "background": { "information": { diff --git a/packages/blade/src/tokens/theme/__tests__/__snapshots__/createTheme.web.test.tsx.snap b/packages/blade/src/tokens/theme/__tests__/__snapshots__/createTheme.web.test.tsx.snap index f54680e770f..7d7806c0aaf 100644 --- a/packages/blade/src/tokens/theme/__tests__/__snapshots__/createTheme.web.test.tsx.snap +++ b/packages/blade/src/tokens/theme/__tests__/__snapshots__/createTheme.web.test.tsx.snap @@ -32,6 +32,181 @@ exports[`createTheme should create a theme with the correct brand colors 1`] = ` }, "colors": { "onDark": { + "chart": { + "background": { + "categorical": { + "azure": { + "faint": "hsla(227, 69%, 25%, 1)", + "intense": "hsla(220, 100%, 73%, 1)", + "moderate": "hsla(227, 100%, 59%, 1)", + "strong": "hsla(219, 95%, 85%, 1)", + "subtle": "hsla(227, 69%, 42%, 1)", + }, + "cider": { + "faint": "hsla(25, 82%, 20%, 1)", + "intense": "hsla(25, 100%, 72%, 1)", + "moderate": "hsla(25, 90%, 48%, 1)", + "strong": "hsla(25, 100%, 80%, 1)", + "subtle": "hsla(25, 82%, 35%, 1)", + }, + "crimson": { + "faint": "hsla(0, 84%, 25%, 1)", + "intense": "hsla(4, 96%, 79%, 1)", + "moderate": "hsla(4, 77%, 40%, 1)", + "strong": "hsla(3, 97%, 88%, 1)", + "subtle": "hsla(0, 83%, 33%, 1)", + }, + "emerald": { + "faint": "hsla(150, 100%, 11%, 1)", + "intense": "hsla(150, 59%, 73%, 1)", + "moderate": "hsla(150, 100%, 27%, 1)", + "strong": "hsla(150, 59%, 82%, 1)", + "subtle": "hsla(150, 100%, 21%, 1)", + }, + "gray": { + "faint": "hsla(214, 44%, 7%, 1)", + "intense": "hsla(216, 27%, 24%, 1)", + "moderate": "hsla(218, 26%, 18%, 1)", + "strong": "hsla(216, 22%, 36%, 1)", + "subtle": "hsla(216, 30%, 13%, 1)", + }, + "magenta": { + "faint": "hsla(331, 65%, 15%, 1)", + "intense": "hsla(331, 82%, 74%, 1)", + "moderate": "hsla(331, 64%, 34%, 1)", + "strong": "hsla(330, 83%, 84%, 1)", + "subtle": "hsla(331, 65%, 27%, 1)", + }, + "orchid": { + "faint": "hsla(252, 50%, 17%, 1)", + "intense": "hsla(252, 100%, 80%, 1)", + "moderate": "hsla(252, 57%, 54%, 1)", + "strong": "hsla(252, 100%, 88%, 1)", + "subtle": "hsla(251, 49%, 32%, 1)", + }, + "sapphire": { + "faint": "hsla(199, 84%, 15%, 1)", + "intense": "hsla(200, 90%, 72%, 1)", + "moderate": "hsla(200, 84%, 44%, 1)", + "strong": "hsla(200, 89%, 82%, 1)", + "subtle": "hsla(200, 84%, 29%, 1)", + }, + "topaz": { + "faint": "hsla(50, 97%, 19%, 1)", + "intense": "hsla(51, 100%, 60%, 1)", + "moderate": "hsla(50, 100%, 40%, 1)", + "strong": "hsla(51, 100%, 68%, 1)", + "subtle": "hsla(50, 100%, 28%, 1)", + }, + }, + "sequential": { + "azure": { + "100": "hsla(227, 69%, 25%, 1)", + "1000": "hsla(222, 100%, 98%, 1)", + "200": "hsla(227, 69%, 34%, 1)", + "300": "hsla(227, 69%, 42%, 1)", + "400": "hsla(227, 71%, 51%, 1)", + "50": "hsla(227, 69%, 17%, 1)", + "500": "hsla(227, 100%, 59%, 1)", + "600": "hsla(223, 100%, 65%, 1)", + "700": "hsla(220, 100%, 73%, 1)", + "800": "hsla(219, 95%, 85%, 1)", + "900": "hsla(221, 90%, 92%, 1)", + }, + "cider": { + "100": "hsla(25, 80%, 28%, 1)", + "1000": "hsla(24, 100%, 96%, 1)", + "200": "hsla(25, 82%, 35%, 1)", + "300": "hsla(25, 85%, 42%, 1)", + "400": "hsla(25, 90%, 48%, 1)", + "50": "hsla(25, 82%, 20%, 1)", + "500": "hsla(25, 100%, 55%, 1)", + "600": "hsla(25, 100%, 63%, 1)", + "700": "hsla(25, 100%, 72%, 1)", + "800": "hsla(25, 100%, 80%, 1)", + "900": "hsla(25, 100%, 90%, 1)", + }, + "crimson": { + "100": "hsla(0, 84%, 29%, 1)", + "1000": "hsla(0, 100%, 98%, 1)", + "200": "hsla(0, 83%, 33%, 1)", + "300": "hsla(4, 77%, 40%, 1)", + "400": "hsla(4, 74%, 49%, 1)", + "50": "hsla(0, 84%, 25%, 1)", + "500": "hsla(4, 86%, 58%, 1)", + "600": "hsla(4, 93%, 68%, 1)", + "700": "hsla(4, 96%, 79%, 1)", + "800": "hsla(3, 97%, 88%, 1)", + "900": "hsla(4, 93%, 94%, 1)", + }, + "emerald": { + "100": "hsla(150, 100%, 16%, 1)", + "1000": "hsla(152, 60%, 95%, 1)", + "200": "hsla(150, 100%, 21%, 1)", + "300": "hsla(150, 100%, 27%, 1)", + "400": "hsla(150, 100%, 32%, 1)", + "50": "hsla(150, 100%, 11%, 1)", + "500": "hsla(150, 100%, 37%, 1)", + "600": "hsla(150, 59%, 55%, 1)", + "700": "hsla(150, 59%, 73%, 1)", + "800": "hsla(150, 59%, 82%, 1)", + "900": "hsla(151, 57%, 91%, 1)", + }, + "magenta": { + "100": "hsla(331, 65%, 21%, 1)", + "1000": "hsla(330, 78%, 97%, 1)", + "200": "hsla(331, 65%, 27%, 1)", + "300": "hsla(331, 64%, 34%, 1)", + "400": "hsla(331, 64%, 45%, 1)", + "50": "hsla(331, 65%, 15%, 1)", + "500": "hsla(331, 82%, 56%, 1)", + "600": "hsla(331, 82%, 65%, 1)", + "700": "hsla(331, 82%, 74%, 1)", + "800": "hsla(330, 83%, 84%, 1)", + "900": "hsla(331, 83%, 93%, 1)", + }, + "orchid": { + "100": "hsla(252, 50%, 24%, 1)", + "1000": "hsla(249, 100%, 98%, 1)", + "200": "hsla(251, 49%, 32%, 1)", + "300": "hsla(251, 49%, 41%, 1)", + "400": "hsla(252, 57%, 54%, 1)", + "50": "hsla(252, 50%, 17%, 1)", + "500": "hsla(252, 100%, 67%, 1)", + "600": "hsla(252, 100%, 74%, 1)", + "700": "hsla(252, 100%, 80%, 1)", + "800": "hsla(252, 100%, 88%, 1)", + "900": "hsla(252, 100%, 95%, 1)", + }, + "sapphire": { + "100": "hsla(200, 84%, 22%, 1)", + "1000": "hsla(201, 92%, 95%, 1)", + "200": "hsla(200, 84%, 29%, 1)", + "300": "hsla(200, 84%, 37%, 1)", + "400": "hsla(200, 84%, 44%, 1)", + "50": "hsla(199, 84%, 15%, 1)", + "500": "hsla(198, 90%, 52%, 1)", + "600": "hsla(200, 90%, 65%, 1)", + "700": "hsla(200, 90%, 72%, 1)", + "800": "hsla(200, 89%, 82%, 1)", + "900": "hsla(200, 88%, 90%, 1)", + }, + "topaz": { + "100": "hsla(50, 100%, 24%, 1)", + "1000": "hsla(51, 100%, 92%, 1)", + "200": "hsla(50, 100%, 28%, 1)", + "300": "hsla(50, 100%, 32%, 1)", + "400": "hsla(50, 100%, 40%, 1)", + "50": "hsla(50, 97%, 19%, 1)", + "500": "hsla(50, 100%, 46%, 1)", + "600": "hsla(51, 100%, 50%, 1)", + "700": "hsla(51, 100%, 60%, 1)", + "800": "hsla(51, 100%, 68%, 1)", + "900": "hsla(51, 100%, 80%, 1)", + }, + }, + }, + }, "feedback": { "background": { "information": { @@ -482,6 +657,181 @@ exports[`createTheme should create a theme with the correct brand colors 1`] = ` "transparent": "hsla(217, 27%, 15%, 0)", }, "onLight": { + "chart": { + "background": { + "categorical": { + "azure": { + "faint": "hsla(222, 100%, 98%, 1)", + "intense": "hsla(227, 71%, 51%, 1)", + "moderate": "hsla(227, 100%, 59%, 1)", + "strong": "hsla(227, 69%, 34%, 1)", + "subtle": "hsla(219, 95%, 85%, 1)", + }, + "cider": { + "faint": "hsla(25, 90%, 48%, 1)", + "intense": "hsla(25, 90%, 48%, 1)", + "moderate": "hsla(25, 100%, 80%, 1)", + "strong": "hsla(25, 82%, 35%, 1)", + "subtle": "hsla(24, 100%, 96%, 1)", + }, + "crimson": { + "faint": "hsla(0, 100%, 98%, 1)", + "intense": "hsla(4, 74%, 49%, 1)", + "moderate": "hsla(4, 74%, 49%, 1)", + "strong": "hsla(0, 83%, 33%, 1)", + "subtle": "hsla(3, 97%, 88%, 1)", + }, + "emerald": { + "faint": "hsla(152, 60%, 95%, 1)", + "intense": "hsla(150, 100%, 32%, 1)", + "moderate": "hsla(150, 100%, 37%, 1)", + "strong": "hsla(150, 100%, 21%, 1)", + "subtle": "hsla(150, 59%, 82%, 1)", + }, + "gray": { + "faint": "hsla(216, 22%, 36%, 1)", + "intense": "hsla(216, 18%, 62%, 1)", + "moderate": "hsla(214, 20%, 84%, 1)", + "strong": "hsla(216, 27%, 24%, 1)", + "subtle": "hsla(214, 20%, 84%, 0.09)", + }, + "magenta": { + "faint": "hsla(331, 64%, 45%, 1)", + "intense": "hsla(331, 64%, 45%, 1)", + "moderate": "hsla(330, 83%, 84%, 1)", + "strong": "hsla(331, 65%, 27%, 1)", + "subtle": "hsla(330, 78%, 97%, 1)", + }, + "orchid": { + "faint": "hsla(252, 57%, 54%, 1)", + "intense": "hsla(252, 100%, 67%, 1)", + "moderate": "hsla(252, 100%, 88%, 1)", + "strong": "hsla(251, 49%, 32%, 1)", + "subtle": "hsla(249, 100%, 98%, 1)", + }, + "sapphire": { + "faint": "hsla(200, 84%, 44%, 1)", + "intense": "hsla(200, 84%, 44%, 1)", + "moderate": "hsla(200, 89%, 82%, 1)", + "strong": "hsla(200, 84%, 29%, 1)", + "subtle": "hsla(201, 92%, 95%, 1)", + }, + "topaz": { + "faint": "hsla(50, 100%, 40%, 1)", + "intense": "hsla(50, 100%, 46%, 1)", + "moderate": "hsla(51, 100%, 68%, 1)", + "strong": "hsla(50, 100%, 28%, 1)", + "subtle": "hsla(51, 100%, 92%, 1)", + }, + }, + "sequential": { + "azure": { + "100": "hsla(221, 90%, 92%, 1)", + "1000": "hsla(227, 69%, 17%, 1)", + "200": "hsla(219, 95%, 85%, 1)", + "300": "hsla(220, 100%, 73%, 1)", + "400": "hsla(223, 100%, 65%, 1)", + "50": "hsla(222, 100%, 98%, 1)", + "500": "hsla(227, 100%, 59%, 1)", + "600": "hsla(227, 71%, 51%, 1)", + "700": "hsla(227, 69%, 42%, 1)", + "800": "hsla(227, 69%, 34%, 1)", + "900": "hsla(227, 69%, 25%, 1)", + }, + "cider": { + "100": "hsla(25, 100%, 90%, 1)", + "1000": "hsla(25, 82%, 20%, 1)", + "200": "hsla(25, 100%, 80%, 1)", + "300": "hsla(25, 100%, 72%, 1)", + "400": "hsla(25, 100%, 63%, 1)", + "50": "hsla(24, 100%, 96%, 1)", + "500": "hsla(25, 100%, 55%, 1)", + "600": "hsla(25, 90%, 48%, 1)", + "700": "hsla(25, 85%, 42%, 1)", + "800": "hsla(25, 82%, 35%, 1)", + "900": "hsla(25, 80%, 28%, 1)", + }, + "crimson": { + "100": "hsla(4, 93%, 94%, 1)", + "1000": "hsla(0, 84%, 25%, 1)", + "200": "hsla(3, 97%, 88%, 1)", + "300": "hsla(4, 96%, 79%, 1)", + "400": "hsla(4, 93%, 68%, 1)", + "50": "hsla(0, 100%, 98%, 1)", + "500": "hsla(4, 86%, 58%, 1)", + "600": "hsla(4, 74%, 49%, 1)", + "700": "hsla(4, 77%, 40%, 1)", + "800": "hsla(0, 83%, 33%, 1)", + "900": "hsla(0, 84%, 29%, 1)", + }, + "emerald": { + "100": "hsla(151, 57%, 91%, 1)", + "1000": "hsla(150, 100%, 11%, 1)", + "200": "hsla(150, 59%, 82%, 1)", + "300": "hsla(150, 59%, 73%, 1)", + "400": "hsla(150, 59%, 55%, 1)", + "50": "hsla(152, 60%, 95%, 1)", + "500": "hsla(150, 100%, 37%, 1)", + "600": "hsla(150, 100%, 32%, 1)", + "700": "hsla(150, 100%, 27%, 1)", + "800": "hsla(150, 100%, 21%, 1)", + "900": "hsla(150, 100%, 16%, 1)", + }, + "magenta": { + "100": "hsla(331, 83%, 93%, 1)", + "1000": "hsla(331, 65%, 15%, 1)", + "200": "hsla(330, 83%, 84%, 1)", + "300": "hsla(331, 82%, 74%, 1)", + "400": "hsla(331, 82%, 65%, 1)", + "50": "hsla(330, 78%, 97%, 1)", + "500": "hsla(331, 82%, 56%, 1)", + "600": "hsla(331, 64%, 45%, 1)", + "700": "hsla(331, 64%, 34%, 1)", + "800": "hsla(331, 65%, 27%, 1)", + "900": "hsla(331, 65%, 21%, 1)", + }, + "orchid": { + "100": "hsla(252, 100%, 95%, 1)", + "1000": "hsla(252, 50%, 17%, 1)", + "200": "hsla(252, 100%, 88%, 1)", + "300": "hsla(252, 100%, 80%, 1)", + "400": "hsla(252, 100%, 74%, 1)", + "50": "hsla(249, 100%, 98%, 1)", + "500": "hsla(252, 100%, 67%, 1)", + "600": "hsla(252, 57%, 54%, 1)", + "700": "hsla(251, 49%, 41%, 1)", + "800": "hsla(251, 49%, 32%, 1)", + "900": "hsla(252, 50%, 24%, 1)", + }, + "sapphire": { + "100": "hsla(200, 88%, 90%, 1)", + "1000": "hsla(199, 84%, 15%, 1)", + "200": "hsla(200, 89%, 82%, 1)", + "300": "hsla(200, 90%, 72%, 1)", + "400": "hsla(200, 90%, 65%, 1)", + "50": "hsla(201, 92%, 95%, 1)", + "500": "hsla(198, 90%, 52%, 1)", + "600": "hsla(200, 84%, 44%, 1)", + "700": "hsla(200, 84%, 37%, 1)", + "800": "hsla(200, 84%, 29%, 1)", + "900": "hsla(200, 84%, 22%, 1)", + }, + "topaz": { + "100": "hsla(51, 100%, 80%, 1)", + "1000": "hsla(50, 97%, 19%, 1)", + "200": "hsla(51, 100%, 68%, 1)", + "300": "hsla(51, 100%, 60%, 1)", + "400": "hsla(51, 100%, 50%, 1)", + "50": "hsla(51, 100%, 92%, 1)", + "500": "hsla(50, 100%, 46%, 1)", + "600": "hsla(50, 100%, 40%, 1)", + "700": "hsla(50, 100%, 32%, 1)", + "800": "hsla(50, 100%, 28%, 1)", + "900": "hsla(50, 100%, 24%, 1)", + }, + }, + }, + }, "feedback": { "background": { "information": { @@ -1297,6 +1647,181 @@ exports[`createTheme should create a theme with the correct brand colors 4`] = ` }, "colors": { "onDark": { + "chart": { + "background": { + "categorical": { + "azure": { + "faint": "hsla(227, 69%, 25%, 1)", + "intense": "hsla(220, 100%, 73%, 1)", + "moderate": "hsla(227, 100%, 59%, 1)", + "strong": "hsla(219, 95%, 85%, 1)", + "subtle": "hsla(227, 69%, 42%, 1)", + }, + "cider": { + "faint": "hsla(25, 82%, 20%, 1)", + "intense": "hsla(25, 100%, 72%, 1)", + "moderate": "hsla(25, 90%, 48%, 1)", + "strong": "hsla(25, 100%, 80%, 1)", + "subtle": "hsla(25, 82%, 35%, 1)", + }, + "crimson": { + "faint": "hsla(0, 84%, 25%, 1)", + "intense": "hsla(4, 96%, 79%, 1)", + "moderate": "hsla(4, 77%, 40%, 1)", + "strong": "hsla(3, 97%, 88%, 1)", + "subtle": "hsla(0, 83%, 33%, 1)", + }, + "emerald": { + "faint": "hsla(150, 100%, 11%, 1)", + "intense": "hsla(150, 59%, 73%, 1)", + "moderate": "hsla(150, 100%, 27%, 1)", + "strong": "hsla(150, 59%, 82%, 1)", + "subtle": "hsla(150, 100%, 21%, 1)", + }, + "gray": { + "faint": "hsla(214, 44%, 7%, 1)", + "intense": "hsla(216, 27%, 24%, 1)", + "moderate": "hsla(218, 26%, 18%, 1)", + "strong": "hsla(216, 22%, 36%, 1)", + "subtle": "hsla(216, 30%, 13%, 1)", + }, + "magenta": { + "faint": "hsla(331, 65%, 15%, 1)", + "intense": "hsla(331, 82%, 74%, 1)", + "moderate": "hsla(331, 64%, 34%, 1)", + "strong": "hsla(330, 83%, 84%, 1)", + "subtle": "hsla(331, 65%, 27%, 1)", + }, + "orchid": { + "faint": "hsla(252, 50%, 17%, 1)", + "intense": "hsla(252, 100%, 80%, 1)", + "moderate": "hsla(252, 57%, 54%, 1)", + "strong": "hsla(252, 100%, 88%, 1)", + "subtle": "hsla(251, 49%, 32%, 1)", + }, + "sapphire": { + "faint": "hsla(199, 84%, 15%, 1)", + "intense": "hsla(200, 90%, 72%, 1)", + "moderate": "hsla(200, 84%, 44%, 1)", + "strong": "hsla(200, 89%, 82%, 1)", + "subtle": "hsla(200, 84%, 29%, 1)", + }, + "topaz": { + "faint": "hsla(50, 97%, 19%, 1)", + "intense": "hsla(51, 100%, 60%, 1)", + "moderate": "hsla(50, 100%, 40%, 1)", + "strong": "hsla(51, 100%, 68%, 1)", + "subtle": "hsla(50, 100%, 28%, 1)", + }, + }, + "sequential": { + "azure": { + "100": "hsla(227, 69%, 25%, 1)", + "1000": "hsla(222, 100%, 98%, 1)", + "200": "hsla(227, 69%, 34%, 1)", + "300": "hsla(227, 69%, 42%, 1)", + "400": "hsla(227, 71%, 51%, 1)", + "50": "hsla(227, 69%, 17%, 1)", + "500": "hsla(227, 100%, 59%, 1)", + "600": "hsla(223, 100%, 65%, 1)", + "700": "hsla(220, 100%, 73%, 1)", + "800": "hsla(219, 95%, 85%, 1)", + "900": "hsla(221, 90%, 92%, 1)", + }, + "cider": { + "100": "hsla(25, 80%, 28%, 1)", + "1000": "hsla(24, 100%, 96%, 1)", + "200": "hsla(25, 82%, 35%, 1)", + "300": "hsla(25, 85%, 42%, 1)", + "400": "hsla(25, 90%, 48%, 1)", + "50": "hsla(25, 82%, 20%, 1)", + "500": "hsla(25, 100%, 55%, 1)", + "600": "hsla(25, 100%, 63%, 1)", + "700": "hsla(25, 100%, 72%, 1)", + "800": "hsla(25, 100%, 80%, 1)", + "900": "hsla(25, 100%, 90%, 1)", + }, + "crimson": { + "100": "hsla(0, 84%, 29%, 1)", + "1000": "hsla(0, 100%, 98%, 1)", + "200": "hsla(0, 83%, 33%, 1)", + "300": "hsla(4, 77%, 40%, 1)", + "400": "hsla(4, 74%, 49%, 1)", + "50": "hsla(0, 84%, 25%, 1)", + "500": "hsla(4, 86%, 58%, 1)", + "600": "hsla(4, 93%, 68%, 1)", + "700": "hsla(4, 96%, 79%, 1)", + "800": "hsla(3, 97%, 88%, 1)", + "900": "hsla(4, 93%, 94%, 1)", + }, + "emerald": { + "100": "hsla(150, 100%, 16%, 1)", + "1000": "hsla(152, 60%, 95%, 1)", + "200": "hsla(150, 100%, 21%, 1)", + "300": "hsla(150, 100%, 27%, 1)", + "400": "hsla(150, 100%, 32%, 1)", + "50": "hsla(150, 100%, 11%, 1)", + "500": "hsla(150, 100%, 37%, 1)", + "600": "hsla(150, 59%, 55%, 1)", + "700": "hsla(150, 59%, 73%, 1)", + "800": "hsla(150, 59%, 82%, 1)", + "900": "hsla(151, 57%, 91%, 1)", + }, + "magenta": { + "100": "hsla(331, 65%, 21%, 1)", + "1000": "hsla(330, 78%, 97%, 1)", + "200": "hsla(331, 65%, 27%, 1)", + "300": "hsla(331, 64%, 34%, 1)", + "400": "hsla(331, 64%, 45%, 1)", + "50": "hsla(331, 65%, 15%, 1)", + "500": "hsla(331, 82%, 56%, 1)", + "600": "hsla(331, 82%, 65%, 1)", + "700": "hsla(331, 82%, 74%, 1)", + "800": "hsla(330, 83%, 84%, 1)", + "900": "hsla(331, 83%, 93%, 1)", + }, + "orchid": { + "100": "hsla(252, 50%, 24%, 1)", + "1000": "hsla(249, 100%, 98%, 1)", + "200": "hsla(251, 49%, 32%, 1)", + "300": "hsla(251, 49%, 41%, 1)", + "400": "hsla(252, 57%, 54%, 1)", + "50": "hsla(252, 50%, 17%, 1)", + "500": "hsla(252, 100%, 67%, 1)", + "600": "hsla(252, 100%, 74%, 1)", + "700": "hsla(252, 100%, 80%, 1)", + "800": "hsla(252, 100%, 88%, 1)", + "900": "hsla(252, 100%, 95%, 1)", + }, + "sapphire": { + "100": "hsla(200, 84%, 22%, 1)", + "1000": "hsla(201, 92%, 95%, 1)", + "200": "hsla(200, 84%, 29%, 1)", + "300": "hsla(200, 84%, 37%, 1)", + "400": "hsla(200, 84%, 44%, 1)", + "50": "hsla(199, 84%, 15%, 1)", + "500": "hsla(198, 90%, 52%, 1)", + "600": "hsla(200, 90%, 65%, 1)", + "700": "hsla(200, 90%, 72%, 1)", + "800": "hsla(200, 89%, 82%, 1)", + "900": "hsla(200, 88%, 90%, 1)", + }, + "topaz": { + "100": "hsla(50, 100%, 24%, 1)", + "1000": "hsla(51, 100%, 92%, 1)", + "200": "hsla(50, 100%, 28%, 1)", + "300": "hsla(50, 100%, 32%, 1)", + "400": "hsla(50, 100%, 40%, 1)", + "50": "hsla(50, 97%, 19%, 1)", + "500": "hsla(50, 100%, 46%, 1)", + "600": "hsla(51, 100%, 50%, 1)", + "700": "hsla(51, 100%, 60%, 1)", + "800": "hsla(51, 100%, 68%, 1)", + "900": "hsla(51, 100%, 80%, 1)", + }, + }, + }, + }, "feedback": { "background": { "information": { @@ -1747,6 +2272,181 @@ exports[`createTheme should create a theme with the correct brand colors 4`] = ` "transparent": "hsla(217, 27%, 15%, 0)", }, "onLight": { + "chart": { + "background": { + "categorical": { + "azure": { + "faint": "hsla(222, 100%, 98%, 1)", + "intense": "hsla(227, 71%, 51%, 1)", + "moderate": "hsla(227, 100%, 59%, 1)", + "strong": "hsla(227, 69%, 34%, 1)", + "subtle": "hsla(219, 95%, 85%, 1)", + }, + "cider": { + "faint": "hsla(25, 90%, 48%, 1)", + "intense": "hsla(25, 90%, 48%, 1)", + "moderate": "hsla(25, 100%, 80%, 1)", + "strong": "hsla(25, 82%, 35%, 1)", + "subtle": "hsla(24, 100%, 96%, 1)", + }, + "crimson": { + "faint": "hsla(0, 100%, 98%, 1)", + "intense": "hsla(4, 74%, 49%, 1)", + "moderate": "hsla(4, 74%, 49%, 1)", + "strong": "hsla(0, 83%, 33%, 1)", + "subtle": "hsla(3, 97%, 88%, 1)", + }, + "emerald": { + "faint": "hsla(152, 60%, 95%, 1)", + "intense": "hsla(150, 100%, 32%, 1)", + "moderate": "hsla(150, 100%, 37%, 1)", + "strong": "hsla(150, 100%, 21%, 1)", + "subtle": "hsla(150, 59%, 82%, 1)", + }, + "gray": { + "faint": "hsla(216, 22%, 36%, 1)", + "intense": "hsla(216, 18%, 62%, 1)", + "moderate": "hsla(214, 20%, 84%, 1)", + "strong": "hsla(216, 27%, 24%, 1)", + "subtle": "hsla(214, 20%, 84%, 0.09)", + }, + "magenta": { + "faint": "hsla(331, 64%, 45%, 1)", + "intense": "hsla(331, 64%, 45%, 1)", + "moderate": "hsla(330, 83%, 84%, 1)", + "strong": "hsla(331, 65%, 27%, 1)", + "subtle": "hsla(330, 78%, 97%, 1)", + }, + "orchid": { + "faint": "hsla(252, 57%, 54%, 1)", + "intense": "hsla(252, 100%, 67%, 1)", + "moderate": "hsla(252, 100%, 88%, 1)", + "strong": "hsla(251, 49%, 32%, 1)", + "subtle": "hsla(249, 100%, 98%, 1)", + }, + "sapphire": { + "faint": "hsla(200, 84%, 44%, 1)", + "intense": "hsla(200, 84%, 44%, 1)", + "moderate": "hsla(200, 89%, 82%, 1)", + "strong": "hsla(200, 84%, 29%, 1)", + "subtle": "hsla(201, 92%, 95%, 1)", + }, + "topaz": { + "faint": "hsla(50, 100%, 40%, 1)", + "intense": "hsla(50, 100%, 46%, 1)", + "moderate": "hsla(51, 100%, 68%, 1)", + "strong": "hsla(50, 100%, 28%, 1)", + "subtle": "hsla(51, 100%, 92%, 1)", + }, + }, + "sequential": { + "azure": { + "100": "hsla(221, 90%, 92%, 1)", + "1000": "hsla(227, 69%, 17%, 1)", + "200": "hsla(219, 95%, 85%, 1)", + "300": "hsla(220, 100%, 73%, 1)", + "400": "hsla(223, 100%, 65%, 1)", + "50": "hsla(222, 100%, 98%, 1)", + "500": "hsla(227, 100%, 59%, 1)", + "600": "hsla(227, 71%, 51%, 1)", + "700": "hsla(227, 69%, 42%, 1)", + "800": "hsla(227, 69%, 34%, 1)", + "900": "hsla(227, 69%, 25%, 1)", + }, + "cider": { + "100": "hsla(25, 100%, 90%, 1)", + "1000": "hsla(25, 82%, 20%, 1)", + "200": "hsla(25, 100%, 80%, 1)", + "300": "hsla(25, 100%, 72%, 1)", + "400": "hsla(25, 100%, 63%, 1)", + "50": "hsla(24, 100%, 96%, 1)", + "500": "hsla(25, 100%, 55%, 1)", + "600": "hsla(25, 90%, 48%, 1)", + "700": "hsla(25, 85%, 42%, 1)", + "800": "hsla(25, 82%, 35%, 1)", + "900": "hsla(25, 80%, 28%, 1)", + }, + "crimson": { + "100": "hsla(4, 93%, 94%, 1)", + "1000": "hsla(0, 84%, 25%, 1)", + "200": "hsla(3, 97%, 88%, 1)", + "300": "hsla(4, 96%, 79%, 1)", + "400": "hsla(4, 93%, 68%, 1)", + "50": "hsla(0, 100%, 98%, 1)", + "500": "hsla(4, 86%, 58%, 1)", + "600": "hsla(4, 74%, 49%, 1)", + "700": "hsla(4, 77%, 40%, 1)", + "800": "hsla(0, 83%, 33%, 1)", + "900": "hsla(0, 84%, 29%, 1)", + }, + "emerald": { + "100": "hsla(151, 57%, 91%, 1)", + "1000": "hsla(150, 100%, 11%, 1)", + "200": "hsla(150, 59%, 82%, 1)", + "300": "hsla(150, 59%, 73%, 1)", + "400": "hsla(150, 59%, 55%, 1)", + "50": "hsla(152, 60%, 95%, 1)", + "500": "hsla(150, 100%, 37%, 1)", + "600": "hsla(150, 100%, 32%, 1)", + "700": "hsla(150, 100%, 27%, 1)", + "800": "hsla(150, 100%, 21%, 1)", + "900": "hsla(150, 100%, 16%, 1)", + }, + "magenta": { + "100": "hsla(331, 83%, 93%, 1)", + "1000": "hsla(331, 65%, 15%, 1)", + "200": "hsla(330, 83%, 84%, 1)", + "300": "hsla(331, 82%, 74%, 1)", + "400": "hsla(331, 82%, 65%, 1)", + "50": "hsla(330, 78%, 97%, 1)", + "500": "hsla(331, 82%, 56%, 1)", + "600": "hsla(331, 64%, 45%, 1)", + "700": "hsla(331, 64%, 34%, 1)", + "800": "hsla(331, 65%, 27%, 1)", + "900": "hsla(331, 65%, 21%, 1)", + }, + "orchid": { + "100": "hsla(252, 100%, 95%, 1)", + "1000": "hsla(252, 50%, 17%, 1)", + "200": "hsla(252, 100%, 88%, 1)", + "300": "hsla(252, 100%, 80%, 1)", + "400": "hsla(252, 100%, 74%, 1)", + "50": "hsla(249, 100%, 98%, 1)", + "500": "hsla(252, 100%, 67%, 1)", + "600": "hsla(252, 57%, 54%, 1)", + "700": "hsla(251, 49%, 41%, 1)", + "800": "hsla(251, 49%, 32%, 1)", + "900": "hsla(252, 50%, 24%, 1)", + }, + "sapphire": { + "100": "hsla(200, 88%, 90%, 1)", + "1000": "hsla(199, 84%, 15%, 1)", + "200": "hsla(200, 89%, 82%, 1)", + "300": "hsla(200, 90%, 72%, 1)", + "400": "hsla(200, 90%, 65%, 1)", + "50": "hsla(201, 92%, 95%, 1)", + "500": "hsla(198, 90%, 52%, 1)", + "600": "hsla(200, 84%, 44%, 1)", + "700": "hsla(200, 84%, 37%, 1)", + "800": "hsla(200, 84%, 29%, 1)", + "900": "hsla(200, 84%, 22%, 1)", + }, + "topaz": { + "100": "hsla(51, 100%, 80%, 1)", + "1000": "hsla(50, 97%, 19%, 1)", + "200": "hsla(51, 100%, 68%, 1)", + "300": "hsla(51, 100%, 60%, 1)", + "400": "hsla(51, 100%, 50%, 1)", + "50": "hsla(51, 100%, 92%, 1)", + "500": "hsla(50, 100%, 46%, 1)", + "600": "hsla(50, 100%, 40%, 1)", + "700": "hsla(50, 100%, 32%, 1)", + "800": "hsla(50, 100%, 28%, 1)", + "900": "hsla(50, 100%, 24%, 1)", + }, + }, + }, + }, "feedback": { "background": { "information": { From eaaf45f055b9c23443864aa583dd2dcc30bfaa98 Mon Sep 17 00:00:00 2001 From: tewarig Date: Mon, 8 Sep 2025 15:29:27 +0530 Subject: [PATCH 051/105] chore: add review notes --- .../Charts/BaseChartComponents/BaseChartComponents.tsx | 1 + packages/blade/src/components/Charts/utils/useColorTheme.tsx | 2 +- packages/blade/src/tokens/theme/bladeTheme.ts | 1 + packages/blade/src/tokens/theme/theme.ts | 2 +- 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/blade/src/components/Charts/BaseChartComponents/BaseChartComponents.tsx b/packages/blade/src/components/Charts/BaseChartComponents/BaseChartComponents.tsx index 2eead17ad39..a4a53960b02 100644 --- a/packages/blade/src/components/Charts/BaseChartComponents/BaseChartComponents.tsx +++ b/packages/blade/src/components/Charts/BaseChartComponents/BaseChartComponents.tsx @@ -116,6 +116,7 @@ export const CartesianGrid: React.FC = (props) => { ); }; +//REVIEW_NOTES: this might change export const ChartToolTip: React.FC = (props) => { const { theme } = useTheme(); diff --git a/packages/blade/src/components/Charts/utils/useColorTheme.tsx b/packages/blade/src/components/Charts/utils/useColorTheme.tsx index eca06b1b349..88362947724 100644 --- a/packages/blade/src/components/Charts/utils/useColorTheme.tsx +++ b/packages/blade/src/components/Charts/utils/useColorTheme.tsx @@ -5,7 +5,7 @@ const useChartsColorTheme = ({ }: { colorTheme: 'default' | 'informational'; }): string[] => { - console.log('colorTheme', colorTheme); + //REVIEW_NOTES: THIS will change const { theme } = useTheme(); const defaultColorThemeArray = [ theme.colors.chart.background.categorical.azure.strong, diff --git a/packages/blade/src/tokens/theme/bladeTheme.ts b/packages/blade/src/tokens/theme/bladeTheme.ts index 38b87045c7b..fb4635a8141 100644 --- a/packages/blade/src/tokens/theme/bladeTheme.ts +++ b/packages/blade/src/tokens/theme/bladeTheme.ts @@ -1070,6 +1070,7 @@ const colors: ColorsWithModes = { intense: globalColors.neutral.blueGrayDark.a100, }, }, + //REVIEW_NOTES: this might change chart: { background: { categorical: { diff --git a/packages/blade/src/tokens/theme/theme.ts b/packages/blade/src/tokens/theme/theme.ts index 98057d0e9bd..ebbcf7396bd 100644 --- a/packages/blade/src/tokens/theme/theme.ts +++ b/packages/blade/src/tokens/theme/theme.ts @@ -29,7 +29,7 @@ export type ChartCategoricalEmphasis = Pick Date: Mon, 8 Sep 2025 15:36:00 +0530 Subject: [PATCH 052/105] fix: change to vairables --- .../Charts/BaseChartComponents/BaseChartComponents.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/blade/src/components/Charts/BaseChartComponents/BaseChartComponents.tsx b/packages/blade/src/components/Charts/BaseChartComponents/BaseChartComponents.tsx index a4a53960b02..f3f0e1d9980 100644 --- a/packages/blade/src/components/Charts/BaseChartComponents/BaseChartComponents.tsx +++ b/packages/blade/src/components/Charts/BaseChartComponents/BaseChartComponents.tsx @@ -182,7 +182,6 @@ const CustomSquareLegend = (props: { }} /> {/* Legend text with custom color and size */} - {/* {entry.value}{' '} */} {entry.value} @@ -229,8 +228,9 @@ const CustomReferenceLabel = ({ const RECT_WIDTH = 80; const RECT_HEIGHT = 30; + const TEXT_BASELINE = 15; const rectX = x + width - RECT_WIDTH; - const rectY = y - 15; + const rectY = y - TEXT_BASELINE; // Padding for text inside the rectangle (4px vertical, 8px horizontal) const PADDING_VERTICAL = 4; @@ -238,13 +238,13 @@ const CustomReferenceLabel = ({ // Text position with padding inside the rectangle const textX = rectX + PADDING_HORIZONTAL + (RECT_WIDTH - PADDING_HORIZONTAL * 2) / 2; - const textY = rectY + PADDING_VERTICAL + 15; // +15 for text baseline + const textY = rectY + PADDING_VERTICAL + TEXT_BASELINE; // +15 for text baseline return ( Date: Mon, 8 Sep 2025 15:45:04 +0530 Subject: [PATCH 053/105] chore: more changes --- .../BaseChartComponents.tsx | 7 +++++-- .../Charts/LineCharts/LineCharts.stories.tsx | 18 ++++++++++++++++++ packages/blade/src/tokens/theme/theme.ts | 2 +- 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/packages/blade/src/components/Charts/BaseChartComponents/BaseChartComponents.tsx b/packages/blade/src/components/Charts/BaseChartComponents/BaseChartComponents.tsx index f3f0e1d9980..1563733e2b9 100644 --- a/packages/blade/src/components/Charts/BaseChartComponents/BaseChartComponents.tsx +++ b/packages/blade/src/components/Charts/BaseChartComponents/BaseChartComponents.tsx @@ -39,6 +39,9 @@ export type CartesianGridProps = ComponentProps; export const XAxis: React.FC = (props) => { const { theme } = useTheme(); + const X_OFFSET = 32; + const Y_OFFSET = 14.5; + const TEXT_BASELINE = 24; return ( = (props) => { stroke={theme.colors.surface.border.gray.muted} label={({ viewBox }: { viewBox: { x: number; y: number; width: number } }) => ( = () => { ); }; +// Line Chart with Default Color Theme export const LineChartWithDefaultColorTheme: StoryFn = () => { return (
      @@ -261,9 +262,26 @@ export const LineChartWithDefaultColorTheme: StoryFn = () => { ); }; +//Line Chart with X and Y axis labels +export const LineChartWithXAndYAxisLabels: StoryFn = () => { + return ( +
      + + + + + + + + +
      + ); +}; + SimpleLineChart.storyName = 'Simple Line Chart'; TinyLineChart.storyName = 'Tiny Line Chart'; ForecastLineChart.storyName = 'Forecast Line Chart'; LineChartConnectNulls.storyName = 'Line Chart (Connect Nulls)'; SteppedLineChart.storyName = 'Stepped Line Chart'; LineChartWithDefaultColorTheme.storyName = 'Line Chart with Default Color Theme'; +LineChartWithXAndYAxisLabels.storyName = 'Line Chart with X and Y axis labels'; diff --git a/packages/blade/src/tokens/theme/theme.ts b/packages/blade/src/tokens/theme/theme.ts index ebbcf7396bd..53081b19461 100644 --- a/packages/blade/src/tokens/theme/theme.ts +++ b/packages/blade/src/tokens/theme/theme.ts @@ -29,7 +29,7 @@ export type ChartCategoricalEmphasis = Pick Date: Mon, 8 Sep 2025 15:47:57 +0530 Subject: [PATCH 054/105] Create calm-peas-work.md --- .changeset/calm-peas-work.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/calm-peas-work.md diff --git a/.changeset/calm-peas-work.md b/.changeset/calm-peas-work.md new file mode 100644 index 00000000000..18a4569d493 --- /dev/null +++ b/.changeset/calm-peas-work.md @@ -0,0 +1,5 @@ +--- +"@razorpay/blade": patch +--- + +feat: add line charts From 23e3cc08911e96f776ce9ad52a346eb1c273813a Mon Sep 17 00:00:00 2001 From: tewarig Date: Mon, 8 Sep 2025 17:18:44 +0530 Subject: [PATCH 055/105] chore: more code changes --- .../src/components/Charts/LineCharts/LineCharts.stories.tsx | 2 +- .../blade/src/components/Charts/LineCharts/lineCharts.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx b/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx index ad0e6ba1b5d..21aceed72f5 100644 --- a/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx +++ b/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx @@ -283,5 +283,5 @@ TinyLineChart.storyName = 'Tiny Line Chart'; ForecastLineChart.storyName = 'Forecast Line Chart'; LineChartConnectNulls.storyName = 'Line Chart (Connect Nulls)'; SteppedLineChart.storyName = 'Stepped Line Chart'; -LineChartWithDefaultColorTheme.storyName = 'Line Chart with Default Color Theme'; +LineChartWithDefaultColorTheme.storyName = 'Line Chart with Color Theme'; LineChartWithXAndYAxisLabels.storyName = 'Line Chart with X and Y axis labels'; diff --git a/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx b/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx index 2f9b90eaaec..b5d50a20b29 100644 --- a/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx +++ b/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx @@ -63,8 +63,8 @@ export const Line: React.FC = ({ ...props }) => { const { theme } = useTheme(); - const colorIndex = useChartsColorTheme({ colorTheme: _colorTheme ?? 'default' }); - const colorToken = color ? getIn(theme.colors, color) : colorIndex[_index ?? 0]; + const themeColors = useChartsColorTheme({ colorTheme: _colorTheme ?? 'default' }); + const colorToken = color ? getIn(theme.colors, color) : themeColors[_index ?? 0]; const strokeDasharray = strokeStyle === 'dashed' ? '5 5' : strokeStyle === 'dotted' ? '2 2' : undefined; From f5dae271f22eb0f166d211e3344c002b012f1952 Mon Sep 17 00:00:00 2001 From: tewarig Date: Mon, 8 Sep 2025 17:23:15 +0530 Subject: [PATCH 056/105] fix: update name --- .../blade/src/components/Charts/LineCharts/lineCharts.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx b/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx index b5d50a20b29..c596cb6fc2d 100644 --- a/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx +++ b/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx @@ -97,7 +97,7 @@ export const LineChart: React.FC = ({ colorTheme = 'default', ...props }) => { - const childrenWithIndex = React.useMemo(() => { + const lineChartModifiedChildrens = React.useMemo(() => { let LineChartIndex = 0; return React.Children.map(children, (child) => { if (React.isValidElement(child) && child.type === Line) { @@ -113,7 +113,7 @@ export const LineChart: React.FC = ({ return ( - {childrenWithIndex} + {lineChartModifiedChildrens} ); From dbf39126c9fc47d97bc5500018cf4cc4d8042f20 Mon Sep 17 00:00:00 2001 From: tewarig Date: Mon, 8 Sep 2025 17:41:11 +0530 Subject: [PATCH 057/105] feat: update area charts to support color tokens --- .../Charts/AreaChart/AreaChart.stories.tsx | 19 ++++ .../Charts/AreaChart/AreaCharts.tsx | 94 ++++++++++++------- 2 files changed, 81 insertions(+), 32 deletions(-) diff --git a/packages/blade/src/components/Charts/AreaChart/AreaChart.stories.tsx b/packages/blade/src/components/Charts/AreaChart/AreaChart.stories.tsx index 4df3362ea1e..593cfcee89d 100644 --- a/packages/blade/src/components/Charts/AreaChart/AreaChart.stories.tsx +++ b/packages/blade/src/components/Charts/AreaChart/AreaChart.stories.tsx @@ -276,3 +276,22 @@ StackedAreaChart.storyName = 'Stacked Area Chart'; AreaChartConnectNulls.storyName = 'Area Chart (Connect Nulls)'; TinyAreaChart.storyName = 'Tiny Area Chart'; AreaChartWithReferenceLine.storyName = 'Area Chart with Reference Line'; + +// Area Chart with Default Color Theme +export const AreaChartWithDefaultColorTheme: StoryFn = () => { + return ( +
      + + + + + + + + + +
      + ); +}; + +AreaChartWithDefaultColorTheme.storyName = 'Area Chart with Color Theme'; diff --git a/packages/blade/src/components/Charts/AreaChart/AreaCharts.tsx b/packages/blade/src/components/Charts/AreaChart/AreaCharts.tsx index 757e1ecfcc1..d2c253c280c 100644 --- a/packages/blade/src/components/Charts/AreaChart/AreaCharts.tsx +++ b/packages/blade/src/components/Charts/AreaChart/AreaCharts.tsx @@ -8,6 +8,7 @@ import { } from 'recharts'; import type { AreaProps as RechartAreaProps } from 'recharts'; +import { useChartsColorTheme } from '../utils'; import { useTheme } from '~components/BladeProvider'; import type { Theme } from '~components/BladeProvider'; import type { StyledPropsBlade } from '~components/Box/styledProps'; @@ -31,12 +32,54 @@ export interface AreaProps { color?: BladeColorToken; dot?: RechartAreaProps['dot']; activeDot?: RechartAreaProps['activeDot']; + /** + * @private + */ + _index?: number; // Add this for internal use + /** + * @private + */ + _colorTheme?: 'default' | 'informational'; } +export const Area: React.FC = ({ + color, + type = 'monotone', + connectNulls = false, + showLegend = true, + stackId = 1, + dot = false, + activeDot = false, + _index, + _colorTheme, + ...props +}) => { + const { theme } = useTheme(); + const themeColors = useChartsColorTheme({ colorTheme: _colorTheme ?? 'default' }); + const colorToken = color ? getIn(theme.colors, color) : themeColors[_index ?? 0]; + + return ( + + ); +}; + // TypeScript prop types export type AreaChartProps = Omit, 'margin'> & StyledPropsBlade & { children?: React.ReactNode; + colorTheme?: 'default' | 'informational'; }; // Styled wrapper for AreaChart with predefined margins @@ -45,7 +88,11 @@ const StyledAreaChart = styled(RechartsAreaChart)<{ theme: Theme }>` `; // Main components -export const AreaChart: React.FC = ({ children, ...props }) => { +export const AreaChart: React.FC = ({ + children, + colorTheme = 'default', + ...props +}) => { const { theme } = useTheme(); const styledProps = getStyledProps(props); @@ -57,43 +104,26 @@ export const AreaChart: React.FC = ({ children, ...props }) => { left: 16, }; + const modifiedChildren = React.useMemo(() => { + let AreaChartIndex = 0; + return React.Children.map(children, (child) => { + if (React.isValidElement(child) && child.type === Area) { + return React.cloneElement(child, { + _index: AreaChartIndex++, + _colorTheme: colorTheme, + } as Partial); + } + return child; + }); + }, [children, colorTheme]); + return ( - {children} + {modifiedChildren} ); }; - -export const Area: React.FC = ({ - color, - type = 'monotone', - connectNulls = false, - showLegend = true, - stackId = 1, - dot = false, - activeDot = false, - ...props -}) => { - const { theme } = useTheme(); - const colorToken = color ? getIn(theme.colors, color) : undefined; - - return ( - - ); -}; From c4f1e3a80f19f64fcc72d09097381c3ce01103f7 Mon Sep 17 00:00:00 2001 From: tewarig Date: Mon, 8 Sep 2025 23:04:01 +0530 Subject: [PATCH 058/105] feat: update bar charts for review --- .../Charts/BarCharts/BarChartContext.ts | 2 + .../Charts/BarCharts/BarCharts.stories.tsx | 29 ++- .../components/Charts/BarCharts/BarCharts.tsx | 166 +++++------------- 3 files changed, 72 insertions(+), 125 deletions(-) diff --git a/packages/blade/src/components/Charts/BarCharts/BarChartContext.ts b/packages/blade/src/components/Charts/BarCharts/BarChartContext.ts index 3b50dee8cb0..b787a11f16f 100644 --- a/packages/blade/src/components/Charts/BarCharts/BarChartContext.ts +++ b/packages/blade/src/components/Charts/BarCharts/BarChartContext.ts @@ -3,11 +3,13 @@ import { createContext, useContext } from 'react'; interface BarChartContextType { layout?: 'horizontal' | 'vertical'; activeIndex?: number; + colorTheme: 'default' | 'informational'; } export const BarChartContext = createContext({ layout: 'horizontal', activeIndex: undefined, + colorTheme: 'default', }); export const useBarChartContext = (): BarChartContextType => useContext(BarChartContext); diff --git a/packages/blade/src/components/Charts/BarCharts/BarCharts.stories.tsx b/packages/blade/src/components/Charts/BarCharts/BarCharts.stories.tsx index fd627e4a87d..0f5464e18a2 100644 --- a/packages/blade/src/components/Charts/BarCharts/BarCharts.stories.tsx +++ b/packages/blade/src/components/Charts/BarCharts/BarCharts.stories.tsx @@ -24,7 +24,6 @@ const chartData = [ { name: 'Dec', seriesA: 1200, seriesB: 4600, seriesC: 2000 }, ]; -// 2.3.a - TinyBarChart export const TinyBarChart: StoryFn = () => { return (
      @@ -35,7 +34,6 @@ export const TinyBarChart: StoryFn = () => { ); }; -// 2.3.b - SimpleBarChart export const SimpleBarChart: StoryFn = () => { return (
      @@ -60,7 +58,6 @@ export const SimpleBarChart: StoryFn = () => { ); }; -// 2.3.c - StackedBarChart export const StackedBarChart: StoryFn = () => { return (
      @@ -110,7 +107,6 @@ export const GroupedBarChart: StoryFn = () => { ); }; -// 2.4.d - Vertical Bar Chart export const VerticalBarChart: StoryFn = () => { return (
      @@ -143,7 +139,32 @@ export const VerticalBarChart: StoryFn = () => { ); }; +export const BarChartWithInformationalColorTheme: StoryFn = () => { + return ( +
      + + + + + + + + + + + + + + + + + +
      + ); +}; + TinyBarChart.storyName = 'Tiny Bar Chart'; SimpleBarChart.storyName = 'Simple Bar Chart'; StackedBarChart.storyName = 'Stacked Bar Chart'; VerticalBarChart.storyName = 'Vertical Bar Chart'; +BarChartWithInformationalColorTheme.storyName = 'Bar Chart With Informational Color Theme'; diff --git a/packages/blade/src/components/Charts/BarCharts/BarCharts.tsx b/packages/blade/src/components/Charts/BarCharts/BarCharts.tsx index 01494e9b9f4..165590107fd 100644 --- a/packages/blade/src/components/Charts/BarCharts/BarCharts.tsx +++ b/packages/blade/src/components/Charts/BarCharts/BarCharts.tsx @@ -5,6 +5,7 @@ import { Bar as RechartsBar, ResponsiveContainer as RechartsResponsiveContainer, } from 'recharts'; +import { useChartsColorTheme } from '../utils'; import { BarChartContext, useBarChartContext } from './BarChartContext'; import { useTheme } from '~components/BladeProvider'; import BaseBox from '~components/Box/BaseBox'; @@ -16,6 +17,7 @@ import type { ChartSequentialEmphasis, } from '~tokens/theme/theme'; import isNumber from '~utils/lodashButBetter/isNumber'; +import { throwBladeError } from '~utils/logger'; // Color token that allows both categorical and sequential chart colors export type BladeColorToken = @@ -36,15 +38,19 @@ export interface BarProps stackId?: string; activeBar?: React.ReactElement | boolean; label?: React.ReactElement | boolean; + /* + * @private + */ + _index?: number; } export type BarChartProps = Omit, 'margin'> & { - // Guard to limit number of series (Bars) - can be adjusted based on design decision - maxBars?: number; children?: React.ReactNode; + colorTheme?: 'default' | 'informational'; }; const DEFAULT_MARGIN = { top: 16, right: 16, bottom: 16, left: 16 }; +const MAX_BARS = 10; export type RechartsShapeProps = { x: number; @@ -55,51 +61,10 @@ export type RechartsShapeProps = { index: number; }; -// Default categorical palette order for auto-assignment when color isn't provided -const DEFAULT_CATEGORICAL_COLOR_TOKENS: BladeColorToken[] = [ - 'chart.background.categorical.azure.moderate', - 'chart.background.categorical.emerald.moderate', - 'chart.background.categorical.crimson.moderate', - 'chart.background.categorical.cider.moderate', - 'chart.background.categorical.sapphire.moderate', - 'chart.background.categorical.orchid.moderate', - 'chart.background.categorical.magenta.moderate', - 'chart.background.categorical.gray.moderate', -]; - // Arbitrary sequential limit per palette (can be tuned later with design) -const MAX_SEQUENTIAL_PER_CATEGORY = 6; const BAR_CHART_CORNER_RADIUS = 2; const DISTANCE_BETWEEN_STACKED_BARS = 2; -// Internal: Check and limit sequential usage; fallback to undefined to allow categorical defaulting -function enforceSequentialLimit(token?: BladeColorToken): BladeColorToken | undefined { - if (!token) return token; - const match = token.match(/^chart\.background\.sequential\.([^.]+)\./); - if (!match) return token; - - const category = match[1] as Exclude; - // Track sequential counts on a module-level map - // Note: This resets between renders naturally; it's enforced per render/compose - (enforceSequentialLimit as { _seq?: Map })._seq = - (enforceSequentialLimit as { _seq?: Map })._seq ?? new Map(); - const seqMap: Map = (enforceSequentialLimit as { _seq?: Map }) - ._seq!; - - const current = (seqMap.get(category) ?? 0) + 1; - seqMap.set(category, current); - - if (current > MAX_SEQUENTIAL_PER_CATEGORY) { - // eslint-disable-next-line no-console - console.error( - `[BarChart] Exceeded sequential color limit for '${category}'. Falling back to categorical default.`, - ); - return undefined; - } - - return token; -} - // Bar component - resolves Blade color tokens to actual colors export const Bar: React.FC = ({ color, @@ -107,11 +72,13 @@ export const Bar: React.FC = ({ dataKey, activeBar = false, label = false, + _index, ...rest }) => { const { theme } = useTheme(); - const { layout, activeIndex } = useBarChartContext(); - const fill = color ? getIn(theme.colors, color) : undefined; + const { layout, activeIndex, colorTheme } = useBarChartContext(); + const defaultColorArray = useChartsColorTheme({ colorTheme }); + const fill = color ? getIn(theme.colors, color) : defaultColorArray[_index ?? 0]; return ( = ({ }; // BarChart wrapper with default margin, auto-color assignment, and max bars guard -export const BarChart: React.FC = ({ children, maxBars = 8, ...props }) => { +export const BarChart: React.FC = ({ + children, + colorTheme = 'default', + ...props +}) => { const [activeIndex, setActiveIndex] = useState(undefined); - const processed = React.useMemo(() => { - const kids = React.Children.toArray(children); - - // Count children - //TODO: Look into this logic before final code review. - const barChildren = kids.filter( - (child) => - React.isValidElement(child) && (child.type as React.ComponentType) === Bar, - ) as React.ReactElement[]; - - if (barChildren.length > maxBars) { - return { - error: `Too many bars configured. Maximum allowed is ${maxBars}.`, - children: null as React.ReactNode, - }; - } - - // Assign colors and default names - const coloredKids: React.ReactNode[] = []; - let autoColorIdx = 0; - kids.forEach((child) => { - if (!React.isValidElement(child) || (child.type as React.ComponentType) !== Bar) { - coloredKids.push(child); - return; + const barChartModifiedChildrens = React.useMemo(() => { + let BarChartIndex = 0; + return React.Children.map(children, (child) => { + if (__DEV__ && BarChartIndex >= MAX_BARS) { + throwBladeError({ + message: `Too many bars configured. Maximum allowed is ${MAX_BARS}.`, + moduleName: 'BarChart', + }); } - - const incomingColor = child.props.color as BladeColorToken | undefined; - const limitedColor = enforceSequentialLimit(incomingColor); - const resolvedColor = - limitedColor ?? - DEFAULT_CATEGORICAL_COLOR_TOKENS[autoColorIdx++ % DEFAULT_CATEGORICAL_COLOR_TOKENS.length]; - - coloredKids.push( - React.cloneElement(child as React.ReactElement, { - color: resolvedColor, - name: child.props.name ?? child.props.dataKey, - }), - ); + if (React.isValidElement(child) && child.type === Bar) { + return React.cloneElement(child, { + _index: BarChartIndex++, + } as Partial); + } + return child; }); - - return { error: null as string | null, children: coloredKids }; - }, [children, maxBars]); - - const { theme } = useTheme(); + }, [children]); return ( - + - {processed.error ? ( - // Simple centered error message; non-intrusive layout -
      - {processed.error} -
      - ) : ( - { - setActiveIndex(state?.activeIndex ? Number(state?.activeIndex) : undefined); - }} - > - {processed.children} - - )} + { + setActiveIndex(state?.activeIndex ? Number(state?.activeIndex) : undefined); + }} + > + {barChartModifiedChildrens} +
      From e2e367b77db3714498e4790e444500f6f51e41e0 Mon Sep 17 00:00:00 2001 From: tewarig Date: Mon, 8 Sep 2025 23:32:24 +0530 Subject: [PATCH 059/105] chore: errors in charts --- .../src/components/Charts/LineCharts/lineCharts.tsx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx b/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx index c596cb6fc2d..50fa55b5ddd 100644 --- a/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx +++ b/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx @@ -12,6 +12,9 @@ import { metaAttribute } from '~utils/metaAttribute'; import BaseBox from '~components/Box/BaseBox'; import type { ChartColorCategories, ChartCategoricalEmphasis } from '~tokens/theme/theme'; import getIn from '~utils/lodashButBetter/get'; +import { throwBladeError } from '~utils/logger'; + +const MAX_LINES = 10; // BladeColorToken type for charts - only allows categorical chart colors for line charts export type BladeColorToken = `chart.background.categorical.${ChartColorCategories}.${keyof ChartCategoricalEmphasis}`; @@ -100,6 +103,13 @@ export const LineChart: React.FC = ({ const lineChartModifiedChildrens = React.useMemo(() => { let LineChartIndex = 0; return React.Children.map(children, (child) => { + if (__DEV__ && LineChartIndex >= MAX_LINES) { + throwBladeError({ + message: `Too many lines configured. Maximum allowed is ${MAX_LINES}.`, + moduleName: 'LineChart', + }); + } + if (React.isValidElement(child) && child.type === Line) { return React.cloneElement(child, { _index: LineChartIndex++, From df8de7e9c2d7138c54202c5d87b54cbb1c45bb6c Mon Sep 17 00:00:00 2001 From: tewarig Date: Mon, 8 Sep 2025 23:41:24 +0530 Subject: [PATCH 060/105] chore: update animation token and colors --- .../src/components/Charts/AreaChart/AreaCharts.tsx | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/blade/src/components/Charts/AreaChart/AreaCharts.tsx b/packages/blade/src/components/Charts/AreaChart/AreaCharts.tsx index d2c253c280c..6845b5467de 100644 --- a/packages/blade/src/components/Charts/AreaChart/AreaCharts.tsx +++ b/packages/blade/src/components/Charts/AreaChart/AreaCharts.tsx @@ -17,7 +17,9 @@ import { metaAttribute } from '~utils/metaAttribute'; import BaseBox from '~components/Box/BaseBox'; import type { ChartColorCategories, ChartCategoricalEmphasis } from '~tokens/theme/theme'; import getIn from '~utils/lodashButBetter/get'; +import { throwBladeError } from '~utils/logger'; +const MAX_AREAS = 10; // BladeColorToken type for charts - only allows categorical chart colors for area charts export type BladeColorToken = `chart.background.categorical.${ChartColorCategories}.${keyof ChartCategoricalEmphasis}`; @@ -57,6 +59,8 @@ export const Area: React.FC = ({ const { theme } = useTheme(); const themeColors = useChartsColorTheme({ colorTheme: _colorTheme ?? 'default' }); const colorToken = color ? getIn(theme.colors, color) : themeColors[_index ?? 0]; + const animationBegin = theme.motion.delay.gentle; + const animationDuration = theme.motion.duration.xgentle; return ( = ({ dot={dot} stackId={stackId} activeDot={activeDot} + animationBegin={animationBegin} + animationDuration={animationDuration} /> ); }; @@ -107,6 +113,12 @@ export const AreaChart: React.FC = ({ const modifiedChildren = React.useMemo(() => { let AreaChartIndex = 0; return React.Children.map(children, (child) => { + if (__DEV__ && AreaChartIndex >= MAX_AREAS) { + throwBladeError({ + message: `Too many areas configured. Maximum allowed is ${MAX_AREAS}.`, + moduleName: 'AreaChart', + }); + } if (React.isValidElement(child) && child.type === Area) { return React.cloneElement(child, { _index: AreaChartIndex++, From 1992e9999c6a0624a6c4ecf8fc01fe4da5ce639e Mon Sep 17 00:00:00 2001 From: tewarig Date: Tue, 9 Sep 2025 00:47:01 +0530 Subject: [PATCH 061/105] fea: update bar charts --- .../Charts/BarCharts/BarChartContext.ts | 2 ++ .../Charts/BarCharts/BarCharts.stories.tsx | 7 ---- .../components/Charts/BarCharts/BarCharts.tsx | 35 ++++++++++++------- 3 files changed, 24 insertions(+), 20 deletions(-) diff --git a/packages/blade/src/components/Charts/BarCharts/BarChartContext.ts b/packages/blade/src/components/Charts/BarCharts/BarChartContext.ts index b787a11f16f..5b2fa438304 100644 --- a/packages/blade/src/components/Charts/BarCharts/BarChartContext.ts +++ b/packages/blade/src/components/Charts/BarCharts/BarChartContext.ts @@ -4,12 +4,14 @@ interface BarChartContextType { layout?: 'horizontal' | 'vertical'; activeIndex?: number; colorTheme: 'default' | 'informational'; + totalBars: number; } export const BarChartContext = createContext({ layout: 'horizontal', activeIndex: undefined, colorTheme: 'default', + totalBars: 0, }); export const useBarChartContext = (): BarChartContextType => useContext(BarChartContext); diff --git a/packages/blade/src/components/Charts/BarCharts/BarCharts.stories.tsx b/packages/blade/src/components/Charts/BarCharts/BarCharts.stories.tsx index 0f5464e18a2..ff0d266ea0c 100644 --- a/packages/blade/src/components/Charts/BarCharts/BarCharts.stories.tsx +++ b/packages/blade/src/components/Charts/BarCharts/BarCharts.stories.tsx @@ -151,13 +151,6 @@ export const BarChartWithInformationalColorTheme: StoryFn = () - - - - - - -
      ); diff --git a/packages/blade/src/components/Charts/BarCharts/BarCharts.tsx b/packages/blade/src/components/Charts/BarCharts/BarCharts.tsx index 165590107fd..8fcbb889605 100644 --- a/packages/blade/src/components/Charts/BarCharts/BarCharts.tsx +++ b/packages/blade/src/components/Charts/BarCharts/BarCharts.tsx @@ -72,13 +72,20 @@ export const Bar: React.FC = ({ dataKey, activeBar = false, label = false, - _index, + _index = 0, ...rest }) => { const { theme } = useTheme(); - const { layout, activeIndex, colorTheme } = useBarChartContext(); + const { layout, activeIndex, colorTheme, totalBars } = useBarChartContext(); const defaultColorArray = useChartsColorTheme({ colorTheme }); - const fill = color ? getIn(theme.colors, color) : defaultColorArray[_index ?? 0]; + const fill = color ? getIn(theme.colors, color) : defaultColorArray[_index]; + const isStacked = rest.stackId !== undefined; + const animationBegin = isStacked + ? (theme.motion.duration.gentle / totalBars) * _index + : theme.motion.duration.gentle; + const animationDuration = isStacked + ? theme.motion.duration.gentle / totalBars + : theme.motion.duration.gentle; return ( = ({ legendType="rect" activeBar={activeBar} label={label} + animationBegin={animationBegin} + animationDuration={animationDuration} // https://github.com/recharts/recharts/issues/2244#issuecomment-2288572842 shape={(props: unknown) => { const { fill, x, y, width, height, index: barIndex } = props as RechartsShapeProps; @@ -108,9 +117,6 @@ export const Bar: React.FC = ({ rx={BAR_CHART_CORNER_RADIUS} ry={BAR_CHART_CORNER_RADIUS} fillOpacity={fillOpacity} - style={{ - transition: 'fill-opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1)', - }} /> ); } @@ -124,9 +130,6 @@ export const Bar: React.FC = ({ rx={BAR_CHART_CORNER_RADIUS} ry={BAR_CHART_CORNER_RADIUS} fillOpacity={fillOpacity} - style={{ - transition: 'fill-opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1)', - }} /> ); }} @@ -142,9 +145,9 @@ export const BarChart: React.FC = ({ }) => { const [activeIndex, setActiveIndex] = useState(undefined); - const barChartModifiedChildrens = React.useMemo(() => { + const { barChartModifiedChildrens, totalBars } = React.useMemo(() => { let BarChartIndex = 0; - return React.Children.map(children, (child) => { + const modifiedChildren = React.Children.map(children, (child) => { if (__DEV__ && BarChartIndex >= MAX_BARS) { throwBladeError({ message: `Too many bars configured. Maximum allowed is ${MAX_BARS}.`, @@ -158,11 +161,17 @@ export const BarChart: React.FC = ({ } return child; }); - }, [children]); + return { + barChartModifiedChildrens: modifiedChildren, + totalBars: BarChartIndex, + }; + }, [children]); return ( - + Date: Wed, 10 Sep 2025 03:15:48 +0530 Subject: [PATCH 062/105] Revert "feat: add area charts" (#2936) From bdb7bc7453c24fadb41eb6149789f86ba4d3d4f3 Mon Sep 17 00:00:00 2001 From: tewarig Date: Wed, 10 Sep 2025 13:01:32 +0530 Subject: [PATCH 063/105] chore: update change set --- .changeset/calm-peas-work.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/calm-peas-work.md b/.changeset/calm-peas-work.md index 18a4569d493..64cf2d58b8d 100644 --- a/.changeset/calm-peas-work.md +++ b/.changeset/calm-peas-work.md @@ -1,5 +1,5 @@ --- -"@razorpay/blade": patch +"@razorpay/blade": minor --- feat: add line charts From d70e2ab3192b2eab4a9700fccd328fa57e1c251d Mon Sep 17 00:00:00 2001 From: tewarig Date: Wed, 10 Sep 2025 13:41:38 +0530 Subject: [PATCH 064/105] review: update export types --- .../BaseChartComponents.tsx | 37 ++++++++++++------- .../Charts/LineCharts/lineCharts.tsx | 16 ++++---- 2 files changed, 31 insertions(+), 22 deletions(-) diff --git a/packages/blade/src/components/Charts/BaseChartComponents/BaseChartComponents.tsx b/packages/blade/src/components/Charts/BaseChartComponents/BaseChartComponents.tsx index 1563733e2b9..b9ad568d40d 100644 --- a/packages/blade/src/components/Charts/BaseChartComponents/BaseChartComponents.tsx +++ b/packages/blade/src/components/Charts/BaseChartComponents/BaseChartComponents.tsx @@ -16,28 +16,28 @@ import { Box } from '~components/Box'; import { useTheme } from '~components/BladeProvider'; import getIn from '~utils/lodashButBetter/get'; -export type ReferenceLineProps = { +type ReferenceLineProps = { y?: number; x?: number; label: string; }; -export type XAxisProps = Omit & { +type XAxisProps = Omit & { label?: string; dataKey?: string; }; -export type YAxisProps = Omit & { +type YAxisProps = Omit & { label?: string; dataKey?: string; }; -export type ChartToolTipProps = ComponentProps; -export type LegendProps = ComponentProps; -export type ResponsiveContainerProps = ComponentProps; +type ChartToolTipProps = ComponentProps; +type LegendProps = ComponentProps; +type ResponsiveContainerProps = ComponentProps; -export type CartesianGridProps = ComponentProps; +type CartesianGridProps = ComponentProps; -export const XAxis: React.FC = (props) => { +const XAxis: React.FC = (props) => { const { theme } = useTheme(); const X_OFFSET = 32; const Y_OFFSET = 14.5; @@ -73,7 +73,7 @@ export const XAxis: React.FC = (props) => { ); }; -export const YAxis: React.FC = (props) => { +const YAxis: React.FC = (props) => { const { theme } = useTheme(); return ( @@ -107,7 +107,7 @@ export const YAxis: React.FC = (props) => { ); }; -export const CartesianGrid: React.FC = (props) => { +const CartesianGrid: React.FC = (props) => { const { theme } = useTheme(); return ( @@ -120,7 +120,7 @@ export const CartesianGrid: React.FC = (props) => { }; //REVIEW_NOTES: this might change -export const ChartToolTip: React.FC = (props) => { +const ChartToolTip: React.FC = (props) => { const { theme } = useTheme(); return ( @@ -197,7 +197,7 @@ const CustomSquareLegend = (props: { ); }; -export const Legend: React.FC = (props) => { +const Legend: React.FC = (props) => { const { theme } = useTheme(); return ( @@ -215,7 +215,7 @@ export const Legend: React.FC = (props) => { ); }; -export const ResponsiveContainer: React.FC = (props) => { +const ResponsiveContainer: React.FC = (props) => { return ; }; @@ -284,3 +284,14 @@ export const ReferenceLine: React.FC = ({ label, x, y }) => /> ); }; + +export type { + ReferenceLineProps, + XAxisProps, + YAxisProps, + ChartToolTipProps, + LegendProps, + ResponsiveContainerProps, + CartesianGridProps, +}; +export { XAxis, YAxis, ResponsiveContainer, CartesianGrid, ChartToolTip, Legend }; diff --git a/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx b/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx index 50fa55b5ddd..d13d04387aa 100644 --- a/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx +++ b/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx @@ -16,11 +16,10 @@ import { throwBladeError } from '~utils/logger'; const MAX_LINES = 10; -// BladeColorToken type for charts - only allows categorical chart colors for line charts export type BladeColorToken = `chart.background.categorical.${ChartColorCategories}.${keyof ChartCategoricalEmphasis}`; // Chart-specific interfaces based on user specifications -export interface LineProps { +interface LineProps { type?: 'step' | 'stepAfter' | 'stepBefore' | 'linear' | 'monotone'; dot?: RechartsLineProps['dot']; activeDot?: RechartsLineProps['activeDot']; @@ -41,7 +40,7 @@ export interface LineProps { } // TypeScript prop types -export type LineChartProps = ComponentProps & { +type LineChartProps = ComponentProps & { colorTheme?: 'default' | 'informational'; }; @@ -54,7 +53,7 @@ export interface ReferenceLineProps { labelOffset?: number; } -export const Line: React.FC = ({ +const Line: React.FC = ({ color, strokeStyle = 'solid', type = 'monotone', @@ -95,11 +94,7 @@ export const Line: React.FC = ({ }; // Main components -export const LineChart: React.FC = ({ - children, - colorTheme = 'default', - ...props -}) => { +const LineChart: React.FC = ({ children, colorTheme = 'default', ...props }) => { const lineChartModifiedChildrens = React.useMemo(() => { let LineChartIndex = 0; return React.Children.map(children, (child) => { @@ -128,3 +123,6 @@ export const LineChart: React.FC = ({ ); }; + +export type { LineChartProps, LineProps }; +export { LineChart, Line }; From 62efbbea642152795272403890b804741c424ed4 Mon Sep 17 00:00:00 2001 From: tewarig Date: Wed, 10 Sep 2025 14:20:04 +0530 Subject: [PATCH 065/105] chore: update example --- .../components/Charts/LineCharts/LineCharts.stories.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx b/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx index 21aceed72f5..6ed65c5de22 100644 --- a/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx +++ b/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx @@ -271,8 +271,12 @@ export const LineChartWithXAndYAxisLabels: StoryFn = () => { - - + +
      ); From 049e0e0a019843566dcb19e61afc6e5afc010f85 Mon Sep 17 00:00:00 2001 From: tewarig Date: Wed, 10 Sep 2025 15:07:19 +0530 Subject: [PATCH 066/105] fix: update baseChart components --- .../BaseChartComponents.tsx | 59 +++++++++++++++---- 1 file changed, 49 insertions(+), 10 deletions(-) diff --git a/packages/blade/src/components/Charts/BaseChartComponents/BaseChartComponents.tsx b/packages/blade/src/components/Charts/BaseChartComponents/BaseChartComponents.tsx index b9ad568d40d..5a89fdc1709 100644 --- a/packages/blade/src/components/Charts/BaseChartComponents/BaseChartComponents.tsx +++ b/packages/blade/src/components/Charts/BaseChartComponents/BaseChartComponents.tsx @@ -10,8 +10,7 @@ import { ReferenceLine as RechartsReferenceLine, } from 'recharts'; import type { XAxisProps as RechartsXAxisProps, YAxisProps as RechartsYAxisProps } from 'recharts'; -import { castWebType } from '~utils'; -import { Text } from '~components/Typography'; +import { Heading, Text } from '~components/Typography'; import { Box } from '~components/Box'; import { useTheme } from '~components/BladeProvider'; import getIn from '~utils/lodashButBetter/get'; @@ -125,14 +124,54 @@ const ChartToolTip: React.FC = (props) => { return ( { + console.log({ + active, + payload, + label, + }); + return ( +
      + + {label} + + + {payload.map((item) => ( + + +
      + + {item.name} + + + + {item.value} + + + ))} + +
      + ); }} cursor={{ fill: 'rgba(0, 0, 0, 0.1)', stroke: theme.colors.surface.border.gray.muted }} {...props} From c58ff8b56f7ccb190d1a75efbee16e05941cd0cd Mon Sep 17 00:00:00 2001 From: tewarig Date: Wed, 10 Sep 2025 15:18:45 +0530 Subject: [PATCH 067/105] chore: update forecasted data --- .../src/components/Charts/LineCharts/LineCharts.stories.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx b/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx index 6ed65c5de22..0230e06ee74 100644 --- a/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx +++ b/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx @@ -178,7 +178,7 @@ export const ForecastLineChart: StoryFn = () => { /> Date: Wed, 10 Sep 2025 15:23:21 +0530 Subject: [PATCH 068/105] chore: add changeset --- .changeset/red-ears-check.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/red-ears-check.md diff --git a/.changeset/red-ears-check.md b/.changeset/red-ears-check.md new file mode 100644 index 00000000000..b9f204423a3 --- /dev/null +++ b/.changeset/red-ears-check.md @@ -0,0 +1,5 @@ +--- +'@razorpay/blade': minor +--- + +feat: add area chart From 94da90dd4a5ae767641aa0e9550475c08e18ec36 Mon Sep 17 00:00:00 2001 From: tewarig Date: Wed, 10 Sep 2025 15:34:42 +0530 Subject: [PATCH 069/105] fix: add bar chart --- .changeset/fluffy-impalas-raise.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/fluffy-impalas-raise.md diff --git a/.changeset/fluffy-impalas-raise.md b/.changeset/fluffy-impalas-raise.md new file mode 100644 index 00000000000..28d7af2b4de --- /dev/null +++ b/.changeset/fluffy-impalas-raise.md @@ -0,0 +1,5 @@ +--- +'@razorpay/blade': minor +--- + +feat: add bar chart From 844936683e8bdb7fe37ef2c7e01d9ee9426c688e Mon Sep 17 00:00:00 2001 From: tewarig Date: Wed, 10 Sep 2025 15:41:39 +0530 Subject: [PATCH 070/105] chore: remove logs from bar chart --- .../Charts/BaseChartComponents/BaseChartComponents.tsx | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/blade/src/components/Charts/BaseChartComponents/BaseChartComponents.tsx b/packages/blade/src/components/Charts/BaseChartComponents/BaseChartComponents.tsx index a1e777a3d9e..b3711be784e 100644 --- a/packages/blade/src/components/Charts/BaseChartComponents/BaseChartComponents.tsx +++ b/packages/blade/src/components/Charts/BaseChartComponents/BaseChartComponents.tsx @@ -124,12 +124,7 @@ const ChartToolTip: React.FC = (props) => { return ( { - console.log({ - active, - payload, - label, - }); + content={({ payload, label }) => { return (
      Date: Fri, 19 Sep 2025 00:02:11 +0530 Subject: [PATCH 071/105] refactor: bar charts --- .../Charts/BarCharts/BarChartContext.ts | 8 +- .../Charts/BarCharts/BarCharts.native.tsx | 25 ++ .../Charts/BarCharts/BarCharts.stories.tsx | 139 ++++---- .../{BarCharts.tsx => BarCharts.web.tsx} | 104 ++---- .../BarCharts/__tests__/BarChart.web.test.tsx | 0 .../src/components/Charts/BarCharts/index.ts | 2 +- .../src/components/Charts/BarCharts/tokens.ts | 9 + .../src/components/Charts/BarCharts/types.ts | 71 ++++ .../BaseChartComponents.tsx | 331 ------------------ .../Charts/BaseChartComponents/index.ts | 18 - .../Charts/CommonChartComponents/types.ts | 12 +- .../Charts/LineCharts/LineCharts.stories.tsx | 291 --------------- .../src/components/Charts/LineCharts/index.ts | 3 - .../Charts/LineCharts/lineCharts.tsx | 128 ------- packages/blade/src/components/Charts/index.ts | 2 +- 15 files changed, 233 insertions(+), 910 deletions(-) create mode 100644 packages/blade/src/components/Charts/BarCharts/BarCharts.native.tsx rename packages/blade/src/components/Charts/BarCharts/{BarCharts.tsx => BarCharts.web.tsx} (62%) create mode 100644 packages/blade/src/components/Charts/BarCharts/__tests__/BarChart.web.test.tsx create mode 100644 packages/blade/src/components/Charts/BarCharts/tokens.ts create mode 100644 packages/blade/src/components/Charts/BarCharts/types.ts delete mode 100644 packages/blade/src/components/Charts/BaseChartComponents/BaseChartComponents.tsx delete mode 100644 packages/blade/src/components/Charts/BaseChartComponents/index.ts delete mode 100644 packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx delete mode 100644 packages/blade/src/components/Charts/LineCharts/index.ts delete mode 100644 packages/blade/src/components/Charts/LineCharts/lineCharts.tsx diff --git a/packages/blade/src/components/Charts/BarCharts/BarChartContext.ts b/packages/blade/src/components/Charts/BarCharts/BarChartContext.ts index 5b2fa438304..b13de58bcd4 100644 --- a/packages/blade/src/components/Charts/BarCharts/BarChartContext.ts +++ b/packages/blade/src/components/Charts/BarCharts/BarChartContext.ts @@ -1,11 +1,5 @@ import { createContext, useContext } from 'react'; -// Context for chart orientation -interface BarChartContextType { - layout?: 'horizontal' | 'vertical'; - activeIndex?: number; - colorTheme: 'default' | 'informational'; - totalBars: number; -} +import type { BarChartContextType } from './types'; export const BarChartContext = createContext({ layout: 'horizontal', diff --git a/packages/blade/src/components/Charts/BarCharts/BarCharts.native.tsx b/packages/blade/src/components/Charts/BarCharts/BarCharts.native.tsx new file mode 100644 index 00000000000..3482df3dde3 --- /dev/null +++ b/packages/blade/src/components/Charts/BarCharts/BarCharts.native.tsx @@ -0,0 +1,25 @@ +import React from 'react'; +import type { ChartBarProps, ChartBarWrapperProps } from './types'; +import { Text } from '~components/Typography'; +import { throwBladeError } from '~utils/logger'; + +const ChartBar = (_prop: ChartBarProps): React.ReactElement => { + throwBladeError({ + message: 'ChartLineWrapper is not yet implemented for native', + moduleName: 'ChartBar', + }); + + return ChartLineWrapper is not available for Native mobile apps.; +}; + +const ChartBarWrapper = (_prop: ChartBarWrapperProps): React.ReactElement => { + throwBladeError({ + message: 'ChartBarWrapperProps is not yet implemented for native', + moduleName: 'ChartBarWrapper', + }); + + return ChartLine is not available for Native mobile apps.; +}; + +export type { ChartBarProps, ChartBarWrapperProps }; +export { ChartBar, ChartBarWrapper }; diff --git a/packages/blade/src/components/Charts/BarCharts/BarCharts.stories.tsx b/packages/blade/src/components/Charts/BarCharts/BarCharts.stories.tsx index ff0d266ea0c..3cd5ca9ac6b 100644 --- a/packages/blade/src/components/Charts/BarCharts/BarCharts.stories.tsx +++ b/packages/blade/src/components/Charts/BarCharts/BarCharts.stories.tsx @@ -1,13 +1,20 @@ import type { StoryFn, Meta } from '@storybook/react'; import React from 'react'; -import { XAxis, YAxis, CartesianGrid, ChartToolTip, Legend } from '../BaseChartComponents'; -import { BarChart, Bar } from './BarCharts'; +import { + ChartBar, + ChartBarWrapper, + ChartXAxis, + ChartYAxis, + ChartCartesianGrid, + ChartTooltip, + ChartLegend, +} from '~components/Charts'; export default { title: 'Components/Charts/BarChart', - component: BarChart, + component: ChartBar, tags: ['autodocs'], -} as Meta; +} as Meta; const chartData = [ { name: 'Jan', seriesA: 4000, seriesB: 2400, seriesC: 1200 }, @@ -24,134 +31,142 @@ const chartData = [ { name: 'Dec', seriesA: 1200, seriesB: 4600, seriesC: 2000 }, ]; -export const TinyBarChart: StoryFn = () => { +export const TinyBarChart: StoryFn = () => { return (
      - - - + + +
      ); }; -export const SimpleBarChart: StoryFn = () => { +export const SimpleBarChart: StoryFn = () => { return (
      - - - - - - - + + + + + + - - +
      ); }; -export const StackedBarChart: StoryFn = () => { +export const StackedBarChart: StoryFn = () => { return (
      - - - - - - - + + + + + + - - - +
      ); }; -export const GroupedBarChart: StoryFn = () => { +export const GroupedBarChart: StoryFn = () => { return (
      - - - - - - - - - - + + + + + + + + + +
      ); }; -export const VerticalBarChart: StoryFn = () => { +export const VerticalBarChart: StoryFn = () => { return (
      - - - - - - - + + + + + + - - - +
      ); }; -export const BarChartWithInformationalColorTheme: StoryFn = () => { +export const BarChartWithInformationalColorTheme: StoryFn = () => { return (
      - - - - - - - - - - + + + + + + + + + +
      ); }; diff --git a/packages/blade/src/components/Charts/BarCharts/BarCharts.tsx b/packages/blade/src/components/Charts/BarCharts/BarCharts.web.tsx similarity index 62% rename from packages/blade/src/components/Charts/BarCharts/BarCharts.tsx rename to packages/blade/src/components/Charts/BarCharts/BarCharts.web.tsx index 8fcbb889605..00375275873 100644 --- a/packages/blade/src/components/Charts/BarCharts/BarCharts.tsx +++ b/packages/blade/src/components/Charts/BarCharts/BarCharts.web.tsx @@ -1,5 +1,4 @@ import React, { useState } from 'react'; -import type { ComponentProps } from 'react'; import { BarChart as RechartsBarChart, Bar as RechartsBar, @@ -7,50 +6,17 @@ import { } from 'recharts'; import { useChartsColorTheme } from '../utils'; import { BarChartContext, useBarChartContext } from './BarChartContext'; +import type { ChartBarProps, ChartBarWrapperProps } from './types'; +import { BAR_CHART_CORNER_RADIUS, DISTANCE_BETWEEN_STACKED_BARS, componentIds } from './tokens'; import { useTheme } from '~components/BladeProvider'; import BaseBox from '~components/Box/BaseBox'; import { metaAttribute } from '~utils/metaAttribute'; import getIn from '~utils/lodashButBetter/get'; -import type { - ChartColorCategories, - ChartCategoricalEmphasis, - ChartSequentialEmphasis, -} from '~tokens/theme/theme'; import isNumber from '~utils/lodashButBetter/isNumber'; -import { throwBladeError } from '~utils/logger'; - -// Color token that allows both categorical and sequential chart colors -export type BladeColorToken = - | `chart.background.categorical.${ChartColorCategories}.${keyof ChartCategoricalEmphasis}` - | `chart.background.sequential.${Exclude< - ChartColorCategories, - 'gray' - >}.${keyof ChartSequentialEmphasis}`; - -export interface BarProps - extends Omit< - ComponentProps, - 'fill' | 'dataKey' | 'name' | 'label' | 'activeBar' - > { - dataKey: string; - name?: string; // default to dataKey - color?: BladeColorToken; - stackId?: string; - activeBar?: React.ReactElement | boolean; - label?: React.ReactElement | boolean; - /* - * @private - */ - _index?: number; -} - -export type BarChartProps = Omit, 'margin'> & { - children?: React.ReactNode; - colorTheme?: 'default' | 'informational'; -}; - -const DEFAULT_MARGIN = { top: 16, right: 16, bottom: 16, left: 16 }; -const MAX_BARS = 10; +import { assignWithoutSideEffects } from '~utils/assignWithoutSideEffects'; +import { getComponentId } from '~utils/isValidAllowedChildren'; +import { makeAnalyticsAttribute } from '~utils/makeAnalyticsAttribute'; +import type { DataAnalyticsAttribute, TestID } from '~utils/types'; export type RechartsShapeProps = { x: number; @@ -61,12 +27,8 @@ export type RechartsShapeProps = { index: number; }; -// Arbitrary sequential limit per palette (can be tuned later with design) -const BAR_CHART_CORNER_RADIUS = 2; -const DISTANCE_BETWEEN_STACKED_BARS = 2; - // Bar component - resolves Blade color tokens to actual colors -export const Bar: React.FC = ({ +const _ChartBar: React.FC = ({ color, name, dataKey, @@ -76,8 +38,8 @@ export const Bar: React.FC = ({ ...rest }) => { const { theme } = useTheme(); - const { layout, activeIndex, colorTheme, totalBars } = useBarChartContext(); - const defaultColorArray = useChartsColorTheme({ colorTheme }); + const { layout, activeIndex, colorTheme: _colorTheme, totalBars } = useBarChartContext(); + const defaultColorArray = useChartsColorTheme({ colorTheme: _colorTheme ?? 'default' }); const fill = color ? getIn(theme.colors, color) : defaultColorArray[_index]; const isStacked = rest.stackId !== undefined; const animationBegin = isStacked @@ -89,20 +51,21 @@ export const Bar: React.FC = ({ return ( { const { fill, x, y, width, height, index: barIndex } = props as RechartsShapeProps; const fillOpacity = isNumber(activeIndex) ? (barIndex === activeIndex ? 1 : 0.4) : 1; const gap = DISTANCE_BETWEEN_STACKED_BARS; - // Check if this is a vertical layout (bars going up/down) const isVertical = layout === 'vertical'; if (isVertical) { @@ -126,7 +89,7 @@ export const Bar: React.FC = ({ x={x} y={y + gap / 2} width={width} - height={height - gap} + height={height > gap ? height - gap : 0} rx={BAR_CHART_CORNER_RADIUS} ry={BAR_CHART_CORNER_RADIUS} fillOpacity={fillOpacity} @@ -137,27 +100,28 @@ export const Bar: React.FC = ({ ); }; +const ChartBar = assignWithoutSideEffects(_ChartBar, { + componentId: componentIds.barChart, +}); + // BarChart wrapper with default margin, auto-color assignment, and max bars guard -export const BarChart: React.FC = ({ +const ChartBarWrapper: React.FC = ({ children, colorTheme = 'default', - ...props + layout = 'horizontal', + testID, + data = [], + ...restProps }) => { const [activeIndex, setActiveIndex] = useState(undefined); const { barChartModifiedChildrens, totalBars } = React.useMemo(() => { let BarChartIndex = 0; const modifiedChildren = React.Children.map(children, (child) => { - if (__DEV__ && BarChartIndex >= MAX_BARS) { - throwBladeError({ - message: `Too many bars configured. Maximum allowed is ${MAX_BARS}.`, - moduleName: 'BarChart', - }); - } - if (React.isValidElement(child) && child.type === Bar) { + if (React.isValidElement(child) && getComponentId(child) === componentIds.barChart) { return React.cloneElement(child, { _index: BarChartIndex++, - } as Partial); + } as Partial); } return child; }); @@ -168,20 +132,24 @@ export const BarChart: React.FC = ({ }; }, [children]); return ( - - + + { setActiveIndex(state?.activeIndex ? Number(state?.activeIndex) : undefined); }} + layout={layout} + data={data} > {barChartModifiedChildrens} @@ -190,3 +158,5 @@ export const BarChart: React.FC = ({ ); }; + +export { ChartBarWrapper, ChartBar }; diff --git a/packages/blade/src/components/Charts/BarCharts/__tests__/BarChart.web.test.tsx b/packages/blade/src/components/Charts/BarCharts/__tests__/BarChart.web.test.tsx new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/blade/src/components/Charts/BarCharts/index.ts b/packages/blade/src/components/Charts/BarCharts/index.ts index f110182a748..76b51ccd38f 100644 --- a/packages/blade/src/components/Charts/BarCharts/index.ts +++ b/packages/blade/src/components/Charts/BarCharts/index.ts @@ -1 +1 @@ -export * from './BarCharts'; +export { ChartBarWrapper, ChartBar } from './BarCharts'; diff --git a/packages/blade/src/components/Charts/BarCharts/tokens.ts b/packages/blade/src/components/Charts/BarCharts/tokens.ts new file mode 100644 index 00000000000..cfaab2b44e1 --- /dev/null +++ b/packages/blade/src/components/Charts/BarCharts/tokens.ts @@ -0,0 +1,9 @@ +// Arbitrary sequential limit per palette (we will not have this in updated design) +const BAR_CHART_CORNER_RADIUS = 2; +const DISTANCE_BETWEEN_STACKED_BARS = 2; + +const componentIds = { + barChart: 'BarChart', +}; + +export { componentIds, DISTANCE_BETWEEN_STACKED_BARS, BAR_CHART_CORNER_RADIUS }; diff --git a/packages/blade/src/components/Charts/BarCharts/types.ts b/packages/blade/src/components/Charts/BarCharts/types.ts new file mode 100644 index 00000000000..6fdeef645c7 --- /dev/null +++ b/packages/blade/src/components/Charts/BarCharts/types.ts @@ -0,0 +1,71 @@ +import type { BarProps as RechartsBarProps } from 'recharts'; +import type { + ChartsCategoricalColorToken, + ChartSequentialColorToken, +} from '../CommonChartComponents/types'; +import type { colorTheme } from '../utils'; +import type { + BaseBoxProps, + FlexboxProps, + GridProps, +} from '~components/Box/BaseBox/types/propsTypes'; + +type ChartBarProps = Omit & { + /** + * The data key of the bar chart. + */ + dataKey: RechartsBarProps['dataKey']; + /** + * The name of the bar chart. + */ + name?: RechartsBarProps['name']; // default to dataKey + /** + * The color of the bar chart. + */ + color?: ChartsCategoricalColorToken | ChartSequentialColorToken; + /** + * The stack id of the bar chart. + */ + stackId?: RechartsBarProps['stackId']; + /** + * The active bar of the bar chart. + */ + activeBar?: RechartsBarProps['activeBar']; + /** + * The label of the bar chart. + */ + label?: RechartsBarProps['label']; + /* + * @private + */ + _index?: number; +}; + +type data = { + [key: string]: unknown; +}; + +type ChartBarWrapperProps = { + children?: React.ReactNode; + /** + * The color theme of the bar chart. + */ + colorTheme?: colorTheme; + /** + * The layout of the bar chart. + */ + layout?: 'horizontal' | 'vertical'; + /** + * Chart data to be rendered + */ + data: data[]; +} & Partial>; + +interface BarChartContextType { + layout?: 'horizontal' | 'vertical'; + activeIndex?: number; + colorTheme: colorTheme; + totalBars: number; +} + +export type { ChartBarProps, ChartBarWrapperProps, BarChartContextType }; diff --git a/packages/blade/src/components/Charts/BaseChartComponents/BaseChartComponents.tsx b/packages/blade/src/components/Charts/BaseChartComponents/BaseChartComponents.tsx deleted file mode 100644 index b3711be784e..00000000000 --- a/packages/blade/src/components/Charts/BaseChartComponents/BaseChartComponents.tsx +++ /dev/null @@ -1,331 +0,0 @@ -import React from 'react'; -import type { ComponentProps } from 'react'; -import { - XAxis as RechartsXAxis, - YAxis as RechartsYAxis, - CartesianGrid as RechartsCartesianGrid, - Tooltip as RechartsTooltip, - Legend as RechartsLegend, - ResponsiveContainer as RechartsResponsiveContainer, - ReferenceLine as RechartsReferenceLine, -} from 'recharts'; -import type { XAxisProps as RechartsXAxisProps, YAxisProps as RechartsYAxisProps } from 'recharts'; -import { Heading, Text } from '~components/Typography'; -import { Box } from '~components/Box'; -import { useTheme } from '~components/BladeProvider'; -import getIn from '~utils/lodashButBetter/get'; - -type ReferenceLineProps = { - y?: number; - x?: number; - label: string; -}; - -type XAxisProps = Omit & { - label?: string; - dataKey?: string; -}; -type YAxisProps = Omit & { - label?: string; - dataKey?: string; -}; - -type ChartToolTipProps = ComponentProps; -type LegendProps = ComponentProps; -type ResponsiveContainerProps = ComponentProps; - -type CartesianGridProps = ComponentProps; - -const XAxis: React.FC = (props) => { - const { theme } = useTheme(); - const X_OFFSET = 32; - const Y_OFFSET = 14.5; - const TEXT_BASELINE = 24; - - return ( - ( - - {props?.label} - - )} - dataKey={props?.dataKey} - /> - ); -}; - -const YAxis: React.FC = (props) => { - const { theme } = useTheme(); - - return ( - - ); -}; - -const CartesianGrid: React.FC = (props) => { - const { theme } = useTheme(); - - return ( - - ); -}; - -//REVIEW_NOTES: this might change -const ChartToolTip: React.FC = (props) => { - const { theme } = useTheme(); - - return ( - { - return ( -
      - - {label} - - - {payload.map((item) => ( - - -
      - - {item.name} - - - - {item.value} - - - ))} - -
      - ); - }} - cursor={false} - {...props} - /> - ); -}; - -const CustomSquareLegend = (props: { - payload?: Array<{ - payload: { - legendType: 'none' | 'line'; - }; - value: string; - color: string; - }>; -}): JSX.Element | null => { - const { payload } = props; - - if (!payload || payload.length === 0) { - return null; - } - const filteredPayload = payload.filter((entry) => entry?.payload?.legendType !== 'none'); - - return ( -
        - {filteredPayload.map((entry, index) => ( -
      • - - - {/* Legend text with custom color and size */} - - {entry.value} - - - - {/* Changed text color to a dark gray */} -
      • - ))} -
      - ); -}; - -const Legend: React.FC = (props) => { - const { theme } = useTheme(); - - return ( - } - {...props} - /> - ); -}; - -const ResponsiveContainer: React.FC = (props) => { - return ; -}; - -const CustomReferenceLabel = ({ - viewBox, - value, -}: { - viewBox?: { x: number; y: number; width: number }; - value: string | undefined; -}): JSX.Element => { - const { x, y, width } = viewBox ?? { x: 0, y: 0, width: 0 }; - const { theme } = useTheme(); - - const RECT_WIDTH = 80; - const RECT_HEIGHT = 30; - const TEXT_BASELINE = 15; - const rectX = x + width - RECT_WIDTH; - const rectY = y - TEXT_BASELINE; - - // Padding for text inside the rectangle (4px vertical, 8px horizontal) - const PADDING_VERTICAL = 4; - const PADDING_HORIZONTAL = 8; - - // Text position with padding inside the rectangle - const textX = rectX + PADDING_HORIZONTAL + (RECT_WIDTH - PADDING_HORIZONTAL * 2) / 2; - const textY = rectY + PADDING_VERTICAL + TEXT_BASELINE; // +15 for text baseline - - return ( - - - - {value} - - - ); -}; - -export const ReferenceLine: React.FC = ({ label, x, y }) => { - const { theme } = useTheme(); - return ( - } - x={x} - y={y} - /> - ); -}; - -export type { - ReferenceLineProps, - XAxisProps, - YAxisProps, - ChartToolTipProps, - LegendProps, - ResponsiveContainerProps, - CartesianGridProps, -}; -export { XAxis, YAxis, ResponsiveContainer, CartesianGrid, ChartToolTip, Legend }; diff --git a/packages/blade/src/components/Charts/BaseChartComponents/index.ts b/packages/blade/src/components/Charts/BaseChartComponents/index.ts deleted file mode 100644 index e5dc691baff..00000000000 --- a/packages/blade/src/components/Charts/BaseChartComponents/index.ts +++ /dev/null @@ -1,18 +0,0 @@ -export type { - XAxisProps, - YAxisProps, - CartesianGridProps, - ChartToolTipProps, - LegendProps, - ResponsiveContainerProps, - ReferenceLineProps, -} from './BaseChartComponents'; -export { - XAxis, - YAxis, - CartesianGrid, - ChartToolTip, - Legend, - ResponsiveContainer, - ReferenceLine, -} from './BaseChartComponents'; diff --git a/packages/blade/src/components/Charts/CommonChartComponents/types.ts b/packages/blade/src/components/Charts/CommonChartComponents/types.ts index 78a8652b503..c4703910cef 100644 --- a/packages/blade/src/components/Charts/CommonChartComponents/types.ts +++ b/packages/blade/src/components/Charts/CommonChartComponents/types.ts @@ -7,7 +7,11 @@ import type { ReferenceLineProps as RechartsReferenceLineProps, } from 'recharts'; import type { ComponentProps } from 'react'; -import type { ChartColorCategories, ChartCategoricalEmphasis } from '~tokens/theme/theme'; +import type { + ChartColorCategories, + ChartCategoricalEmphasis, + ChartSequentialEmphasis, +} from '~tokens/theme/theme'; type ChartReferenceLineProps = { /** @@ -55,6 +59,11 @@ type ChartCartesianGridProps = Omit< type ChartsCategoricalColorToken = `chart.background.categorical.${ChartColorCategories}.${keyof ChartCategoricalEmphasis}`; +type ChartSequentialColorToken = `chart.background.sequential.${Exclude< + ChartColorCategories, + 'gray' +>}.${keyof ChartSequentialEmphasis}`; + export type { ChartReferenceLineProps, ChartXAxisProps, @@ -63,4 +72,5 @@ export type { ChartLegendProps, ChartCartesianGridProps, ChartsCategoricalColorToken, + ChartSequentialColorToken, }; diff --git a/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx b/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx deleted file mode 100644 index 0230e06ee74..00000000000 --- a/packages/blade/src/components/Charts/LineCharts/LineCharts.stories.tsx +++ /dev/null @@ -1,291 +0,0 @@ -import type { StoryFn, Meta } from '@storybook/react'; -import React from 'react'; -import { - XAxis, - YAxis, - CartesianGrid, - ChartToolTip, - Legend, - ReferenceLine, -} from '../BaseChartComponents'; -import { LineChart, Line } from './lineCharts'; -import { Heading } from '~components/Typography/Heading'; -import { Sandbox } from '~utils/storybook/Sandbox'; -import StoryPageWrapper from '~utils/storybook/StoryPageWrapper'; -import { getStyledPropsArgTypes } from '~components/Box/BaseBox/storybookArgTypes'; - -const Page = (): React.ReactElement => { - return ( - - Usage - - {` - import { - LineChart, - Line, - XAxis, - YAxis, - CartesianGrid, - Tooltip, - Legend, - ResponsiveContainer - } from '@razorpay/blade/components'; - - function App() { - const data = [ - { name: 'Jan', sales: 4000 }, - { name: 'Feb', sales: 3000 }, - { name: 'Mar', sales: 2000 }, - ]; - - return ( - - - - - - - - - ) - } - - export default App; - `} - - - ); -}; - -export default { - title: 'Components/Charts/LineChart', - component: LineChart, - tags: ['autodocs'], - argTypes: { - ...getStyledPropsArgTypes(), - }, - parameters: { - docs: { - page: Page, - }, - }, -} as Meta; - -// Sample data for charts -const chartData = [ - { month: 'Jan', teamA: 4000, teamB: 2400 }, - { month: 'Feb', teamA: 3000, teamB: 1398 }, - { month: 'Mar', teamA: 2000, teamB: 9800 }, - { month: 'Apr', teamA: 2780, teamB: 3908 }, - { month: 'May', teamA: 1890, teamB: 4800 }, - { month: 'Jun', teamA: 2390, teamB: 3800 }, -]; - -const forecastData = [ - { date: 'Jan', historical: 4000, forecast: null }, - { date: 'Feb', historical: 3000, forecast: null }, - { date: 'Mar', historical: 2000, forecast: null }, - { date: 'Apr', historical: 2500, forecast: 2500 }, - { date: 'May', historical: null, forecast: 4000 }, - { date: 'Jun', historical: null, forecast: 2390 }, -]; - -// Data with null values for connectNulls example -const dataWithNulls = [ - { month: 'Jan', sales: 4000 }, - { month: 'Feb', sales: 3000 }, - { month: 'Mar', sales: 5000 }, - { month: 'Apr', sales: null }, - { month: 'May', sales: 1890 }, - { month: 'Jun', sales: 2390 }, -]; - -// Data for stepped line chart example -const steppedData = [ - { month: 'Jan', value: 100 }, - { month: 'Feb', value: 150 }, - { month: 'Mar', value: 120 }, - { month: 'Apr', value: 200 }, - { month: 'May', value: 180 }, - { month: 'Jun', value: 250 }, -]; - -// Simple Line Chart Example -export const SimpleLineChart: StoryFn = () => { - return ( -
      - - - - - - - - - - -
      - ); -}; - -// Tiny Line Chart Example (no dots for cleaner look) -export const TinyLineChart: StoryFn = () => { - return ( -
      - - - -
      - ); -}; - -// Forecast Line Chart Example -export const ForecastLineChart: StoryFn = () => { - return ( -
      - - - - - - - - - -
      - ); -}; - -// Line Chart that Connects Nulls -export const LineChartConnectNulls: StoryFn = () => { - return ( -
      - Line Chart that do not Connects Nulls (default) - - - - - - - - - Line Chart that Connects Nulls - - - - - - - - -
      - ); -}; - -// Stepped Line Chart Example -export const SteppedLineChart: StoryFn = () => { - return ( -
      - - - - - - - - -
      - ); -}; - -// Line Chart with Default Color Theme -export const LineChartWithDefaultColorTheme: StoryFn = () => { - return ( -
      - - - - - - - - - -
      - ); -}; - -//Line Chart with X and Y axis labels -export const LineChartWithXAndYAxisLabels: StoryFn = () => { - return ( -
      - - - - - - - - -
      - ); -}; - -SimpleLineChart.storyName = 'Simple Line Chart'; -TinyLineChart.storyName = 'Tiny Line Chart'; -ForecastLineChart.storyName = 'Forecast Line Chart'; -LineChartConnectNulls.storyName = 'Line Chart (Connect Nulls)'; -SteppedLineChart.storyName = 'Stepped Line Chart'; -LineChartWithDefaultColorTheme.storyName = 'Line Chart with Color Theme'; -LineChartWithXAndYAxisLabels.storyName = 'Line Chart with X and Y axis labels'; diff --git a/packages/blade/src/components/Charts/LineCharts/index.ts b/packages/blade/src/components/Charts/LineCharts/index.ts deleted file mode 100644 index f650d2fd001..00000000000 --- a/packages/blade/src/components/Charts/LineCharts/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export { LineChart, Line } from './lineCharts'; - -export type { LineChartProps, LineProps } from './lineCharts'; diff --git a/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx b/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx deleted file mode 100644 index d13d04387aa..00000000000 --- a/packages/blade/src/components/Charts/LineCharts/lineCharts.tsx +++ /dev/null @@ -1,128 +0,0 @@ -import React from 'react'; -import type { ComponentProps } from 'react'; -import { - LineChart as RechartsLineChart, - Line as RechartsLine, - ResponsiveContainer as RechartsResponsiveContainer, -} from 'recharts'; -import type { LineProps as RechartsLineProps } from 'recharts'; -import { useChartsColorTheme } from '../utils'; -import { useTheme } from '~components/BladeProvider'; -import { metaAttribute } from '~utils/metaAttribute'; -import BaseBox from '~components/Box/BaseBox'; -import type { ChartColorCategories, ChartCategoricalEmphasis } from '~tokens/theme/theme'; -import getIn from '~utils/lodashButBetter/get'; -import { throwBladeError } from '~utils/logger'; - -const MAX_LINES = 10; - -export type BladeColorToken = `chart.background.categorical.${ChartColorCategories}.${keyof ChartCategoricalEmphasis}`; - -// Chart-specific interfaces based on user specifications -interface LineProps { - type?: 'step' | 'stepAfter' | 'stepBefore' | 'linear' | 'monotone'; - dot?: RechartsLineProps['dot']; - activeDot?: RechartsLineProps['activeDot']; - connectNulls?: boolean; - showLegend?: boolean; - dataKey: string; - name?: string; - color?: BladeColorToken; - strokeStyle?: 'dotted' | 'dashed' | 'solid'; - /** - * @private - */ - _index?: number; // Add this for internal use - /** - * @private - */ - _colorTheme?: 'default' | 'informational'; -} - -// TypeScript prop types -type LineChartProps = ComponentProps & { - colorTheme?: 'default' | 'informational'; -}; - -export interface ReferenceLineProps { - y?: number; - x?: number; - label?: string; - color?: BladeColorToken; - labelPosition?: 'left' | 'right' | 'top' | 'bottom'; - labelOffset?: number; -} - -const Line: React.FC = ({ - color, - strokeStyle = 'solid', - type = 'monotone', - dot = false, - activeDot = false, - showLegend = true, - _index, - _colorTheme, - ...props -}) => { - const { theme } = useTheme(); - const themeColors = useChartsColorTheme({ colorTheme: _colorTheme ?? 'default' }); - const colorToken = color ? getIn(theme.colors, color) : themeColors[_index ?? 0]; - - const strokeDasharray = - strokeStyle === 'dashed' ? '5 5' : strokeStyle === 'dotted' ? '2 2' : undefined; - - const isLineDotted = strokeStyle === 'dashed'; - const animationBegin = isLineDotted - ? theme.motion.delay.gentle + theme.motion.duration.xgentle - : theme.motion.delay.gentle; - const animationDuration = theme.motion.duration.xgentle; - - return ( - - ); -}; - -// Main components -const LineChart: React.FC = ({ children, colorTheme = 'default', ...props }) => { - const lineChartModifiedChildrens = React.useMemo(() => { - let LineChartIndex = 0; - return React.Children.map(children, (child) => { - if (__DEV__ && LineChartIndex >= MAX_LINES) { - throwBladeError({ - message: `Too many lines configured. Maximum allowed is ${MAX_LINES}.`, - moduleName: 'LineChart', - }); - } - - if (React.isValidElement(child) && child.type === Line) { - return React.cloneElement(child, { - _index: LineChartIndex++, - _colorTheme: colorTheme, - } as Partial); - } - return child; - }); - }, [children, colorTheme]); - - return ( - - - {lineChartModifiedChildrens} - - - ); -}; - -export type { LineChartProps, LineProps }; -export { LineChart, Line }; diff --git a/packages/blade/src/components/Charts/index.ts b/packages/blade/src/components/Charts/index.ts index b4294a425d4..4e718607a53 100644 --- a/packages/blade/src/components/Charts/index.ts +++ b/packages/blade/src/components/Charts/index.ts @@ -1,4 +1,4 @@ -// Export LineCharts (includes shared components) export * from './LineChart'; export * from './AreaChart'; +export * from './BarCharts'; export * from './CommonChartComponents'; From 2b3e8f4b7ac2f6fe0fe12883093e666d09d9f30f Mon Sep 17 00:00:00 2001 From: tewarig Date: Fri, 19 Sep 2025 00:12:03 +0530 Subject: [PATCH 072/105] fix: add area chart types --- .../Charts/AreaChart/AreaCharts.tsx | 141 ------------------ 1 file changed, 141 deletions(-) delete mode 100644 packages/blade/src/components/Charts/AreaChart/AreaCharts.tsx diff --git a/packages/blade/src/components/Charts/AreaChart/AreaCharts.tsx b/packages/blade/src/components/Charts/AreaChart/AreaCharts.tsx deleted file mode 100644 index 6845b5467de..00000000000 --- a/packages/blade/src/components/Charts/AreaChart/AreaCharts.tsx +++ /dev/null @@ -1,141 +0,0 @@ -import React from 'react'; -import type { ComponentProps } from 'react'; -import styled from 'styled-components'; -import { - AreaChart as RechartsAreaChart, - Area as RechartsArea, - ResponsiveContainer, -} from 'recharts'; -import type { AreaProps as RechartAreaProps } from 'recharts'; - -import { useChartsColorTheme } from '../utils'; -import { useTheme } from '~components/BladeProvider'; -import type { Theme } from '~components/BladeProvider'; -import type { StyledPropsBlade } from '~components/Box/styledProps'; -import { getStyledProps } from '~components/Box/styledProps'; -import { metaAttribute } from '~utils/metaAttribute'; -import BaseBox from '~components/Box/BaseBox'; -import type { ChartColorCategories, ChartCategoricalEmphasis } from '~tokens/theme/theme'; -import getIn from '~utils/lodashButBetter/get'; -import { throwBladeError } from '~utils/logger'; - -const MAX_AREAS = 10; -// BladeColorToken type for charts - only allows categorical chart colors for area charts -export type BladeColorToken = `chart.background.categorical.${ChartColorCategories}.${keyof ChartCategoricalEmphasis}`; - -// Chart-specific interfaces based on user specifications -export interface AreaProps { - type?: 'step' | 'stepAfter' | 'stepBefore' | 'linear' | 'monotone'; - connectNulls?: boolean; - showLegend?: boolean; - dataKey: string; - name: string; - stackId?: string | number; - color?: BladeColorToken; - dot?: RechartAreaProps['dot']; - activeDot?: RechartAreaProps['activeDot']; - /** - * @private - */ - _index?: number; // Add this for internal use - /** - * @private - */ - _colorTheme?: 'default' | 'informational'; -} - -export const Area: React.FC = ({ - color, - type = 'monotone', - connectNulls = false, - showLegend = true, - stackId = 1, - dot = false, - activeDot = false, - _index, - _colorTheme, - ...props -}) => { - const { theme } = useTheme(); - const themeColors = useChartsColorTheme({ colorTheme: _colorTheme ?? 'default' }); - const colorToken = color ? getIn(theme.colors, color) : themeColors[_index ?? 0]; - const animationBegin = theme.motion.delay.gentle; - const animationDuration = theme.motion.duration.xgentle; - - return ( - - ); -}; - -// TypeScript prop types -export type AreaChartProps = Omit, 'margin'> & - StyledPropsBlade & { - children?: React.ReactNode; - colorTheme?: 'default' | 'informational'; - }; - -// Styled wrapper for AreaChart with predefined margins -const StyledAreaChart = styled(RechartsAreaChart)<{ theme: Theme }>` - font-family: ${(props) => props.theme.typography.fonts.family.text}; -`; - -// Main components -export const AreaChart: React.FC = ({ - children, - colorTheme = 'default', - ...props -}) => { - const { theme } = useTheme(); - const styledProps = getStyledProps(props); - - // Predefined margins - not exposed to user - const defaultMargin = { - top: 16, - right: 16, - bottom: 16, - left: 16, - }; - - const modifiedChildren = React.useMemo(() => { - let AreaChartIndex = 0; - return React.Children.map(children, (child) => { - if (__DEV__ && AreaChartIndex >= MAX_AREAS) { - throwBladeError({ - message: `Too many areas configured. Maximum allowed is ${MAX_AREAS}.`, - moduleName: 'AreaChart', - }); - } - if (React.isValidElement(child) && child.type === Area) { - return React.cloneElement(child, { - _index: AreaChartIndex++, - _colorTheme: colorTheme, - } as Partial); - } - return child; - }); - }, [children, colorTheme]); - - return ( - - - - {modifiedChildren} - - - - ); -}; From 4ae36ecb123a3c9607865de36dbd2a41f59dcb40 Mon Sep 17 00:00:00 2001 From: tewarig Date: Fri, 19 Sep 2025 00:20:55 +0530 Subject: [PATCH 073/105] feat: add test cases --- .../BarCharts/__tests__/BarChart.web.test.tsx | 101 + .../__snapshots__/BarChart.web.test.tsx.snap | 1701 +++++++++++++++++ 2 files changed, 1802 insertions(+) create mode 100644 packages/blade/src/components/Charts/BarCharts/__tests__/__snapshots__/BarChart.web.test.tsx.snap diff --git a/packages/blade/src/components/Charts/BarCharts/__tests__/BarChart.web.test.tsx b/packages/blade/src/components/Charts/BarCharts/__tests__/BarChart.web.test.tsx index e69de29bb2d..320ccc67bef 100644 --- a/packages/blade/src/components/Charts/BarCharts/__tests__/BarChart.web.test.tsx +++ b/packages/blade/src/components/Charts/BarCharts/__tests__/BarChart.web.test.tsx @@ -0,0 +1,101 @@ +import React from 'react'; +import { ChartBarWrapper, ChartBar } from '../BarCharts'; +import { + ChartXAxis, + ChartYAxis, + ChartCartesianGrid, + ChartTooltip, + ChartLegend, +} from '../../CommonChartComponents'; +import renderWithTheme from '~utils/testing/renderWithTheme.web'; +import { Box } from '~components/Box/Box'; + +const mockData = [ + { name: 'Jan', sales: 4000, profit: 2000, revenue: 6000 }, + { name: 'Feb', sales: 3000, profit: 1500, revenue: 4500 }, + { name: 'Mar', sales: 2000, profit: 1000, revenue: 3000 }, + { name: 'Apr', sales: 5000, profit: 2500, revenue: 7500 }, +]; + +// Mock recharts ResponsiveContainer for consistent testing +// Thanks to : https://jskim1991.medium.com/react-writing-tests-with-graphs-9b7f2c9eeefc + +jest.mock('recharts', () => { + const OriginalModule = jest.requireActual('recharts'); + return { + ...OriginalModule, + ResponsiveContainer: ({ children }: { children: React.ReactNode }) => ( + + {children} + + ), + }; +}); + +describe('', () => { + it('should render basic BarChart with single bar', () => { + const { container } = renderWithTheme( + + + + + , + ); + expect(container).toMatchSnapshot(); + }); + + it('should render BarChart with multiple bars', () => { + const { container } = renderWithTheme( + + + + + + + , + ); + expect(container).toMatchSnapshot(); + }); + + it('should render stacked BarChart', () => { + const { container } = renderWithTheme( + + + + + + + , + ); + expect(container).toMatchSnapshot(); + }); + + it('should render BarChart with custom colors', () => { + const { container } = renderWithTheme( + + + + + + , + ); + expect(container).toMatchSnapshot(); + }); + + it('should render complete chart with all components', () => { + const { container } = renderWithTheme( + + + + + + + + + + + , + ); + expect(container).toMatchSnapshot(); + }); +}); diff --git a/packages/blade/src/components/Charts/BarCharts/__tests__/__snapshots__/BarChart.web.test.tsx.snap b/packages/blade/src/components/Charts/BarCharts/__tests__/__snapshots__/BarChart.web.test.tsx.snap new file mode 100644 index 00000000000..4a2731d8ac5 --- /dev/null +++ b/packages/blade/src/components/Charts/BarCharts/__tests__/__snapshots__/BarChart.web.test.tsx.snap @@ -0,0 +1,1701 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` should render BarChart with custom colors 1`] = ` +.c0.c0.c0.c0.c0 { + height: 500px; + width: 500px; +} + +.c1.c1.c1.c1.c1 { + height: 100%; + width: 100%; +} + +
      +
      +
      +
      +
      +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +
      +
      +`; + +exports[` should render BarChart with multiple bars 1`] = ` +.c0.c0.c0.c0.c0 { + height: 500px; + width: 500px; +} + +.c1.c1.c1.c1.c1 { + height: 100%; + width: 100%; +} + +
      +
      +
      +
      +
      +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +
      +
      +`; + +exports[` should render basic BarChart with single bar 1`] = ` +.c0.c0.c0.c0.c0 { + height: 500px; + width: 500px; +} + +.c1.c1.c1.c1.c1 { + height: 100%; + width: 100%; +} + +
      +
      +
      +
      +
      +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +
      +
      +`; + +exports[` should render complete chart with all components 1`] = ` +.c0.c0.c0.c0.c0 { + height: 500px; + width: 500px; +} + +.c1.c1.c1.c1.c1 { + height: 100%; + width: 100%; +} + +.c3.c3.c3.c3.c3 { + padding-top: 12px; +} + +.c4.c4.c4.c4.c4 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-pack: center; + -webkit-justify-content: center; + -ms-flex-pack: center; + justify-content: center; + gap: 16px; +} + +.c5.c5.c5.c5.c5 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c6.c6.c6.c6.c6 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: center; + -webkit-justify-content: center; + -ms-flex-pack: center; + justify-content: center; + gap: 8px; +} + +.c2.c2.c2.c2.c2 { + color: hsla(0,0%,100%,1); + font-family: "TASA Orbiter","TASA Orbiter Fallback Arial",Arial; + font-size: 1.125rem; + font-weight: 600; + font-style: normal; + -webkit-text-decoration-line: none; + text-decoration-line: none; + line-height: 1.5rem; + -webkit-letter-spacing: 0px; + -moz-letter-spacing: 0px; + -ms-letter-spacing: 0px; + letter-spacing: 0px; + margin: 0; + padding: 0; +} + +.c7.c7.c7.c7.c7 { + color: hsla(211,22%,56%,1); + font-family: "Inter","Inter Fallback Arial",Arial; + font-size: 0.875rem; + font-weight: 400; + font-style: normal; + -webkit-text-decoration-line: none; + text-decoration-line: none; + line-height: 1.25rem; + -webkit-letter-spacing: 0px; + -moz-letter-spacing: 0px; + -ms-letter-spacing: 0px; + letter-spacing: 0px; + margin: 0; + padding: 0; +} + +
      +
      +
      +
      +
      +
      + +
      +
      +
      +
      +
      +`; + +exports[` should render stacked BarChart 1`] = ` +.c0.c0.c0.c0.c0 { + height: 500px; + width: 500px; +} + +.c1.c1.c1.c1.c1 { + height: 100%; + width: 100%; +} + +
      +
      +
      +
      +
      +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +
      +
      +`; From 2afee818eda79a2cee906abbfe32649ecfa9ee9b Mon Sep 17 00:00:00 2001 From: tewarig Date: Fri, 19 Sep 2025 00:30:34 +0530 Subject: [PATCH 074/105] feat: add bar chart --- .../knowledgebase/components/BarChart.md | 180 ++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 packages/blade-mcp/knowledgebase/components/BarChart.md diff --git a/packages/blade-mcp/knowledgebase/components/BarChart.md b/packages/blade-mcp/knowledgebase/components/BarChart.md new file mode 100644 index 00000000000..c89388761d9 --- /dev/null +++ b/packages/blade-mcp/knowledgebase/components/BarChart.md @@ -0,0 +1,180 @@ +# BarChart + +## Component Name + +BarChart + +## Description + +BarChart is a comprehensive data visualization component that renders interactive bar charts with support for grouped, stacked, and vertical layouts. It provides customizable colors, animations, and interactive features like hover states and tooltips. The component is built on top of Recharts and integrates seamlessly with Blade's design system, offering consistent styling and accessibility features. BarChart supports multiple data series, custom color themes, and various chart configurations for displaying categorical data effectively. + +## Important Constraints + +- `ChartBarWrapper` component only accepts `ChartBar` components as direct children +- `data` prop is required and must be an array of objects with consistent data structure +- `dataKey` prop is required for each `ChartBar` component and must correspond to a property in the data array +- `stackId` must be consistent across all bars that should be stacked together +- `layout="vertical"` requires `ChartXAxis` to have `type="number"` and `ChartYAxis` to have `type="category"` +- Color tokens must follow the exact format: `chart.background.categorical.{color}.{emphasis}` or `chart.background.sequential.{color}.{number}` + +## TypeScript Types + +These types define the props that the BarChart component and its subcomponents accept: + +```typescript +type ChartBarProps = Omit & { + /** + * The data key of the bar chart. + */ + dataKey: RechartsBarProps['dataKey']; + /** + * The name of the bar chart. + */ + name?: RechartsBarProps['name']; + /** + * The color of the bar chart. + */ + color?: ChartsCategoricalColorToken | ChartSequentialColorToken; + /** + * The stack id of the bar chart. + */ + stackId?: RechartsBarProps['stackId']; + /** + * The active bar of the bar chart. + */ + activeBar?: RechartsBarProps['activeBar']; + /** + * The label of the bar chart. + */ + label?: RechartsBarProps['label']; +}; + +type data = { + [key: string]: unknown; +}; + +type ChartBarWrapperProps = { + children?: React.ReactNode; + /** + * The color theme of the bar chart. + */ + colorTheme?: colorTheme; + /** + * The layout of the bar chart. + */ + layout?: 'horizontal' | 'vertical'; + /** + * Chart data to be rendered + */ + data: data[]; +} & Partial>; + +interface BarChartContextType { + layout?: 'horizontal' | 'vertical'; + activeIndex?: number; + colorTheme: colorTheme; + totalBars: number; +} + +type ChartsCategoricalColorToken = `chart.background.categorical.${ChartColorCategories}.${keyof ChartCategoricalEmphasis}`; + +type ChartSequentialColorToken = `chart.background.sequential.${Exclude}.${keyof ChartSequentialEmphasis}`; + +type colorTheme = 'default'; + +type ChartXAxisProps = Omit & { + /** + * The label of the x-axis. + */ + label?: string; + /** + * The data key of the x-axis. + */ + dataKey?: string; +}; + +type ChartYAxisProps = Omit & { + /** + * The label of the y-axis. + */ + label?: string; + /** + * The data key of the y-axis. + */ + dataKey?: string; +}; + +type ChartTooltipProps = ComponentProps; + +type ChartLegendProps = ComponentProps; + +type ChartCartesianGridProps = Omit; + +type ChartReferenceLineProps = { + /** + * The y-coordinate of the reference line. + */ + y?: RechartsReferenceLineProps['y']; + /** + * The x-coordinate of the reference line. + */ + x?: RechartsReferenceLineProps['x']; + /** + * The label of the reference line. + */ + label: string; +}; +``` + +## Example + +### Basic BarChart with Multiple Series + +```tsx +import React from 'react'; +import { + ChartBar, + ChartBarWrapper, + ChartXAxis, + ChartYAxis, + ChartCartesianGrid, + ChartTooltip, + ChartLegend, +} from '@razorpay/blade/components'; + +const salesData = [ + { month: 'Jan', revenue: 4000, profit: 2000, expenses: 1000 }, + { month: 'Feb', revenue: 3000, profit: 1500, expenses: 800 }, + { month: 'Mar', revenue: 5000, profit: 3000, expenses: 1200 }, + { month: 'Apr', revenue: 4500, profit: 2500, expenses: 1100 }, +]; + +const BasicBarChart = () => { + return ( +
      + + + + + + + + + + +
      + ); +}; +``` \ No newline at end of file From 4083df813b3b20bd917504256da2cf6d36844aca Mon Sep 17 00:00:00 2001 From: tewarig Date: Fri, 19 Sep 2025 00:36:47 +0530 Subject: [PATCH 075/105] feat: add changset --- .changeset/calm-peas-work.md | 5 ----- .changeset/fluffy-impalas-raise.md | 4 +++- .changeset/red-ears-check.md | 5 ----- .changeset/yellow-lamps-sneeze.md | 5 +++++ 4 files changed, 8 insertions(+), 11 deletions(-) delete mode 100644 .changeset/calm-peas-work.md delete mode 100644 .changeset/red-ears-check.md create mode 100644 .changeset/yellow-lamps-sneeze.md diff --git a/.changeset/calm-peas-work.md b/.changeset/calm-peas-work.md deleted file mode 100644 index 64cf2d58b8d..00000000000 --- a/.changeset/calm-peas-work.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@razorpay/blade": minor ---- - -feat: add line charts diff --git a/.changeset/fluffy-impalas-raise.md b/.changeset/fluffy-impalas-raise.md index 28d7af2b4de..b8963c358bc 100644 --- a/.changeset/fluffy-impalas-raise.md +++ b/.changeset/fluffy-impalas-raise.md @@ -2,4 +2,6 @@ '@razorpay/blade': minor --- -feat: add bar chart +feat(blade): add BarChart component + +[Docs Link](https://blade.razorpay.com/?path=/docs/components-charts-barchart--docs) \ No newline at end of file diff --git a/.changeset/red-ears-check.md b/.changeset/red-ears-check.md deleted file mode 100644 index b9f204423a3..00000000000 --- a/.changeset/red-ears-check.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@razorpay/blade': minor ---- - -feat: add area chart diff --git a/.changeset/yellow-lamps-sneeze.md b/.changeset/yellow-lamps-sneeze.md new file mode 100644 index 00000000000..ea700a5c299 --- /dev/null +++ b/.changeset/yellow-lamps-sneeze.md @@ -0,0 +1,5 @@ +--- +'@razorpay/blade-mcp': minor +--- + +feat(blade-mcp): update knowledgebase with BarChart From be35727c9b857a38d068a76eb9c6dad31dd8d80a Mon Sep 17 00:00:00 2001 From: tewarig Date: Fri, 19 Sep 2025 15:37:26 +0530 Subject: [PATCH 076/105] fix: update bar chart --- .../Charts/AreaChart/AreaChart.stories.tsx | 2 +- .../Charts/BarCharts/BarCharts.stories.tsx | 143 ++++++++++++++++++ .../src/components/Charts/BarCharts/types.ts | 2 +- .../Charts/LineChart/LineChart.stories.tsx | 2 +- 4 files changed, 146 insertions(+), 3 deletions(-) diff --git a/packages/blade/src/components/Charts/AreaChart/AreaChart.stories.tsx b/packages/blade/src/components/Charts/AreaChart/AreaChart.stories.tsx index 18aafbb233b..27547d948bb 100644 --- a/packages/blade/src/components/Charts/AreaChart/AreaChart.stories.tsx +++ b/packages/blade/src/components/Charts/AreaChart/AreaChart.stories.tsx @@ -21,7 +21,7 @@ const Page = (): React.ReactElement => { componentName="AreaChart" componentDescription="An Area Chart component built on top of Recharts with Blade design system styling." apiDecisionLink={ - 'https://github.com/razorpay/blade/blob/5920fbd32c70793454f8c8c6ff544b2a7413afb5/packages/blade/src/components/Charts/_decisions/decisions.md' + 'https://github.com/razorpay/blade/blob/master/packages/blade/src/components/Charts/_decisions/decisions.md' } figmaURL="https://www.figma.com/design/jubmQL9Z8V7881ayUD95ps/Blade-DSL?node-id=92678-188717&p=f&m=dev" > diff --git a/packages/blade/src/components/Charts/BarCharts/BarCharts.stories.tsx b/packages/blade/src/components/Charts/BarCharts/BarCharts.stories.tsx index 3cd5ca9ac6b..11020b53548 100644 --- a/packages/blade/src/components/Charts/BarCharts/BarCharts.stories.tsx +++ b/packages/blade/src/components/Charts/BarCharts/BarCharts.stories.tsx @@ -9,11 +9,105 @@ import { ChartTooltip, ChartLegend, } from '~components/Charts'; +import { Heading } from '~components/Typography/Heading'; +import { Sandbox } from '~utils/storybook/Sandbox'; +import StoryPageWrapper from '~utils/storybook/StoryPageWrapper'; + +const Page = (): React.ReactElement => { + return ( + + Usage + + {` + import { + ChartBar, + ChartBarWrapper, + ChartXAxis, + ChartYAxis, + ChartCartesianGrid, + ChartTooltip, + ChartLegend, + } from '@razorpay/blade/components'; + + function App() { + const data = [ + { name: 'Jan', sales: 4000 }, + { name: 'Feb', sales: 3000 }, + { name: 'Mar', sales: 2000 }, + ]; + + return ( + + + + + + + + + ) + } + + export default App; + `} + + + ); +}; + +const propsCategory = { + CHART_BAR_PROPS: 'ChartBar Props', +}; export default { title: 'Components/Charts/BarChart', component: ChartBar, tags: ['autodocs'], + argTypes: { + dataKey: { + control: { type: 'text' }, + table: { + category: propsCategory.CHART_BAR_PROPS, + }, + }, + name: { + control: { type: 'text' }, + table: { + category: propsCategory.CHART_BAR_PROPS, + }, + }, + color: { + control: { type: 'text' }, + table: { + category: propsCategory.CHART_BAR_PROPS, + }, + }, + stackId: { + control: { type: 'text' }, + table: { + category: propsCategory.CHART_BAR_PROPS, + }, + }, + // Hide private props from Storybook + _index: { + table: { disable: true }, + }, + _colorTheme: { + table: { disable: true }, + }, + }, + parameters: { + docs: { + page: Page, + }, + }, } as Meta; const chartData = [ @@ -31,6 +125,30 @@ const chartData = [ { name: 'Dec', seriesA: 1200, seriesB: 4600, seriesC: 2000 }, ]; +export const DefaultChart: StoryFn = ({ + dataKey = 'seriesA', + name = 'Series A', + ...props +}) => { + return ( +
      + + + + + + + + +
      + ); +}; + export const TinyBarChart: StoryFn = () => { return (
      @@ -41,6 +159,10 @@ export const TinyBarChart: StoryFn = () => { ); }; +TinyBarChart.parameters = { + controls: { disable: true }, +}; + export const SimpleBarChart: StoryFn = () => { return (
      @@ -65,6 +187,10 @@ export const SimpleBarChart: StoryFn = () => { ); }; +SimpleBarChart.parameters = { + controls: { disable: true }, +}; + export const StackedBarChart: StoryFn = () => { return (
      @@ -97,6 +223,10 @@ export const StackedBarChart: StoryFn = () => { ); }; +StackedBarChart.parameters = { + controls: { disable: true }, +}; + export const GroupedBarChart: StoryFn = () => { return (
      @@ -122,6 +252,10 @@ export const GroupedBarChart: StoryFn = () => { ); }; +GroupedBarChart.parameters = { + controls: { disable: true }, +}; + export const VerticalBarChart: StoryFn = () => { return (
      @@ -154,6 +288,10 @@ export const VerticalBarChart: StoryFn = () => { ); }; +VerticalBarChart.parameters = { + controls: { disable: true }, +}; + export const BarChartWithInformationalColorTheme: StoryFn = () => { return (
      @@ -171,6 +309,11 @@ export const BarChartWithInformationalColorTheme: StoryFn = () ); }; +BarChartWithInformationalColorTheme.parameters = { + controls: { disable: true }, +}; + +DefaultChart.storyName = 'Default Bar Chart'; TinyBarChart.storyName = 'Tiny Bar Chart'; SimpleBarChart.storyName = 'Simple Bar Chart'; StackedBarChart.storyName = 'Stacked Bar Chart'; diff --git a/packages/blade/src/components/Charts/BarCharts/types.ts b/packages/blade/src/components/Charts/BarCharts/types.ts index 6fdeef645c7..e0f0d08972d 100644 --- a/packages/blade/src/components/Charts/BarCharts/types.ts +++ b/packages/blade/src/components/Charts/BarCharts/types.ts @@ -10,7 +10,7 @@ import type { GridProps, } from '~components/Box/BaseBox/types/propsTypes'; -type ChartBarProps = Omit & { +type ChartBarProps = { /** * The data key of the bar chart. */ diff --git a/packages/blade/src/components/Charts/LineChart/LineChart.stories.tsx b/packages/blade/src/components/Charts/LineChart/LineChart.stories.tsx index 10fa0cdd0dc..2196427a5c7 100644 --- a/packages/blade/src/components/Charts/LineChart/LineChart.stories.tsx +++ b/packages/blade/src/components/Charts/LineChart/LineChart.stories.tsx @@ -19,7 +19,7 @@ const Page = (): React.ReactElement => { Usage From 023b593e860c601c9c25e2beff3396c14fb7b73f Mon Sep 17 00:00:00 2001 From: tewarig Date: Fri, 19 Sep 2025 17:26:09 +0530 Subject: [PATCH 077/105] fix: opacity --- .../blade/src/components/Charts/BarCharts/BarCharts.web.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/blade/src/components/Charts/BarCharts/BarCharts.web.tsx b/packages/blade/src/components/Charts/BarCharts/BarCharts.web.tsx index 00375275873..fe5c3ff0253 100644 --- a/packages/blade/src/components/Charts/BarCharts/BarCharts.web.tsx +++ b/packages/blade/src/components/Charts/BarCharts/BarCharts.web.tsx @@ -64,7 +64,7 @@ const _ChartBar: React.FC = ({ // high change we might not have this in updated designs. shape={(props: unknown) => { const { fill, x, y, width, height, index: barIndex } = props as RechartsShapeProps; - const fillOpacity = isNumber(activeIndex) ? (barIndex === activeIndex ? 1 : 0.4) : 1; + const fillOpacity = isNumber(activeIndex) ? (barIndex === activeIndex ? 1 : 0.8) : 1; const gap = DISTANCE_BETWEEN_STACKED_BARS; const isVertical = layout === 'vertical'; From eff08348a804c1023304c708a14dce0f32b8779f Mon Sep 17 00:00:00 2001 From: tewarig Date: Fri, 19 Sep 2025 19:56:49 +0530 Subject: [PATCH 078/105] fix: bar chart review --- .../blade/src/components/Charts/BarCharts/BarCharts.web.tsx | 2 +- .../Charts/CommonChartComponents/CommonChartComponents.web.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/blade/src/components/Charts/BarCharts/BarCharts.web.tsx b/packages/blade/src/components/Charts/BarCharts/BarCharts.web.tsx index fe5c3ff0253..19c863273ef 100644 --- a/packages/blade/src/components/Charts/BarCharts/BarCharts.web.tsx +++ b/packages/blade/src/components/Charts/BarCharts/BarCharts.web.tsx @@ -63,7 +63,7 @@ const _ChartBar: React.FC = ({ // we have this shape to have distance b/w bar charts // high change we might not have this in updated designs. shape={(props: unknown) => { - const { fill, x, y, width, height, index: barIndex } = props as RechartsShapeProps; + const { fill, x, y, w idth, height, index: barIndex } = props as RechartsShapeProps; const fillOpacity = isNumber(activeIndex) ? (barIndex === activeIndex ? 1 : 0.8) : 1; const gap = DISTANCE_BETWEEN_STACKED_BARS; const isVertical = layout === 'vertical'; diff --git a/packages/blade/src/components/Charts/CommonChartComponents/CommonChartComponents.web.tsx b/packages/blade/src/components/Charts/CommonChartComponents/CommonChartComponents.web.tsx index db7cb782872..0ff79ca5318 100644 --- a/packages/blade/src/components/Charts/CommonChartComponents/CommonChartComponents.web.tsx +++ b/packages/blade/src/components/Charts/CommonChartComponents/CommonChartComponents.web.tsx @@ -157,7 +157,7 @@ const ChartTooltip: React.FC = (props) => {
      ); }} - cursor={{ fill: 'rgba(0, 0, 0, 0.1)', stroke: theme.colors.surface.border.gray.muted }} + cursor={{ fill: 'transparent', stroke: 'transparent'}} {...props} /> ); From 213613e47a5c34d26345b1c61d1109c26c8d223b Mon Sep 17 00:00:00 2001 From: tewarig Date: Fri, 19 Sep 2025 19:57:25 +0530 Subject: [PATCH 079/105] fix: bar chart review --- .../blade/src/components/Charts/BarCharts/BarCharts.web.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/blade/src/components/Charts/BarCharts/BarCharts.web.tsx b/packages/blade/src/components/Charts/BarCharts/BarCharts.web.tsx index 19c863273ef..fe5c3ff0253 100644 --- a/packages/blade/src/components/Charts/BarCharts/BarCharts.web.tsx +++ b/packages/blade/src/components/Charts/BarCharts/BarCharts.web.tsx @@ -63,7 +63,7 @@ const _ChartBar: React.FC = ({ // we have this shape to have distance b/w bar charts // high change we might not have this in updated designs. shape={(props: unknown) => { - const { fill, x, y, w idth, height, index: barIndex } = props as RechartsShapeProps; + const { fill, x, y, width, height, index: barIndex } = props as RechartsShapeProps; const fillOpacity = isNumber(activeIndex) ? (barIndex === activeIndex ? 1 : 0.8) : 1; const gap = DISTANCE_BETWEEN_STACKED_BARS; const isVertical = layout === 'vertical'; From d02a39304fe0fe8b872a61c76b1c2ed33137d020 Mon Sep 17 00:00:00 2001 From: tewarig Date: Mon, 22 Sep 2025 10:08:42 +0530 Subject: [PATCH 080/105] chore: update mcp docs --- packages/blade-mcp/knowledgebase/components/BarChart.md | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/packages/blade-mcp/knowledgebase/components/BarChart.md b/packages/blade-mcp/knowledgebase/components/BarChart.md index c89388761d9..effae7f5c23 100644 --- a/packages/blade-mcp/knowledgebase/components/BarChart.md +++ b/packages/blade-mcp/knowledgebase/components/BarChart.md @@ -10,7 +10,7 @@ BarChart is a comprehensive data visualization component that renders interactiv ## Important Constraints -- `ChartBarWrapper` component only accepts `ChartBar` components as direct children +- `ChartBarWrapper` component only accepts `ChartBar`, `ChartXAxis`, `ChartYAxis`, `ChartCartesianGrid`, `ChartTooltip`, `ChartLegend`, and `ChartReferenceLine` components as children. - `data` prop is required and must be an array of objects with consistent data structure - `dataKey` prop is required for each `ChartBar` component and must correspond to a property in the data array - `stackId` must be consistent across all bars that should be stacked together @@ -69,12 +69,6 @@ type ChartBarWrapperProps = { data: data[]; } & Partial>; -interface BarChartContextType { - layout?: 'horizontal' | 'vertical'; - activeIndex?: number; - colorTheme: colorTheme; - totalBars: number; -} type ChartsCategoricalColorToken = `chart.background.categorical.${ChartColorCategories}.${keyof ChartCategoricalEmphasis}`; From ef9eda083e38f12417805b4be6478c8f348b0336 Mon Sep 17 00:00:00 2001 From: tewarig Date: Mon, 22 Sep 2025 10:54:58 +0530 Subject: [PATCH 081/105] chore: resolve review comments --- .../knowledgebase/components/BarChart.md | 4 ++++ .../Charts/BarCharts/BarCharts.web.tsx | 22 +++++++++++++------ .../src/components/Charts/BarCharts/tokens.ts | 7 ++++-- .../src/components/Charts/BarCharts/types.ts | 9 ++++++-- 4 files changed, 31 insertions(+), 11 deletions(-) diff --git a/packages/blade-mcp/knowledgebase/components/BarChart.md b/packages/blade-mcp/knowledgebase/components/BarChart.md index effae7f5c23..1879fd5b1f9 100644 --- a/packages/blade-mcp/knowledgebase/components/BarChart.md +++ b/packages/blade-mcp/knowledgebase/components/BarChart.md @@ -47,6 +47,10 @@ type ChartBarProps = Omit = ({ dataKey, activeBar = false, label = false, + showLegend = true, _index = 0, ...rest }) => { @@ -52,7 +60,7 @@ const _ChartBar: React.FC = ({ = ({ }; const ChartBar = assignWithoutSideEffects(_ChartBar, { - componentId: componentIds.barChart, + componentId: componentIds.chartBar, }); // BarChart wrapper with default margin, auto-color assignment, and max bars guard @@ -118,7 +126,7 @@ const ChartBarWrapper: React.FC { let BarChartIndex = 0; const modifiedChildren = React.Children.map(children, (child) => { - if (React.isValidElement(child) && getComponentId(child) === componentIds.barChart) { + if (React.isValidElement(child) && getComponentId(child) === componentIds.chartBar) { return React.cloneElement(child, { _index: BarChartIndex++, } as Partial); @@ -142,9 +150,9 @@ const ChartBarWrapper: React.FC { setActiveIndex(state?.activeIndex ? Number(state?.activeIndex) : undefined); }} diff --git a/packages/blade/src/components/Charts/BarCharts/tokens.ts b/packages/blade/src/components/Charts/BarCharts/tokens.ts index cfaab2b44e1..33879a2bebf 100644 --- a/packages/blade/src/components/Charts/BarCharts/tokens.ts +++ b/packages/blade/src/components/Charts/BarCharts/tokens.ts @@ -1,9 +1,12 @@ // Arbitrary sequential limit per palette (we will not have this in updated design) const BAR_CHART_CORNER_RADIUS = 2; const DISTANCE_BETWEEN_STACKED_BARS = 2; +const BAR_SIZE = 49; +const DISTANCE_BETWEEN_BARS = 2; +const DISTANCE_BETWEEN_CATEGORY_BARS = 2; const componentIds = { - barChart: 'BarChart', + chartBar: 'ChartBar', }; -export { componentIds, DISTANCE_BETWEEN_STACKED_BARS, BAR_CHART_CORNER_RADIUS }; +export { componentIds, DISTANCE_BETWEEN_STACKED_BARS, BAR_CHART_CORNER_RADIUS, BAR_SIZE, DISTANCE_BETWEEN_BARS, DISTANCE_BETWEEN_CATEGORY_BARS }; diff --git a/packages/blade/src/components/Charts/BarCharts/types.ts b/packages/blade/src/components/Charts/BarCharts/types.ts index e0f0d08972d..ec90dc39e13 100644 --- a/packages/blade/src/components/Charts/BarCharts/types.ts +++ b/packages/blade/src/components/Charts/BarCharts/types.ts @@ -35,14 +35,19 @@ type ChartBarProps = { * The label of the bar chart. */ label?: RechartsBarProps['label']; - /* + /** + * The show legend of the bar chart. + */ + showLegend?: boolean; + /** + * The index of the bar chart. * @private */ _index?: number; }; type data = { - [key: string]: unknown; + [key: string]: string | number; }; type ChartBarWrapperProps = { From e0df6a2437a365933133ad9748b4bbbae3b06348 Mon Sep 17 00:00:00 2001 From: tewarig Date: Mon, 22 Sep 2025 10:55:54 +0530 Subject: [PATCH 082/105] fix:linting issue --- .../Charts/CommonChartComponents/CommonChartComponents.web.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/blade/src/components/Charts/CommonChartComponents/CommonChartComponents.web.tsx b/packages/blade/src/components/Charts/CommonChartComponents/CommonChartComponents.web.tsx index 0ff79ca5318..ca88a0b2ddf 100644 --- a/packages/blade/src/components/Charts/CommonChartComponents/CommonChartComponents.web.tsx +++ b/packages/blade/src/components/Charts/CommonChartComponents/CommonChartComponents.web.tsx @@ -157,7 +157,7 @@ const ChartTooltip: React.FC = (props) => {
      ); }} - cursor={{ fill: 'transparent', stroke: 'transparent'}} + cursor={{ fill: 'transparent', stroke: 'transparent' }} {...props} /> ); From b1daa9cba123ad1c540f67408b205c55f0b7a735 Mon Sep 17 00:00:00 2001 From: tewarig Date: Mon, 22 Sep 2025 11:12:41 +0530 Subject: [PATCH 083/105] chore: update lint --- packages/blade/src/components/Charts/BarCharts/tokens.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/blade/src/components/Charts/BarCharts/tokens.ts b/packages/blade/src/components/Charts/BarCharts/tokens.ts index 33879a2bebf..b5dbd62dcf0 100644 --- a/packages/blade/src/components/Charts/BarCharts/tokens.ts +++ b/packages/blade/src/components/Charts/BarCharts/tokens.ts @@ -9,4 +9,11 @@ const componentIds = { chartBar: 'ChartBar', }; -export { componentIds, DISTANCE_BETWEEN_STACKED_BARS, BAR_CHART_CORNER_RADIUS, BAR_SIZE, DISTANCE_BETWEEN_BARS, DISTANCE_BETWEEN_CATEGORY_BARS }; +export { + componentIds, + DISTANCE_BETWEEN_STACKED_BARS, + BAR_CHART_CORNER_RADIUS, + BAR_SIZE, + DISTANCE_BETWEEN_BARS, + DISTANCE_BETWEEN_CATEGORY_BARS, +}; From b5f4511b34763d2e43e382785fa724376267dc5d Mon Sep 17 00:00:00 2001 From: tewarig Date: Mon, 22 Sep 2025 12:49:02 +0530 Subject: [PATCH 084/105] chore: rename chartBarwrapper --- .../src/components/Charts/BarCharts/BarCharts.native.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/blade/src/components/Charts/BarCharts/BarCharts.native.tsx b/packages/blade/src/components/Charts/BarCharts/BarCharts.native.tsx index 3482df3dde3..38f8793ea9e 100644 --- a/packages/blade/src/components/Charts/BarCharts/BarCharts.native.tsx +++ b/packages/blade/src/components/Charts/BarCharts/BarCharts.native.tsx @@ -5,16 +5,16 @@ import { throwBladeError } from '~utils/logger'; const ChartBar = (_prop: ChartBarProps): React.ReactElement => { throwBladeError({ - message: 'ChartLineWrapper is not yet implemented for native', + message: 'ChartBar is not yet implemented for native', moduleName: 'ChartBar', }); - return ChartLineWrapper is not available for Native mobile apps.; + return ChartBar is not available for Native mobile apps.; }; const ChartBarWrapper = (_prop: ChartBarWrapperProps): React.ReactElement => { throwBladeError({ - message: 'ChartBarWrapperProps is not yet implemented for native', + message: 'ChartBarWrapper is not yet implemented for native', moduleName: 'ChartBarWrapper', }); From c07dff3089c18754c8a3b82bce2d47eb6ed2615e Mon Sep 17 00:00:00 2001 From: tewarig Date: Mon, 22 Sep 2025 16:37:06 +0530 Subject: [PATCH 085/105] fix: update opacity --- .../blade/src/components/Charts/BarCharts/BarCharts.web.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/blade/src/components/Charts/BarCharts/BarCharts.web.tsx b/packages/blade/src/components/Charts/BarCharts/BarCharts.web.tsx index b1a78b1a849..8a9a0309034 100644 --- a/packages/blade/src/components/Charts/BarCharts/BarCharts.web.tsx +++ b/packages/blade/src/components/Charts/BarCharts/BarCharts.web.tsx @@ -72,7 +72,7 @@ const _ChartBar: React.FC = ({ // high change we might not have this in updated designs. shape={(props: unknown) => { const { fill, x, y, width, height, index: barIndex } = props as RechartsShapeProps; - const fillOpacity = isNumber(activeIndex) ? (barIndex === activeIndex ? 1 : 0.8) : 1; + const fillOpacity = isNumber(activeIndex) ? (barIndex === activeIndex ? 1 : 0.2) : 1; const gap = DISTANCE_BETWEEN_STACKED_BARS; const isVertical = layout === 'vertical'; From 08ded0a083d1064a75a12800b118f44c15dd5071 Mon Sep 17 00:00:00 2001 From: tewarig Date: Mon, 22 Sep 2025 17:17:56 +0530 Subject: [PATCH 086/105] fix: set animation easing to linear --- packages/blade/src/components/Charts/BarCharts/BarCharts.web.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/blade/src/components/Charts/BarCharts/BarCharts.web.tsx b/packages/blade/src/components/Charts/BarCharts/BarCharts.web.tsx index 8a9a0309034..5bb725e8629 100644 --- a/packages/blade/src/components/Charts/BarCharts/BarCharts.web.tsx +++ b/packages/blade/src/components/Charts/BarCharts/BarCharts.web.tsx @@ -65,6 +65,7 @@ const _ChartBar: React.FC = ({ label={label} animationBegin={animationBegin} animationDuration={animationDuration} + animationEasing="linear" dataKey={dataKey} name={name} // https://github.com/recharts/recharts/issues/2244#issuecomment-2288572842 From f80e3660813dabf8c862f803c7f4e4ef63a265d9 Mon Sep 17 00:00:00 2001 From: tewarig Date: Tue, 23 Sep 2025 12:01:53 +0530 Subject: [PATCH 087/105] chore: export types --- packages/blade/src/components/Charts/BarCharts/BarCharts.web.tsx | 1 + packages/blade/src/components/Charts/BarCharts/index.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/blade/src/components/Charts/BarCharts/BarCharts.web.tsx b/packages/blade/src/components/Charts/BarCharts/BarCharts.web.tsx index 5bb725e8629..74fda554a46 100644 --- a/packages/blade/src/components/Charts/BarCharts/BarCharts.web.tsx +++ b/packages/blade/src/components/Charts/BarCharts/BarCharts.web.tsx @@ -169,3 +169,4 @@ const ChartBarWrapper: React.FC Date: Tue, 23 Sep 2025 12:12:45 +0530 Subject: [PATCH 088/105] feat: update chart layout --- .../blade-mcp/knowledgebase/components/BarChart.md | 4 ++-- .../components/Charts/BarCharts/BarCharts.stories.tsx | 4 ++-- .../src/components/Charts/BarCharts/BarCharts.web.tsx | 10 +++++----- .../blade/src/components/Charts/BarCharts/types.ts | 4 ++-- .../components/Charts/LineChart/LineChart.stories.tsx | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/blade-mcp/knowledgebase/components/BarChart.md b/packages/blade-mcp/knowledgebase/components/BarChart.md index 1879fd5b1f9..81c32fc708e 100644 --- a/packages/blade-mcp/knowledgebase/components/BarChart.md +++ b/packages/blade-mcp/knowledgebase/components/BarChart.md @@ -64,9 +64,9 @@ type ChartBarWrapperProps = { */ colorTheme?: colorTheme; /** - * The layout of the bar chart. + * The orientation of the bar chart. */ - layout?: 'horizontal' | 'vertical'; + orientation?: 'horizontal' | 'vertical'; /** * Chart data to be rendered */ diff --git a/packages/blade/src/components/Charts/BarCharts/BarCharts.stories.tsx b/packages/blade/src/components/Charts/BarCharts/BarCharts.stories.tsx index 11020b53548..21d8a893f00 100644 --- a/packages/blade/src/components/Charts/BarCharts/BarCharts.stories.tsx +++ b/packages/blade/src/components/Charts/BarCharts/BarCharts.stories.tsx @@ -259,7 +259,7 @@ GroupedBarChart.parameters = { export const VerticalBarChart: StoryFn = () => { return (
      - + @@ -295,7 +295,7 @@ VerticalBarChart.parameters = { export const BarChartWithInformationalColorTheme: StoryFn = () => { return (
      - + diff --git a/packages/blade/src/components/Charts/BarCharts/BarCharts.web.tsx b/packages/blade/src/components/Charts/BarCharts/BarCharts.web.tsx index 74fda554a46..092c04bb75c 100644 --- a/packages/blade/src/components/Charts/BarCharts/BarCharts.web.tsx +++ b/packages/blade/src/components/Charts/BarCharts/BarCharts.web.tsx @@ -46,7 +46,7 @@ const _ChartBar: React.FC = ({ ...rest }) => { const { theme } = useTheme(); - const { layout, activeIndex, colorTheme: _colorTheme, totalBars } = useBarChartContext(); + const { orientation, activeIndex, colorTheme: _colorTheme, totalBars } = useBarChartContext(); const defaultColorArray = useChartsColorTheme({ colorTheme: _colorTheme ?? 'default' }); const fill = color ? getIn(theme.colors, color) : defaultColorArray[_index]; const isStacked = rest.stackId !== undefined; @@ -75,7 +75,7 @@ const _ChartBar: React.FC = ({ const { fill, x, y, width, height, index: barIndex } = props as RechartsShapeProps; const fillOpacity = isNumber(activeIndex) ? (barIndex === activeIndex ? 1 : 0.2) : 1; const gap = DISTANCE_BETWEEN_STACKED_BARS; - const isVertical = layout === 'vertical'; + const isVertical = orientation === 'vertical'; if (isVertical) { // For vertical bars: x and y stay the same, but width/height are swapped @@ -117,7 +117,7 @@ const ChartBar = assignWithoutSideEffects(_ChartBar, { const ChartBarWrapper: React.FC = ({ children, colorTheme = 'default', - layout = 'horizontal', + orientation = 'horizontal', testID, data = [], ...restProps @@ -148,7 +148,7 @@ const ChartBarWrapper: React.FC - + { setActiveIndex(state?.activeIndex ? Number(state?.activeIndex) : undefined); }} - layout={layout} + layout={orientation} data={data} > {barChartModifiedChildrens} diff --git a/packages/blade/src/components/Charts/BarCharts/types.ts b/packages/blade/src/components/Charts/BarCharts/types.ts index ec90dc39e13..15d32d10cf2 100644 --- a/packages/blade/src/components/Charts/BarCharts/types.ts +++ b/packages/blade/src/components/Charts/BarCharts/types.ts @@ -59,7 +59,7 @@ type ChartBarWrapperProps = { /** * The layout of the bar chart. */ - layout?: 'horizontal' | 'vertical'; + orientation?: 'horizontal' | 'vertical'; /** * Chart data to be rendered */ @@ -67,7 +67,7 @@ type ChartBarWrapperProps = { } & Partial>; interface BarChartContextType { - layout?: 'horizontal' | 'vertical'; + orientation?: 'horizontal' | 'vertical'; activeIndex?: number; colorTheme: colorTheme; totalBars: number; diff --git a/packages/blade/src/components/Charts/LineChart/LineChart.stories.tsx b/packages/blade/src/components/Charts/LineChart/LineChart.stories.tsx index 2196427a5c7..b77f72294be 100644 --- a/packages/blade/src/components/Charts/LineChart/LineChart.stories.tsx +++ b/packages/blade/src/components/Charts/LineChart/LineChart.stories.tsx @@ -19,7 +19,7 @@ const Page = (): React.ReactElement => { Usage From 1f88647e5a24841bb27b1f8f7f2d0eb65e090d1b Mon Sep 17 00:00:00 2001 From: tewarig Date: Tue, 23 Sep 2025 12:15:43 +0530 Subject: [PATCH 089/105] chore: rename to charts --- .../BarCharts.native.tsx => BarChart/BarChart.native.tsx} | 0 .../BarCharts.stories.tsx => BarChart/BarChart.stories.tsx} | 0 .../{BarCharts/BarCharts.web.tsx => BarChart/BarChart.web.tsx} | 0 .../Charts/{BarCharts => BarChart}/BarChartContext.ts | 2 +- .../{BarCharts => BarChart}/__tests__/BarChart.web.test.tsx | 2 +- .../__tests__/__snapshots__/BarChart.web.test.tsx.snap | 0 .../src/components/Charts/{BarCharts => BarChart}/index.ts | 2 +- .../src/components/Charts/{BarCharts => BarChart}/tokens.ts | 0 .../src/components/Charts/{BarCharts => BarChart}/types.ts | 0 packages/blade/src/components/Charts/index.ts | 2 +- 10 files changed, 4 insertions(+), 4 deletions(-) rename packages/blade/src/components/Charts/{BarCharts/BarCharts.native.tsx => BarChart/BarChart.native.tsx} (100%) rename packages/blade/src/components/Charts/{BarCharts/BarCharts.stories.tsx => BarChart/BarChart.stories.tsx} (100%) rename packages/blade/src/components/Charts/{BarCharts/BarCharts.web.tsx => BarChart/BarChart.web.tsx} (100%) rename packages/blade/src/components/Charts/{BarCharts => BarChart}/BarChartContext.ts (92%) rename packages/blade/src/components/Charts/{BarCharts => BarChart}/__tests__/BarChart.web.test.tsx (98%) rename packages/blade/src/components/Charts/{BarCharts => BarChart}/__tests__/__snapshots__/BarChart.web.test.tsx.snap (100%) rename packages/blade/src/components/Charts/{BarCharts => BarChart}/index.ts (54%) rename packages/blade/src/components/Charts/{BarCharts => BarChart}/tokens.ts (100%) rename packages/blade/src/components/Charts/{BarCharts => BarChart}/types.ts (100%) diff --git a/packages/blade/src/components/Charts/BarCharts/BarCharts.native.tsx b/packages/blade/src/components/Charts/BarChart/BarChart.native.tsx similarity index 100% rename from packages/blade/src/components/Charts/BarCharts/BarCharts.native.tsx rename to packages/blade/src/components/Charts/BarChart/BarChart.native.tsx diff --git a/packages/blade/src/components/Charts/BarCharts/BarCharts.stories.tsx b/packages/blade/src/components/Charts/BarChart/BarChart.stories.tsx similarity index 100% rename from packages/blade/src/components/Charts/BarCharts/BarCharts.stories.tsx rename to packages/blade/src/components/Charts/BarChart/BarChart.stories.tsx diff --git a/packages/blade/src/components/Charts/BarCharts/BarCharts.web.tsx b/packages/blade/src/components/Charts/BarChart/BarChart.web.tsx similarity index 100% rename from packages/blade/src/components/Charts/BarCharts/BarCharts.web.tsx rename to packages/blade/src/components/Charts/BarChart/BarChart.web.tsx diff --git a/packages/blade/src/components/Charts/BarCharts/BarChartContext.ts b/packages/blade/src/components/Charts/BarChart/BarChartContext.ts similarity index 92% rename from packages/blade/src/components/Charts/BarCharts/BarChartContext.ts rename to packages/blade/src/components/Charts/BarChart/BarChartContext.ts index b13de58bcd4..570d0785ebd 100644 --- a/packages/blade/src/components/Charts/BarCharts/BarChartContext.ts +++ b/packages/blade/src/components/Charts/BarChart/BarChartContext.ts @@ -2,7 +2,7 @@ import { createContext, useContext } from 'react'; import type { BarChartContextType } from './types'; export const BarChartContext = createContext({ - layout: 'horizontal', + orientation: 'horizontal', activeIndex: undefined, colorTheme: 'default', totalBars: 0, diff --git a/packages/blade/src/components/Charts/BarCharts/__tests__/BarChart.web.test.tsx b/packages/blade/src/components/Charts/BarChart/__tests__/BarChart.web.test.tsx similarity index 98% rename from packages/blade/src/components/Charts/BarCharts/__tests__/BarChart.web.test.tsx rename to packages/blade/src/components/Charts/BarChart/__tests__/BarChart.web.test.tsx index 320ccc67bef..6eb1bdff002 100644 --- a/packages/blade/src/components/Charts/BarCharts/__tests__/BarChart.web.test.tsx +++ b/packages/blade/src/components/Charts/BarChart/__tests__/BarChart.web.test.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { ChartBarWrapper, ChartBar } from '../BarCharts'; +import { ChartBarWrapper, ChartBar } from '../BarChart.web'; import { ChartXAxis, ChartYAxis, diff --git a/packages/blade/src/components/Charts/BarCharts/__tests__/__snapshots__/BarChart.web.test.tsx.snap b/packages/blade/src/components/Charts/BarChart/__tests__/__snapshots__/BarChart.web.test.tsx.snap similarity index 100% rename from packages/blade/src/components/Charts/BarCharts/__tests__/__snapshots__/BarChart.web.test.tsx.snap rename to packages/blade/src/components/Charts/BarChart/__tests__/__snapshots__/BarChart.web.test.tsx.snap diff --git a/packages/blade/src/components/Charts/BarCharts/index.ts b/packages/blade/src/components/Charts/BarChart/index.ts similarity index 54% rename from packages/blade/src/components/Charts/BarCharts/index.ts rename to packages/blade/src/components/Charts/BarChart/index.ts index e281bac8e0a..003cde642b3 100644 --- a/packages/blade/src/components/Charts/BarCharts/index.ts +++ b/packages/blade/src/components/Charts/BarChart/index.ts @@ -1,2 +1,2 @@ -export { ChartBarWrapper, ChartBar } from './BarCharts'; +export { ChartBarWrapper, ChartBar } from './BarChart'; export type { ChartBarProps, ChartBarWrapperProps } from './types'; diff --git a/packages/blade/src/components/Charts/BarCharts/tokens.ts b/packages/blade/src/components/Charts/BarChart/tokens.ts similarity index 100% rename from packages/blade/src/components/Charts/BarCharts/tokens.ts rename to packages/blade/src/components/Charts/BarChart/tokens.ts diff --git a/packages/blade/src/components/Charts/BarCharts/types.ts b/packages/blade/src/components/Charts/BarChart/types.ts similarity index 100% rename from packages/blade/src/components/Charts/BarCharts/types.ts rename to packages/blade/src/components/Charts/BarChart/types.ts diff --git a/packages/blade/src/components/Charts/index.ts b/packages/blade/src/components/Charts/index.ts index 4e718607a53..40949e184ef 100644 --- a/packages/blade/src/components/Charts/index.ts +++ b/packages/blade/src/components/Charts/index.ts @@ -1,4 +1,4 @@ export * from './LineChart'; export * from './AreaChart'; -export * from './BarCharts'; +export * from './BarChart'; export * from './CommonChartComponents'; From 4bd36fa776845ca290fe7609226f52fbae9c8ad6 Mon Sep 17 00:00:00 2001 From: tewarig Date: Tue, 23 Sep 2025 12:30:01 +0530 Subject: [PATCH 090/105] chore: rename story --- .../src/components/Charts/BarChart/BarChart.stories.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/blade/src/components/Charts/BarChart/BarChart.stories.tsx b/packages/blade/src/components/Charts/BarChart/BarChart.stories.tsx index 21d8a893f00..f0dc4a8bb87 100644 --- a/packages/blade/src/components/Charts/BarChart/BarChart.stories.tsx +++ b/packages/blade/src/components/Charts/BarChart/BarChart.stories.tsx @@ -292,7 +292,7 @@ VerticalBarChart.parameters = { controls: { disable: true }, }; -export const BarChartWithInformationalColorTheme: StoryFn = () => { +export const BarChartWithDefaultColorTheme: StoryFn = () => { return (
      @@ -309,7 +309,7 @@ export const BarChartWithInformationalColorTheme: StoryFn = () ); }; -BarChartWithInformationalColorTheme.parameters = { +BarChartWithDefaultColorTheme.parameters = { controls: { disable: true }, }; @@ -318,4 +318,4 @@ TinyBarChart.storyName = 'Tiny Bar Chart'; SimpleBarChart.storyName = 'Simple Bar Chart'; StackedBarChart.storyName = 'Stacked Bar Chart'; VerticalBarChart.storyName = 'Vertical Bar Chart'; -BarChartWithInformationalColorTheme.storyName = 'Bar Chart With Informational Color Theme'; +BarChartWithDefaultColorTheme.storyName = 'Bar Chart With Informational Color Theme'; From a3bc3e9f6fe06932aa4a6ba7ab1612b025813aff Mon Sep 17 00:00:00 2001 From: tewarig Date: Tue, 23 Sep 2025 13:04:42 +0530 Subject: [PATCH 091/105] chore: update barchart stories --- .../blade/src/components/Charts/BarChart/BarChart.stories.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/blade/src/components/Charts/BarChart/BarChart.stories.tsx b/packages/blade/src/components/Charts/BarChart/BarChart.stories.tsx index f0dc4a8bb87..366c9a88406 100644 --- a/packages/blade/src/components/Charts/BarChart/BarChart.stories.tsx +++ b/packages/blade/src/components/Charts/BarChart/BarChart.stories.tsx @@ -318,4 +318,4 @@ TinyBarChart.storyName = 'Tiny Bar Chart'; SimpleBarChart.storyName = 'Simple Bar Chart'; StackedBarChart.storyName = 'Stacked Bar Chart'; VerticalBarChart.storyName = 'Vertical Bar Chart'; -BarChartWithDefaultColorTheme.storyName = 'Bar Chart With Informational Color Theme'; +BarChartWithDefaultColorTheme.storyName = 'Bar Chart With Default Color Theme'; From 2deae8c24aeacb7c1913d0f33a9e8aafe25e1565 Mon Sep 17 00:00:00 2001 From: tewarig Date: Tue, 23 Sep 2025 20:29:06 +0530 Subject: [PATCH 092/105] fix: add animation --- .../Charts/BarChart/BarChart.web.tsx | 86 +++++++++++++++++-- 1 file changed, 79 insertions(+), 7 deletions(-) diff --git a/packages/blade/src/components/Charts/BarChart/BarChart.web.tsx b/packages/blade/src/components/Charts/BarChart/BarChart.web.tsx index 092c04bb75c..2c77bbb8317 100644 --- a/packages/blade/src/components/Charts/BarChart/BarChart.web.tsx +++ b/packages/blade/src/components/Charts/BarChart/BarChart.web.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useEffect, useState } from 'react'; import { BarChart as RechartsBarChart, Bar as RechartsBar, @@ -24,6 +24,70 @@ import { assignWithoutSideEffects } from '~utils/assignWithoutSideEffects'; import { getComponentId } from '~utils/isValidAllowedChildren'; import { makeAnalyticsAttribute } from '~utils/makeAnalyticsAttribute'; import type { DataAnalyticsAttribute, TestID } from '~utils/types'; +import { LazyMotion, domAnimation, m, useAnimation } from 'framer-motion'; + +const MotionRectangle = (props: any) => { + const controls = useAnimation(); + const [animationState, setAnimationState] = useState('idle'); + + useEffect(() => { + if (props.animationTrigger === 'fadeIn') { + setAnimationState('fadingIn'); + controls.start({ + fillOpacity: 1, + transition: { + duration: 0.3, + ease: 'easeOut', + delay: 0, + onComplete: () => { + setAnimationState('fadedIn'); + if (props.onFadeInComplete) props.onFadeInComplete(); + }, + }, + }); + } else if (props.animationTrigger === 'fadeOut') { + setAnimationState('fadingOut'); + controls.start({ + fillOpacity: 0.2, + transition: { + duration: 0.3, + ease: 'easeIn', + onComplete: () => { + setAnimationState('fadedOut'); + if (props.onFadeOutComplete) props.onFadeOutComplete(); + }, + }, + }); + } else if (props.animationTrigger === 'reset') { + setAnimationState('resetting'); + controls.start({ + fillOpacity: props.fillOpacity, + transition: { + duration: 0.2, + ease: 'easeInOut', + onComplete: () => { + setAnimationState('idle'); + if (props.onResetComplete) props.onResetComplete(); + }, + }, + }); + } + }, [props.animationTrigger, props.fillOpacity, controls]); + + return ( + + + + ); +}; export type RechartsShapeProps = { x: number; @@ -56,6 +120,7 @@ const _ChartBar: React.FC = ({ const animationDuration = isStacked ? theme.motion.duration.gentle / totalBars : theme.motion.duration.gentle; + return ( = ({ animationEasing="linear" dataKey={dataKey} name={name} - // https://github.com/recharts/recharts/issues/2244#issuecomment-2288572842 - // we have this shape to have distance b/w bar charts - // high change we might not have this in updated designs. shape={(props: unknown) => { const { fill, x, y, width, height, index: barIndex } = props as RechartsShapeProps; const fillOpacity = isNumber(activeIndex) ? (barIndex === activeIndex ? 1 : 0.2) : 1; const gap = DISTANCE_BETWEEN_STACKED_BARS; const isVertical = orientation === 'vertical'; + // Determine animation trigger + const getAnimationTrigger = () => { + if (!isNumber(activeIndex)) return 'reset'; + if (barIndex === activeIndex) return 'fadeIn'; + return 'fadeOut'; + }; + if (isVertical) { - // For vertical bars: x and y stay the same, but width/height are swapped return ( - = ({ rx={BAR_CHART_CORNER_RADIUS} ry={BAR_CHART_CORNER_RADIUS} fillOpacity={fillOpacity} + animationTrigger={getAnimationTrigger()} + initialOpacity={0.2} /> ); } return ( - = ({ rx={BAR_CHART_CORNER_RADIUS} ry={BAR_CHART_CORNER_RADIUS} fillOpacity={fillOpacity} + animationTrigger={getAnimationTrigger()} + initialOpacity={0.2} /> ); }} From fd2825ba637cbefdda00fc615b8a1837643a6cb6 Mon Sep 17 00:00:00 2001 From: tewarig Date: Tue, 23 Sep 2025 23:12:06 +0530 Subject: [PATCH 093/105] fix: animation speed --- .../Charts/BarChart/BarChart.web.tsx | 33 +++++++++++++------ 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/packages/blade/src/components/Charts/BarChart/BarChart.web.tsx b/packages/blade/src/components/Charts/BarChart/BarChart.web.tsx index 2c77bbb8317..3170ed1eae4 100644 --- a/packages/blade/src/components/Charts/BarChart/BarChart.web.tsx +++ b/packages/blade/src/components/Charts/BarChart/BarChart.web.tsx @@ -1,9 +1,10 @@ -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useState, useRef } from 'react'; import { BarChart as RechartsBarChart, Bar as RechartsBar, ResponsiveContainer as RechartsResponsiveContainer, } from 'recharts'; +import { LazyMotion, domAnimation, m, useAnimation } from 'framer-motion'; import { useChartsColorTheme } from '../utils'; import { BarChartContext, useBarChartContext } from './BarChartContext'; import type { ChartBarProps, ChartBarWrapperProps } from './types'; @@ -24,7 +25,6 @@ import { assignWithoutSideEffects } from '~utils/assignWithoutSideEffects'; import { getComponentId } from '~utils/isValidAllowedChildren'; import { makeAnalyticsAttribute } from '~utils/makeAnalyticsAttribute'; import type { DataAnalyticsAttribute, TestID } from '~utils/types'; -import { LazyMotion, domAnimation, m, useAnimation } from 'framer-motion'; const MotionRectangle = (props: any) => { const controls = useAnimation(); @@ -33,7 +33,7 @@ const MotionRectangle = (props: any) => { useEffect(() => { if (props.animationTrigger === 'fadeIn') { setAnimationState('fadingIn'); - controls.start({ + void controls.start({ fillOpacity: 1, transition: { duration: 0.3, @@ -47,7 +47,7 @@ const MotionRectangle = (props: any) => { }); } else if (props.animationTrigger === 'fadeOut') { setAnimationState('fadingOut'); - controls.start({ + void controls.start({ fillOpacity: 0.2, transition: { duration: 0.3, @@ -60,7 +60,7 @@ const MotionRectangle = (props: any) => { }); } else if (props.animationTrigger === 'reset') { setAnimationState('resetting'); - controls.start({ + void controls.start({ fillOpacity: props.fillOpacity, transition: { duration: 0.2, @@ -72,19 +72,21 @@ const MotionRectangle = (props: any) => { }, }); } - }, [props.animationTrigger, props.fillOpacity, controls]); + }, [props.animationTrigger, props.fillOpacity, controls, props]); return ( + /> ); }; @@ -111,6 +113,7 @@ const _ChartBar: React.FC = ({ }) => { const { theme } = useTheme(); const { orientation, activeIndex, colorTheme: _colorTheme, totalBars } = useBarChartContext(); + const hasChartMounted = useRef(false); const defaultColorArray = useChartsColorTheme({ colorTheme: _colorTheme ?? 'default' }); const fill = color ? getIn(theme.colors, color) : defaultColorArray[_index]; const isStacked = rest.stackId !== undefined; @@ -121,6 +124,16 @@ const _ChartBar: React.FC = ({ ? theme.motion.duration.gentle / totalBars : theme.motion.duration.gentle; + useEffect(() => { + const animationEndTime = animationDuration + animationBegin + 300; + if (!hasChartMounted.current) { + //Let the initial animation happen + setTimeout(() => { + hasChartMounted.current = true; + }, animationEndTime); + } + }, [animationDuration, animationBegin]); + return ( = ({ ry={BAR_CHART_CORNER_RADIUS} fillOpacity={fillOpacity} animationTrigger={getAnimationTrigger()} - initialOpacity={0.2} + initialOpacity={hasChartMounted.current ? 0.2 : 1} /> ); } @@ -173,7 +186,7 @@ const _ChartBar: React.FC = ({ ry={BAR_CHART_CORNER_RADIUS} fillOpacity={fillOpacity} animationTrigger={getAnimationTrigger()} - initialOpacity={0.2} + initialOpacity={hasChartMounted.current ? 0.2 : 1} /> ); }} From 51d968858f3b7feff00f84c39f413ee7623f5129 Mon Sep 17 00:00:00 2001 From: tewarig Date: Wed, 24 Sep 2025 00:22:10 +0530 Subject: [PATCH 094/105] fix: animation logic and stacked bar logic --- .../Charts/BarChart/BarChart.web.tsx | 47 ++++++++++--------- .../src/components/Charts/BarChart/tokens.ts | 2 + 2 files changed, 27 insertions(+), 22 deletions(-) diff --git a/packages/blade/src/components/Charts/BarChart/BarChart.web.tsx b/packages/blade/src/components/Charts/BarChart/BarChart.web.tsx index 3170ed1eae4..b71357e3cd2 100644 --- a/packages/blade/src/components/Charts/BarChart/BarChart.web.tsx +++ b/packages/blade/src/components/Charts/BarChart/BarChart.web.tsx @@ -15,6 +15,7 @@ import { BAR_SIZE, DISTANCE_BETWEEN_BARS, DISTANCE_BETWEEN_CATEGORY_BARS, + ANIMATION_TIME_OFFEST, } from './tokens'; import { useTheme } from '~components/BladeProvider'; import BaseBox from '~components/Box/BaseBox'; @@ -34,10 +35,10 @@ const MotionRectangle = (props: any) => { if (props.animationTrigger === 'fadeIn') { setAnimationState('fadingIn'); void controls.start({ - fillOpacity: 1, + fillOpacity: props.fillOpacity, transition: { - duration: 0.3, - ease: 'easeOut', + duration: 0.01, + ease: 'easeIn', delay: 0, onComplete: () => { setAnimationState('fadedIn'); @@ -113,26 +114,18 @@ const _ChartBar: React.FC = ({ }) => { const { theme } = useTheme(); const { orientation, activeIndex, colorTheme: _colorTheme, totalBars } = useBarChartContext(); - const hasChartMounted = useRef(false); + const hasChartAnimationEnded = useRef(false); const defaultColorArray = useChartsColorTheme({ colorTheme: _colorTheme ?? 'default' }); const fill = color ? getIn(theme.colors, color) : defaultColorArray[_index]; const isStacked = rest.stackId !== undefined; - const animationBegin = isStacked - ? (theme.motion.duration.gentle / totalBars) * _index - : theme.motion.duration.gentle; - const animationDuration = isStacked - ? theme.motion.duration.gentle / totalBars - : theme.motion.duration.gentle; - - useEffect(() => { - const animationEndTime = animationDuration + animationBegin + 300; - if (!hasChartMounted.current) { - //Let the initial animation happen - setTimeout(() => { - hasChartMounted.current = true; - }, animationEndTime); - } - }, [animationDuration, animationBegin]); + const animationBegin = + isStacked && !hasChartAnimationEnded.current + ? (theme.motion.duration.gentle / totalBars) * _index + : theme.motion.duration.gentle; + const animationDuration = + isStacked && !hasChartAnimationEnded.current + ? theme.motion.duration.gentle / totalBars + : theme.motion.duration.gentle; return ( = ({ animationBegin={animationBegin} animationDuration={animationDuration} animationEasing="linear" + onAnimationStart={() => { + hasChartAnimationEnded.current = false; + }} + onAnimationEnd={() => { + // Sometimes this function is being called before the animation is complete, so we need to wait for the animation to complete + setTimeout(() => { + hasChartAnimationEnded.current = true; + }, ANIMATION_TIME_OFFEST); + }} dataKey={dataKey} name={name} shape={(props: unknown) => { @@ -153,6 +155,7 @@ const _ChartBar: React.FC = ({ const isVertical = orientation === 'vertical'; // Determine animation trigger + // eslint-disable-next-line @typescript-eslint/explicit-function-return-type const getAnimationTrigger = () => { if (!isNumber(activeIndex)) return 'reset'; if (barIndex === activeIndex) return 'fadeIn'; @@ -171,7 +174,7 @@ const _ChartBar: React.FC = ({ ry={BAR_CHART_CORNER_RADIUS} fillOpacity={fillOpacity} animationTrigger={getAnimationTrigger()} - initialOpacity={hasChartMounted.current ? 0.2 : 1} + initialOpacity={hasChartAnimationEnded.current ? 1 : 0.2} /> ); } @@ -186,7 +189,7 @@ const _ChartBar: React.FC = ({ ry={BAR_CHART_CORNER_RADIUS} fillOpacity={fillOpacity} animationTrigger={getAnimationTrigger()} - initialOpacity={hasChartMounted.current ? 0.2 : 1} + initialOpacity={hasChartAnimationEnded.current ? 1 : 0.2} /> ); }} diff --git a/packages/blade/src/components/Charts/BarChart/tokens.ts b/packages/blade/src/components/Charts/BarChart/tokens.ts index b5dbd62dcf0..90ce8eef94d 100644 --- a/packages/blade/src/components/Charts/BarChart/tokens.ts +++ b/packages/blade/src/components/Charts/BarChart/tokens.ts @@ -4,6 +4,7 @@ const DISTANCE_BETWEEN_STACKED_BARS = 2; const BAR_SIZE = 49; const DISTANCE_BETWEEN_BARS = 2; const DISTANCE_BETWEEN_CATEGORY_BARS = 2; +const ANIMATION_TIME_OFFEST = 200; const componentIds = { chartBar: 'ChartBar', @@ -16,4 +17,5 @@ export { BAR_SIZE, DISTANCE_BETWEEN_BARS, DISTANCE_BETWEEN_CATEGORY_BARS, + ANIMATION_TIME_OFFEST, }; From c8892f73e281d03c1b8c461347b45263ba44b0e0 Mon Sep 17 00:00:00 2001 From: tewarig Date: Wed, 24 Sep 2025 01:15:22 +0530 Subject: [PATCH 095/105] chore: more animation fixes --- .../blade/src/components/Charts/BarChart/BarChart.stories.tsx | 4 ++-- .../blade/src/components/Charts/BarChart/BarChart.web.tsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/blade/src/components/Charts/BarChart/BarChart.stories.tsx b/packages/blade/src/components/Charts/BarChart/BarChart.stories.tsx index 366c9a88406..7b93543ca14 100644 --- a/packages/blade/src/components/Charts/BarChart/BarChart.stories.tsx +++ b/packages/blade/src/components/Charts/BarChart/BarChart.stories.tsx @@ -268,7 +268,7 @@ export const VerticalBarChart: StoryFn = () => { = () => { diff --git a/packages/blade/src/components/Charts/BarChart/BarChart.web.tsx b/packages/blade/src/components/Charts/BarChart/BarChart.web.tsx index b71357e3cd2..eda1a00837f 100644 --- a/packages/blade/src/components/Charts/BarChart/BarChart.web.tsx +++ b/packages/blade/src/components/Charts/BarChart/BarChart.web.tsx @@ -62,10 +62,10 @@ const MotionRectangle = (props: any) => { } else if (props.animationTrigger === 'reset') { setAnimationState('resetting'); void controls.start({ - fillOpacity: props.fillOpacity, + fillOpacity: 1, transition: { duration: 0.2, - ease: 'easeInOut', + ease: 'easeOut', onComplete: () => { setAnimationState('idle'); if (props.onResetComplete) props.onResetComplete(); From 6346b23a648a4f0d1039010561ddf10271e71d1e Mon Sep 17 00:00:00 2001 From: tewarig Date: Wed, 24 Sep 2025 05:01:30 +0530 Subject: [PATCH 096/105] fix: update animation --- .../Charts/BarChart/BarChart.web.tsx | 97 +++++++++++++------ .../Charts/BarChart/BarChartContext.ts | 2 + .../src/components/Charts/BarChart/tokens.ts | 7 ++ .../src/components/Charts/BarChart/types.ts | 15 ++- 4 files changed, 92 insertions(+), 29 deletions(-) diff --git a/packages/blade/src/components/Charts/BarChart/BarChart.web.tsx b/packages/blade/src/components/Charts/BarChart/BarChart.web.tsx index eda1a00837f..3368c8a3c4d 100644 --- a/packages/blade/src/components/Charts/BarChart/BarChart.web.tsx +++ b/packages/blade/src/components/Charts/BarChart/BarChart.web.tsx @@ -7,7 +7,7 @@ import { import { LazyMotion, domAnimation, m, useAnimation } from 'framer-motion'; import { useChartsColorTheme } from '../utils'; import { BarChartContext, useBarChartContext } from './BarChartContext'; -import type { ChartBarProps, ChartBarWrapperProps } from './types'; +import type { ChartBarProps, ChartBarWrapperProps, MotionRectProps } from './types'; import { BAR_CHART_CORNER_RADIUS, DISTANCE_BETWEEN_STACKED_BARS, @@ -15,7 +15,7 @@ import { BAR_SIZE, DISTANCE_BETWEEN_BARS, DISTANCE_BETWEEN_CATEGORY_BARS, - ANIMATION_TIME_OFFEST, + MOTION_TRIGGERS, } from './tokens'; import { useTheme } from '~components/BladeProvider'; import BaseBox from '~components/Box/BaseBox'; @@ -27,12 +27,12 @@ import { getComponentId } from '~utils/isValidAllowedChildren'; import { makeAnalyticsAttribute } from '~utils/makeAnalyticsAttribute'; import type { DataAnalyticsAttribute, TestID } from '~utils/types'; -const MotionRectangle = (props: any) => { +const MotionRectangle = (props: MotionRectProps): React.ReactNode => { const controls = useAnimation(); const [animationState, setAnimationState] = useState('idle'); useEffect(() => { - if (props.animationTrigger === 'fadeIn') { + if (props.animationTrigger === MOTION_TRIGGERS.FADE_IN) { setAnimationState('fadingIn'); void controls.start({ fillOpacity: props.fillOpacity, @@ -46,7 +46,7 @@ const MotionRectangle = (props: any) => { }, }, }); - } else if (props.animationTrigger === 'fadeOut') { + } else if (props.animationTrigger === MOTION_TRIGGERS.FADE_OUT) { setAnimationState('fadingOut'); void controls.start({ fillOpacity: 0.2, @@ -59,7 +59,7 @@ const MotionRectangle = (props: any) => { }, }, }); - } else if (props.animationTrigger === 'reset') { + } else if (props.animationTrigger === MOTION_TRIGGERS.RESET) { setAnimationState('resetting'); void controls.start({ fillOpacity: 1, @@ -113,17 +113,25 @@ const _ChartBar: React.FC = ({ ...rest }) => { const { theme } = useTheme(); - const { orientation, activeIndex, colorTheme: _colorTheme, totalBars } = useBarChartContext(); - const hasChartAnimationEnded = useRef(false); + const { + orientation, + activeIndex, + colorTheme: _colorTheme, + totalBars, + isFirstActiveIndex, + shouldPlayResetAnimation, + } = useBarChartContext(); const defaultColorArray = useChartsColorTheme({ colorTheme: _colorTheme ?? 'default' }); + const [isHovered, setIsHovered] = useState(false); + const isContainerHovered = isNumber(activeIndex); const fill = color ? getIn(theme.colors, color) : defaultColorArray[_index]; const isStacked = rest.stackId !== undefined; const animationBegin = - isStacked && !hasChartAnimationEnded.current + isStacked && !isContainerHovered ? (theme.motion.duration.gentle / totalBars) * _index : theme.motion.duration.gentle; const animationDuration = - isStacked && !hasChartAnimationEnded.current + isStacked && !isContainerHovered ? theme.motion.duration.gentle / totalBars : theme.motion.duration.gentle; @@ -136,16 +144,9 @@ const _ChartBar: React.FC = ({ label={label} animationBegin={animationBegin} animationDuration={animationDuration} + onMouseEnter={() => setIsHovered(true)} + onMouseLeave={() => setIsHovered(false)} animationEasing="linear" - onAnimationStart={() => { - hasChartAnimationEnded.current = false; - }} - onAnimationEnd={() => { - // Sometimes this function is being called before the animation is complete, so we need to wait for the animation to complete - setTimeout(() => { - hasChartAnimationEnded.current = true; - }, ANIMATION_TIME_OFFEST); - }} dataKey={dataKey} name={name} shape={(props: unknown) => { @@ -154,12 +155,26 @@ const _ChartBar: React.FC = ({ const gap = DISTANCE_BETWEEN_STACKED_BARS; const isVertical = orientation === 'vertical'; - // Determine animation trigger - // eslint-disable-next-line @typescript-eslint/explicit-function-return-type - const getAnimationTrigger = () => { - if (!isNumber(activeIndex)) return 'reset'; - if (barIndex === activeIndex) return 'fadeIn'; - return 'fadeOut'; + const getAnimationTrigger = (): string => { + if (!isNumber(activeIndex)) return MOTION_TRIGGERS.RESET; + if (barIndex === activeIndex) return MOTION_TRIGGERS.FADE_IN; + return MOTION_TRIGGERS.FADE_OUT; + }; + + const getInitialOpacity = (): number => { + const animationTrigger = getAnimationTrigger(); + if (isContainerHovered) { + if (animationTrigger === MOTION_TRIGGERS.FADE_IN && isFirstActiveIndex) return 1; + if (animationTrigger === MOTION_TRIGGERS.FADE_IN && !isFirstActiveIndex) return 0.2; + if (animationTrigger === MOTION_TRIGGERS.FADE_OUT && isFirstActiveIndex) return 1; + if (animationTrigger === MOTION_TRIGGERS.FADE_OUT && !isFirstActiveIndex) return 0.2; + if (animationTrigger === MOTION_TRIGGERS.RESET && isHovered) return 0.2; + } + if (shouldPlayResetAnimation) { + if (animationTrigger === MOTION_TRIGGERS.RESET) return 0.2; + } + + return 1; }; if (isVertical) { @@ -174,7 +189,7 @@ const _ChartBar: React.FC = ({ ry={BAR_CHART_CORNER_RADIUS} fillOpacity={fillOpacity} animationTrigger={getAnimationTrigger()} - initialOpacity={hasChartAnimationEnded.current ? 1 : 0.2} + initialOpacity={getInitialOpacity()} /> ); } @@ -189,7 +204,7 @@ const _ChartBar: React.FC = ({ ry={BAR_CHART_CORNER_RADIUS} fillOpacity={fillOpacity} animationTrigger={getAnimationTrigger()} - initialOpacity={hasChartAnimationEnded.current ? 1 : 0.2} + initialOpacity={getInitialOpacity()} /> ); }} @@ -211,6 +226,16 @@ const ChartBarWrapper: React.FC { const [activeIndex, setActiveIndex] = useState(undefined); + const [shouldPlayResetAnimation, setShouldPlayResetAnimation] = useState(false); + const prevActiveIndex = useRef(undefined); + + // Check if this is the first active index + const isFirstActiveIndex = isNumber(activeIndex) && prevActiveIndex.current === undefined; + + // Update previous activeIndex + useEffect(() => { + prevActiveIndex.current = activeIndex; + }, [activeIndex]); const { barChartModifiedChildrens, totalBars } = React.useMemo(() => { let BarChartIndex = 0; @@ -228,6 +253,7 @@ const ChartBarWrapper: React.FC - + { + if (isNumber(prevActiveIndex.current) && !state?.activeIndex) { + setShouldPlayResetAnimation(true); + setTimeout(() => { + setShouldPlayResetAnimation(false); + }, 1000); + } setActiveIndex(state?.activeIndex ? Number(state?.activeIndex) : undefined); }} layout={orientation} diff --git a/packages/blade/src/components/Charts/BarChart/BarChartContext.ts b/packages/blade/src/components/Charts/BarChart/BarChartContext.ts index 570d0785ebd..af9663d572f 100644 --- a/packages/blade/src/components/Charts/BarChart/BarChartContext.ts +++ b/packages/blade/src/components/Charts/BarChart/BarChartContext.ts @@ -6,6 +6,8 @@ export const BarChartContext = createContext({ activeIndex: undefined, colorTheme: 'default', totalBars: 0, + isFirstActiveIndex: false, + shouldPlayResetAnimation: false, }); export const useBarChartContext = (): BarChartContextType => useContext(BarChartContext); diff --git a/packages/blade/src/components/Charts/BarChart/tokens.ts b/packages/blade/src/components/Charts/BarChart/tokens.ts index 90ce8eef94d..1fe49a81e1a 100644 --- a/packages/blade/src/components/Charts/BarChart/tokens.ts +++ b/packages/blade/src/components/Charts/BarChart/tokens.ts @@ -10,6 +10,12 @@ const componentIds = { chartBar: 'ChartBar', }; +const MOTION_TRIGGERS = { + RESET: 'reset', + FADE_IN: 'fadeIn', + FADE_OUT: 'fadeOut', +}; + export { componentIds, DISTANCE_BETWEEN_STACKED_BARS, @@ -18,4 +24,5 @@ export { DISTANCE_BETWEEN_BARS, DISTANCE_BETWEEN_CATEGORY_BARS, ANIMATION_TIME_OFFEST, + MOTION_TRIGGERS, }; diff --git a/packages/blade/src/components/Charts/BarChart/types.ts b/packages/blade/src/components/Charts/BarChart/types.ts index 15d32d10cf2..288d8b7f35a 100644 --- a/packages/blade/src/components/Charts/BarChart/types.ts +++ b/packages/blade/src/components/Charts/BarChart/types.ts @@ -1,4 +1,6 @@ +import type { ComponentProps } from 'react'; import type { BarProps as RechartsBarProps } from 'recharts'; +import type { m } from 'framer-motion'; import type { ChartsCategoricalColorToken, ChartSequentialColorToken, @@ -71,6 +73,17 @@ interface BarChartContextType { activeIndex?: number; colorTheme: colorTheme; totalBars: number; + isFirstActiveIndex?: boolean; + shouldPlayResetAnimation?: boolean; } -export type { ChartBarProps, ChartBarWrapperProps, BarChartContextType }; +type MotionRectProps = ComponentProps & { + animationTrigger: string; + initialOpacity?: number; + onFadeInComplete?: () => void; + onFadeOutComplete?: () => void; + onResetComplete?: () => void; + fillOpacity: number; +}; + +export type { ChartBarProps, ChartBarWrapperProps, BarChartContextType, MotionRectProps }; From 02837403b7c7ac56ab537af0c096deeda70b7ffa Mon Sep 17 00:00:00 2001 From: tewarig Date: Wed, 24 Sep 2025 05:02:43 +0530 Subject: [PATCH 097/105] chore: update snaps --- .../__snapshots__/BarChart.web.test.tsx.snap | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/packages/blade/src/components/Charts/BarChart/__tests__/__snapshots__/BarChart.web.test.tsx.snap b/packages/blade/src/components/Charts/BarChart/__tests__/__snapshots__/BarChart.web.test.tsx.snap index 4a2731d8ac5..266e089adb2 100644 --- a/packages/blade/src/components/Charts/BarChart/__tests__/__snapshots__/BarChart.web.test.tsx.snap +++ b/packages/blade/src/components/Charts/BarChart/__tests__/__snapshots__/BarChart.web.test.tsx.snap @@ -73,6 +73,7 @@ exports[` should render BarChart with custom colors 1`] = ` height="0" rx="2" ry="2" + style="cursor: pointer; opacity: 1;" width="49" x="53" y="796" @@ -87,6 +88,7 @@ exports[` should render BarChart with custom colors 1`] = ` height="0" rx="2" ry="2" + style="cursor: pointer; opacity: 1;" width="49" x="250.5" y="796" @@ -101,6 +103,7 @@ exports[` should render BarChart with custom colors 1`] = ` height="0" rx="2" ry="2" + style="cursor: pointer; opacity: 1;" width="49" x="448" y="796" @@ -115,6 +118,7 @@ exports[` should render BarChart with custom colors 1`] = ` height="0" rx="2" ry="2" + style="cursor: pointer; opacity: 1;" width="49" x="645.5" y="796" @@ -142,6 +146,7 @@ exports[` should render BarChart with custom colors 1`] = ` height="0" rx="2" ry="2" + style="cursor: pointer; opacity: 1;" width="49" x="104" y="796" @@ -156,6 +161,7 @@ exports[` should render BarChart with custom colors 1`] = ` height="0" rx="2" ry="2" + style="cursor: pointer; opacity: 1;" width="49" x="301.5" y="796" @@ -170,6 +176,7 @@ exports[` should render BarChart with custom colors 1`] = ` height="0" rx="2" ry="2" + style="cursor: pointer; opacity: 1;" width="49" x="499" y="796" @@ -184,6 +191,7 @@ exports[` should render BarChart with custom colors 1`] = ` height="0" rx="2" ry="2" + style="cursor: pointer; opacity: 1;" width="49" x="696.5" y="796" @@ -274,6 +282,7 @@ exports[` should render BarChart with multiple bars 1`] = ` height="0" rx="2" ry="2" + style="cursor: pointer; opacity: 1;" width="49" x="28" y="796" @@ -288,6 +297,7 @@ exports[` should render BarChart with multiple bars 1`] = ` height="0" rx="2" ry="2" + style="cursor: pointer; opacity: 1;" width="49" x="225.5" y="796" @@ -302,6 +312,7 @@ exports[` should render BarChart with multiple bars 1`] = ` height="0" rx="2" ry="2" + style="cursor: pointer; opacity: 1;" width="49" x="423" y="796" @@ -316,6 +327,7 @@ exports[` should render BarChart with multiple bars 1`] = ` height="0" rx="2" ry="2" + style="cursor: pointer; opacity: 1;" width="49" x="620.5" y="796" @@ -343,6 +355,7 @@ exports[` should render BarChart with multiple bars 1`] = ` height="0" rx="2" ry="2" + style="cursor: pointer; opacity: 1;" width="49" x="79" y="796" @@ -357,6 +370,7 @@ exports[` should render BarChart with multiple bars 1`] = ` height="0" rx="2" ry="2" + style="cursor: pointer; opacity: 1;" width="49" x="276.5" y="796" @@ -371,6 +385,7 @@ exports[` should render BarChart with multiple bars 1`] = ` height="0" rx="2" ry="2" + style="cursor: pointer; opacity: 1;" width="49" x="474" y="796" @@ -385,6 +400,7 @@ exports[` should render BarChart with multiple bars 1`] = ` height="0" rx="2" ry="2" + style="cursor: pointer; opacity: 1;" width="49" x="671.5" y="796" @@ -412,6 +428,7 @@ exports[` should render BarChart with multiple bars 1`] = ` height="0" rx="2" ry="2" + style="cursor: pointer; opacity: 1;" width="49" x="130" y="796" @@ -426,6 +443,7 @@ exports[` should render BarChart with multiple bars 1`] = ` height="0" rx="2" ry="2" + style="cursor: pointer; opacity: 1;" width="49" x="327.5" y="796" @@ -440,6 +458,7 @@ exports[` should render BarChart with multiple bars 1`] = ` height="0" rx="2" ry="2" + style="cursor: pointer; opacity: 1;" width="49" x="525" y="796" @@ -454,6 +473,7 @@ exports[` should render BarChart with multiple bars 1`] = ` height="0" rx="2" ry="2" + style="cursor: pointer; opacity: 1;" width="49" x="722.5" y="796" @@ -544,6 +564,7 @@ exports[` should render basic BarChart with single bar 1`] = ` height="0" rx="2" ry="2" + style="cursor: pointer; opacity: 1;" width="49" x="79" y="796" @@ -558,6 +579,7 @@ exports[` should render basic BarChart with single bar 1`] = ` height="0" rx="2" ry="2" + style="cursor: pointer; opacity: 1;" width="49" x="276.5" y="796" @@ -572,6 +594,7 @@ exports[` should render basic BarChart with single bar 1`] = ` height="0" rx="2" ry="2" + style="cursor: pointer; opacity: 1;" width="49" x="474" y="796" @@ -586,6 +609,7 @@ exports[` should render basic BarChart with single bar 1`] = ` height="0" rx="2" ry="2" + style="cursor: pointer; opacity: 1;" width="49" x="671.5" y="796" @@ -1302,6 +1326,7 @@ exports[` should render complete chart with all components 1`] = ` height="0" rx="2" ry="2" + style="cursor: pointer; opacity: 1;" width="49" x="106" y="766" @@ -1316,6 +1341,7 @@ exports[` should render complete chart with all components 1`] = ` height="0" rx="2" ry="2" + style="cursor: pointer; opacity: 1;" width="49" x="288.5" y="766" @@ -1330,6 +1356,7 @@ exports[` should render complete chart with all components 1`] = ` height="0" rx="2" ry="2" + style="cursor: pointer; opacity: 1;" width="49" x="471" y="766" @@ -1344,6 +1371,7 @@ exports[` should render complete chart with all components 1`] = ` height="0" rx="2" ry="2" + style="cursor: pointer; opacity: 1;" width="49" x="653.5" y="766" @@ -1371,6 +1399,7 @@ exports[` should render complete chart with all components 1`] = ` height="0" rx="2" ry="2" + style="cursor: pointer; opacity: 1;" width="49" x="157" y="766" @@ -1385,6 +1414,7 @@ exports[` should render complete chart with all components 1`] = ` height="0" rx="2" ry="2" + style="cursor: pointer; opacity: 1;" width="49" x="339.5" y="766" @@ -1399,6 +1429,7 @@ exports[` should render complete chart with all components 1`] = ` height="0" rx="2" ry="2" + style="cursor: pointer; opacity: 1;" width="49" x="522" y="766" @@ -1413,6 +1444,7 @@ exports[` should render complete chart with all components 1`] = ` height="0" rx="2" ry="2" + style="cursor: pointer; opacity: 1;" width="49" x="704.5" y="766" @@ -1503,6 +1535,7 @@ exports[` should render stacked BarChart 1`] = ` height="0" rx="2" ry="2" + style="cursor: pointer; opacity: 1;" width="49" x="79" y="796" @@ -1517,6 +1550,7 @@ exports[` should render stacked BarChart 1`] = ` height="0" rx="2" ry="2" + style="cursor: pointer; opacity: 1;" width="49" x="276.5" y="796" @@ -1531,6 +1565,7 @@ exports[` should render stacked BarChart 1`] = ` height="0" rx="2" ry="2" + style="cursor: pointer; opacity: 1;" width="49" x="474" y="796" @@ -1545,6 +1580,7 @@ exports[` should render stacked BarChart 1`] = ` height="0" rx="2" ry="2" + style="cursor: pointer; opacity: 1;" width="49" x="671.5" y="796" @@ -1572,6 +1608,7 @@ exports[` should render stacked BarChart 1`] = ` height="0" rx="2" ry="2" + style="cursor: pointer; opacity: 1;" width="49" x="79" y="598.5" @@ -1586,6 +1623,7 @@ exports[` should render stacked BarChart 1`] = ` height="0" rx="2" ry="2" + style="cursor: pointer; opacity: 1;" width="49" x="276.5" y="647.875" @@ -1600,6 +1638,7 @@ exports[` should render stacked BarChart 1`] = ` height="0" rx="2" ry="2" + style="cursor: pointer; opacity: 1;" width="49" x="474" y="697.25" @@ -1614,6 +1653,7 @@ exports[` should render stacked BarChart 1`] = ` height="0" rx="2" ry="2" + style="cursor: pointer; opacity: 1;" width="49" x="671.5" y="549.125" @@ -1641,6 +1681,7 @@ exports[` should render stacked BarChart 1`] = ` height="0" rx="2" ry="2" + style="cursor: pointer; opacity: 1;" width="49" x="79" y="499.75" @@ -1655,6 +1696,7 @@ exports[` should render stacked BarChart 1`] = ` height="0" rx="2" ry="2" + style="cursor: pointer; opacity: 1;" width="49" x="276.5" y="573.8125" @@ -1669,6 +1711,7 @@ exports[` should render stacked BarChart 1`] = ` height="0" rx="2" ry="2" + style="cursor: pointer; opacity: 1;" width="49" x="474" y="647.875" @@ -1683,6 +1726,7 @@ exports[` should render stacked BarChart 1`] = ` height="0" rx="2" ry="2" + style="cursor: pointer; opacity: 1;" width="49" x="671.5" y="425.6875" From e23e82a1a865d2afb3343b2239f9a91dc25523c5 Mon Sep 17 00:00:00 2001 From: tewarig Date: Wed, 24 Sep 2025 10:11:58 +0530 Subject: [PATCH 098/105] fix: ts issue --- packages/blade/src/components/Charts/BarChart/BarChart.web.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/blade/src/components/Charts/BarChart/BarChart.web.tsx b/packages/blade/src/components/Charts/BarChart/BarChart.web.tsx index 3368c8a3c4d..3bffba38350 100644 --- a/packages/blade/src/components/Charts/BarChart/BarChart.web.tsx +++ b/packages/blade/src/components/Charts/BarChart/BarChart.web.tsx @@ -27,7 +27,7 @@ import { getComponentId } from '~utils/isValidAllowedChildren'; import { makeAnalyticsAttribute } from '~utils/makeAnalyticsAttribute'; import type { DataAnalyticsAttribute, TestID } from '~utils/types'; -const MotionRectangle = (props: MotionRectProps): React.ReactNode => { +const MotionRectangle = (props: MotionRectProps): React.ReactElement => { const controls = useAnimation(); const [animationState, setAnimationState] = useState('idle'); From e9c088df6fee5455b209382cf3df9606a4f5deb0 Mon Sep 17 00:00:00 2001 From: tewarig Date: Wed, 24 Sep 2025 10:56:48 +0530 Subject: [PATCH 099/105] chore: add stacking --- .../src/components/Charts/AreaChart/AreaChart.stories.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/blade/src/components/Charts/AreaChart/AreaChart.stories.tsx b/packages/blade/src/components/Charts/AreaChart/AreaChart.stories.tsx index 27547d948bb..b496f436c6c 100644 --- a/packages/blade/src/components/Charts/AreaChart/AreaChart.stories.tsx +++ b/packages/blade/src/components/Charts/AreaChart/AreaChart.stories.tsx @@ -252,6 +252,14 @@ export const StackedAreaChart: StoryFn = ({ color="chart.background.categorical.azure.moderate" {...args} /> + ); From 8fd2bff9656c5f51b91c75f92733b2f0fe5d8122 Mon Sep 17 00:00:00 2001 From: tewarig Date: Wed, 24 Sep 2025 13:43:38 +0530 Subject: [PATCH 100/105] refactor: code animation --- .../Charts/BarChart/BarChart.web.tsx | 101 +++++------------- .../src/components/Charts/BarChart/tokens.ts | 7 -- .../src/components/Charts/BarChart/types.ts | 2 +- 3 files changed, 25 insertions(+), 85 deletions(-) diff --git a/packages/blade/src/components/Charts/BarChart/BarChart.web.tsx b/packages/blade/src/components/Charts/BarChart/BarChart.web.tsx index 3bffba38350..235e6df5993 100644 --- a/packages/blade/src/components/Charts/BarChart/BarChart.web.tsx +++ b/packages/blade/src/components/Charts/BarChart/BarChart.web.tsx @@ -4,7 +4,7 @@ import { Bar as RechartsBar, ResponsiveContainer as RechartsResponsiveContainer, } from 'recharts'; -import { LazyMotion, domAnimation, m, useAnimation } from 'framer-motion'; +import { LazyMotion, domAnimation, m } from 'framer-motion'; import { useChartsColorTheme } from '../utils'; import { BarChartContext, useBarChartContext } from './BarChartContext'; import type { ChartBarProps, ChartBarWrapperProps, MotionRectProps } from './types'; @@ -15,7 +15,6 @@ import { BAR_SIZE, DISTANCE_BETWEEN_BARS, DISTANCE_BETWEEN_CATEGORY_BARS, - MOTION_TRIGGERS, } from './tokens'; import { useTheme } from '~components/BladeProvider'; import BaseBox from '~components/Box/BaseBox'; @@ -28,65 +27,18 @@ import { makeAnalyticsAttribute } from '~utils/makeAnalyticsAttribute'; import type { DataAnalyticsAttribute, TestID } from '~utils/types'; const MotionRectangle = (props: MotionRectProps): React.ReactElement => { - const controls = useAnimation(); - const [animationState, setAnimationState] = useState('idle'); - - useEffect(() => { - if (props.animationTrigger === MOTION_TRIGGERS.FADE_IN) { - setAnimationState('fadingIn'); - void controls.start({ - fillOpacity: props.fillOpacity, - transition: { - duration: 0.01, - ease: 'easeIn', - delay: 0, - onComplete: () => { - setAnimationState('fadedIn'); - if (props.onFadeInComplete) props.onFadeInComplete(); - }, - }, - }); - } else if (props.animationTrigger === MOTION_TRIGGERS.FADE_OUT) { - setAnimationState('fadingOut'); - void controls.start({ - fillOpacity: 0.2, - transition: { - duration: 0.3, - ease: 'easeIn', - onComplete: () => { - setAnimationState('fadedOut'); - if (props.onFadeOutComplete) props.onFadeOutComplete(); - }, - }, - }); - } else if (props.animationTrigger === MOTION_TRIGGERS.RESET) { - setAnimationState('resetting'); - void controls.start({ - fillOpacity: 1, - transition: { - duration: 0.2, - ease: 'easeOut', - onComplete: () => { - setAnimationState('idle'); - if (props.onResetComplete) props.onResetComplete(); - }, - }, - }); - } - }, [props.animationTrigger, props.fillOpacity, controls, props]); - + const { fillOpacity, barIndex, initialOpacity, ...restProps } = props; return ( ); @@ -122,7 +74,6 @@ const _ChartBar: React.FC = ({ shouldPlayResetAnimation, } = useBarChartContext(); const defaultColorArray = useChartsColorTheme({ colorTheme: _colorTheme ?? 'default' }); - const [isHovered, setIsHovered] = useState(false); const isContainerHovered = isNumber(activeIndex); const fill = color ? getIn(theme.colors, color) : defaultColorArray[_index]; const isStacked = rest.stackId !== undefined; @@ -144,8 +95,6 @@ const _ChartBar: React.FC = ({ label={label} animationBegin={animationBegin} animationDuration={animationDuration} - onMouseEnter={() => setIsHovered(true)} - onMouseLeave={() => setIsHovered(false)} animationEasing="linear" dataKey={dataKey} name={name} @@ -155,23 +104,19 @@ const _ChartBar: React.FC = ({ const gap = DISTANCE_BETWEEN_STACKED_BARS; const isVertical = orientation === 'vertical'; - const getAnimationTrigger = (): string => { - if (!isNumber(activeIndex)) return MOTION_TRIGGERS.RESET; - if (barIndex === activeIndex) return MOTION_TRIGGERS.FADE_IN; - return MOTION_TRIGGERS.FADE_OUT; - }; - const getInitialOpacity = (): number => { - const animationTrigger = getAnimationTrigger(); - if (isContainerHovered) { - if (animationTrigger === MOTION_TRIGGERS.FADE_IN && isFirstActiveIndex) return 1; - if (animationTrigger === MOTION_TRIGGERS.FADE_IN && !isFirstActiveIndex) return 0.2; - if (animationTrigger === MOTION_TRIGGERS.FADE_OUT && isFirstActiveIndex) return 1; - if (animationTrigger === MOTION_TRIGGERS.FADE_OUT && !isFirstActiveIndex) return 0.2; - if (animationTrigger === MOTION_TRIGGERS.RESET && isHovered) return 0.2; + // If container is hovered and it is the first active index (then no need to animate. since the hovered bar will have opacity 1) + // for non hovered bar opactiy will go from 1 to 0.2 + if (isContainerHovered && isFirstActiveIndex) { + return 1; + } + // Now if any bar is changing opactiy that means user is hovering over the bar. it's opacity should go from 0.2 to 1. + if (isContainerHovered && !isFirstActiveIndex) { + return 0.2; } + // In case of reset. the opacity should go from 0.2 to 1. if (shouldPlayResetAnimation) { - if (animationTrigger === MOTION_TRIGGERS.RESET) return 0.2; + return 0.2; } return 1; @@ -188,8 +133,8 @@ const _ChartBar: React.FC = ({ rx={BAR_CHART_CORNER_RADIUS} ry={BAR_CHART_CORNER_RADIUS} fillOpacity={fillOpacity} - animationTrigger={getAnimationTrigger()} initialOpacity={getInitialOpacity()} + barIndex={barIndex} /> ); } @@ -203,8 +148,8 @@ const _ChartBar: React.FC = ({ rx={BAR_CHART_CORNER_RADIUS} ry={BAR_CHART_CORNER_RADIUS} fillOpacity={fillOpacity} - animationTrigger={getAnimationTrigger()} initialOpacity={getInitialOpacity()} + barIndex={barIndex} /> ); }} @@ -226,10 +171,12 @@ const ChartBarWrapper: React.FC { const [activeIndex, setActiveIndex] = useState(undefined); + // Whether to play the reset animation. We play reset animation when user is hovering over the bar chart and then moves the mouse away. const [shouldPlayResetAnimation, setShouldPlayResetAnimation] = useState(false); const prevActiveIndex = useRef(undefined); - // Check if this is the first active index + // Check if this is the first active index (user is hovering over bar chart for the first time) + // We need this to determine the initial opacity of the bar. const isFirstActiveIndex = isNumber(activeIndex) && prevActiveIndex.current === undefined; // Update previous activeIndex diff --git a/packages/blade/src/components/Charts/BarChart/tokens.ts b/packages/blade/src/components/Charts/BarChart/tokens.ts index 1fe49a81e1a..90ce8eef94d 100644 --- a/packages/blade/src/components/Charts/BarChart/tokens.ts +++ b/packages/blade/src/components/Charts/BarChart/tokens.ts @@ -10,12 +10,6 @@ const componentIds = { chartBar: 'ChartBar', }; -const MOTION_TRIGGERS = { - RESET: 'reset', - FADE_IN: 'fadeIn', - FADE_OUT: 'fadeOut', -}; - export { componentIds, DISTANCE_BETWEEN_STACKED_BARS, @@ -24,5 +18,4 @@ export { DISTANCE_BETWEEN_BARS, DISTANCE_BETWEEN_CATEGORY_BARS, ANIMATION_TIME_OFFEST, - MOTION_TRIGGERS, }; diff --git a/packages/blade/src/components/Charts/BarChart/types.ts b/packages/blade/src/components/Charts/BarChart/types.ts index 288d8b7f35a..9b78eb13b37 100644 --- a/packages/blade/src/components/Charts/BarChart/types.ts +++ b/packages/blade/src/components/Charts/BarChart/types.ts @@ -78,12 +78,12 @@ interface BarChartContextType { } type MotionRectProps = ComponentProps & { - animationTrigger: string; initialOpacity?: number; onFadeInComplete?: () => void; onFadeOutComplete?: () => void; onResetComplete?: () => void; fillOpacity: number; + barIndex: number; }; export type { ChartBarProps, ChartBarWrapperProps, BarChartContextType, MotionRectProps }; From 01535f758aefb30129d687ab7fcd91dd9f48327b Mon Sep 17 00:00:00 2001 From: tewarig Date: Wed, 24 Sep 2025 14:19:54 +0530 Subject: [PATCH 101/105] chore: update snaps --- .../__snapshots__/BarChart.web.test.tsx.snap | 44 ------------------- 1 file changed, 44 deletions(-) diff --git a/packages/blade/src/components/Charts/BarChart/__tests__/__snapshots__/BarChart.web.test.tsx.snap b/packages/blade/src/components/Charts/BarChart/__tests__/__snapshots__/BarChart.web.test.tsx.snap index 266e089adb2..4a2731d8ac5 100644 --- a/packages/blade/src/components/Charts/BarChart/__tests__/__snapshots__/BarChart.web.test.tsx.snap +++ b/packages/blade/src/components/Charts/BarChart/__tests__/__snapshots__/BarChart.web.test.tsx.snap @@ -73,7 +73,6 @@ exports[` should render BarChart with custom colors 1`] = ` height="0" rx="2" ry="2" - style="cursor: pointer; opacity: 1;" width="49" x="53" y="796" @@ -88,7 +87,6 @@ exports[` should render BarChart with custom colors 1`] = ` height="0" rx="2" ry="2" - style="cursor: pointer; opacity: 1;" width="49" x="250.5" y="796" @@ -103,7 +101,6 @@ exports[` should render BarChart with custom colors 1`] = ` height="0" rx="2" ry="2" - style="cursor: pointer; opacity: 1;" width="49" x="448" y="796" @@ -118,7 +115,6 @@ exports[` should render BarChart with custom colors 1`] = ` height="0" rx="2" ry="2" - style="cursor: pointer; opacity: 1;" width="49" x="645.5" y="796" @@ -146,7 +142,6 @@ exports[` should render BarChart with custom colors 1`] = ` height="0" rx="2" ry="2" - style="cursor: pointer; opacity: 1;" width="49" x="104" y="796" @@ -161,7 +156,6 @@ exports[` should render BarChart with custom colors 1`] = ` height="0" rx="2" ry="2" - style="cursor: pointer; opacity: 1;" width="49" x="301.5" y="796" @@ -176,7 +170,6 @@ exports[` should render BarChart with custom colors 1`] = ` height="0" rx="2" ry="2" - style="cursor: pointer; opacity: 1;" width="49" x="499" y="796" @@ -191,7 +184,6 @@ exports[` should render BarChart with custom colors 1`] = ` height="0" rx="2" ry="2" - style="cursor: pointer; opacity: 1;" width="49" x="696.5" y="796" @@ -282,7 +274,6 @@ exports[` should render BarChart with multiple bars 1`] = ` height="0" rx="2" ry="2" - style="cursor: pointer; opacity: 1;" width="49" x="28" y="796" @@ -297,7 +288,6 @@ exports[` should render BarChart with multiple bars 1`] = ` height="0" rx="2" ry="2" - style="cursor: pointer; opacity: 1;" width="49" x="225.5" y="796" @@ -312,7 +302,6 @@ exports[` should render BarChart with multiple bars 1`] = ` height="0" rx="2" ry="2" - style="cursor: pointer; opacity: 1;" width="49" x="423" y="796" @@ -327,7 +316,6 @@ exports[` should render BarChart with multiple bars 1`] = ` height="0" rx="2" ry="2" - style="cursor: pointer; opacity: 1;" width="49" x="620.5" y="796" @@ -355,7 +343,6 @@ exports[` should render BarChart with multiple bars 1`] = ` height="0" rx="2" ry="2" - style="cursor: pointer; opacity: 1;" width="49" x="79" y="796" @@ -370,7 +357,6 @@ exports[` should render BarChart with multiple bars 1`] = ` height="0" rx="2" ry="2" - style="cursor: pointer; opacity: 1;" width="49" x="276.5" y="796" @@ -385,7 +371,6 @@ exports[` should render BarChart with multiple bars 1`] = ` height="0" rx="2" ry="2" - style="cursor: pointer; opacity: 1;" width="49" x="474" y="796" @@ -400,7 +385,6 @@ exports[` should render BarChart with multiple bars 1`] = ` height="0" rx="2" ry="2" - style="cursor: pointer; opacity: 1;" width="49" x="671.5" y="796" @@ -428,7 +412,6 @@ exports[` should render BarChart with multiple bars 1`] = ` height="0" rx="2" ry="2" - style="cursor: pointer; opacity: 1;" width="49" x="130" y="796" @@ -443,7 +426,6 @@ exports[` should render BarChart with multiple bars 1`] = ` height="0" rx="2" ry="2" - style="cursor: pointer; opacity: 1;" width="49" x="327.5" y="796" @@ -458,7 +440,6 @@ exports[` should render BarChart with multiple bars 1`] = ` height="0" rx="2" ry="2" - style="cursor: pointer; opacity: 1;" width="49" x="525" y="796" @@ -473,7 +454,6 @@ exports[` should render BarChart with multiple bars 1`] = ` height="0" rx="2" ry="2" - style="cursor: pointer; opacity: 1;" width="49" x="722.5" y="796" @@ -564,7 +544,6 @@ exports[` should render basic BarChart with single bar 1`] = ` height="0" rx="2" ry="2" - style="cursor: pointer; opacity: 1;" width="49" x="79" y="796" @@ -579,7 +558,6 @@ exports[` should render basic BarChart with single bar 1`] = ` height="0" rx="2" ry="2" - style="cursor: pointer; opacity: 1;" width="49" x="276.5" y="796" @@ -594,7 +572,6 @@ exports[` should render basic BarChart with single bar 1`] = ` height="0" rx="2" ry="2" - style="cursor: pointer; opacity: 1;" width="49" x="474" y="796" @@ -609,7 +586,6 @@ exports[` should render basic BarChart with single bar 1`] = ` height="0" rx="2" ry="2" - style="cursor: pointer; opacity: 1;" width="49" x="671.5" y="796" @@ -1326,7 +1302,6 @@ exports[` should render complete chart with all components 1`] = ` height="0" rx="2" ry="2" - style="cursor: pointer; opacity: 1;" width="49" x="106" y="766" @@ -1341,7 +1316,6 @@ exports[` should render complete chart with all components 1`] = ` height="0" rx="2" ry="2" - style="cursor: pointer; opacity: 1;" width="49" x="288.5" y="766" @@ -1356,7 +1330,6 @@ exports[` should render complete chart with all components 1`] = ` height="0" rx="2" ry="2" - style="cursor: pointer; opacity: 1;" width="49" x="471" y="766" @@ -1371,7 +1344,6 @@ exports[` should render complete chart with all components 1`] = ` height="0" rx="2" ry="2" - style="cursor: pointer; opacity: 1;" width="49" x="653.5" y="766" @@ -1399,7 +1371,6 @@ exports[` should render complete chart with all components 1`] = ` height="0" rx="2" ry="2" - style="cursor: pointer; opacity: 1;" width="49" x="157" y="766" @@ -1414,7 +1385,6 @@ exports[` should render complete chart with all components 1`] = ` height="0" rx="2" ry="2" - style="cursor: pointer; opacity: 1;" width="49" x="339.5" y="766" @@ -1429,7 +1399,6 @@ exports[` should render complete chart with all components 1`] = ` height="0" rx="2" ry="2" - style="cursor: pointer; opacity: 1;" width="49" x="522" y="766" @@ -1444,7 +1413,6 @@ exports[` should render complete chart with all components 1`] = ` height="0" rx="2" ry="2" - style="cursor: pointer; opacity: 1;" width="49" x="704.5" y="766" @@ -1535,7 +1503,6 @@ exports[` should render stacked BarChart 1`] = ` height="0" rx="2" ry="2" - style="cursor: pointer; opacity: 1;" width="49" x="79" y="796" @@ -1550,7 +1517,6 @@ exports[` should render stacked BarChart 1`] = ` height="0" rx="2" ry="2" - style="cursor: pointer; opacity: 1;" width="49" x="276.5" y="796" @@ -1565,7 +1531,6 @@ exports[` should render stacked BarChart 1`] = ` height="0" rx="2" ry="2" - style="cursor: pointer; opacity: 1;" width="49" x="474" y="796" @@ -1580,7 +1545,6 @@ exports[` should render stacked BarChart 1`] = ` height="0" rx="2" ry="2" - style="cursor: pointer; opacity: 1;" width="49" x="671.5" y="796" @@ -1608,7 +1572,6 @@ exports[` should render stacked BarChart 1`] = ` height="0" rx="2" ry="2" - style="cursor: pointer; opacity: 1;" width="49" x="79" y="598.5" @@ -1623,7 +1586,6 @@ exports[` should render stacked BarChart 1`] = ` height="0" rx="2" ry="2" - style="cursor: pointer; opacity: 1;" width="49" x="276.5" y="647.875" @@ -1638,7 +1600,6 @@ exports[` should render stacked BarChart 1`] = ` height="0" rx="2" ry="2" - style="cursor: pointer; opacity: 1;" width="49" x="474" y="697.25" @@ -1653,7 +1614,6 @@ exports[` should render stacked BarChart 1`] = ` height="0" rx="2" ry="2" - style="cursor: pointer; opacity: 1;" width="49" x="671.5" y="549.125" @@ -1681,7 +1641,6 @@ exports[` should render stacked BarChart 1`] = ` height="0" rx="2" ry="2" - style="cursor: pointer; opacity: 1;" width="49" x="79" y="499.75" @@ -1696,7 +1655,6 @@ exports[` should render stacked BarChart 1`] = ` height="0" rx="2" ry="2" - style="cursor: pointer; opacity: 1;" width="49" x="276.5" y="573.8125" @@ -1711,7 +1669,6 @@ exports[` should render stacked BarChart 1`] = ` height="0" rx="2" ry="2" - style="cursor: pointer; opacity: 1;" width="49" x="474" y="647.875" @@ -1726,7 +1683,6 @@ exports[` should render stacked BarChart 1`] = ` height="0" rx="2" ry="2" - style="cursor: pointer; opacity: 1;" width="49" x="671.5" y="425.6875" From e3a8769677ac8a82a5e32e2f2a42ea9096994b03 Mon Sep 17 00:00:00 2001 From: tewarig Date: Wed, 24 Sep 2025 15:13:01 +0530 Subject: [PATCH 102/105] chore: remove animation :( --- .../Charts/BarChart/BarChart.web.tsx | 93 +++---------------- .../Charts/BarChart/BarChartContext.ts | 2 - .../BarChart/__tests__/BarChart.web.test.tsx | 2 +- .../src/components/Charts/BarChart/types.ts | 15 +-- 4 files changed, 13 insertions(+), 99 deletions(-) diff --git a/packages/blade/src/components/Charts/BarChart/BarChart.web.tsx b/packages/blade/src/components/Charts/BarChart/BarChart.web.tsx index 235e6df5993..4a4aefbf778 100644 --- a/packages/blade/src/components/Charts/BarChart/BarChart.web.tsx +++ b/packages/blade/src/components/Charts/BarChart/BarChart.web.tsx @@ -1,13 +1,12 @@ -import React, { useEffect, useState, useRef } from 'react'; +import React, { useState } from 'react'; import { BarChart as RechartsBarChart, Bar as RechartsBar, ResponsiveContainer as RechartsResponsiveContainer, } from 'recharts'; -import { LazyMotion, domAnimation, m } from 'framer-motion'; import { useChartsColorTheme } from '../utils'; import { BarChartContext, useBarChartContext } from './BarChartContext'; -import type { ChartBarProps, ChartBarWrapperProps, MotionRectProps } from './types'; +import type { ChartBarProps, ChartBarWrapperProps } from './types'; import { BAR_CHART_CORNER_RADIUS, DISTANCE_BETWEEN_STACKED_BARS, @@ -26,24 +25,6 @@ import { getComponentId } from '~utils/isValidAllowedChildren'; import { makeAnalyticsAttribute } from '~utils/makeAnalyticsAttribute'; import type { DataAnalyticsAttribute, TestID } from '~utils/types'; -const MotionRectangle = (props: MotionRectProps): React.ReactElement => { - const { fillOpacity, barIndex, initialOpacity, ...restProps } = props; - return ( - - - - ); -}; - export type RechartsShapeProps = { x: number; y: number; @@ -65,26 +46,16 @@ const _ChartBar: React.FC = ({ ...rest }) => { const { theme } = useTheme(); - const { - orientation, - activeIndex, - colorTheme: _colorTheme, - totalBars, - isFirstActiveIndex, - shouldPlayResetAnimation, - } = useBarChartContext(); + const { orientation, activeIndex, colorTheme: _colorTheme, totalBars } = useBarChartContext(); const defaultColorArray = useChartsColorTheme({ colorTheme: _colorTheme ?? 'default' }); - const isContainerHovered = isNumber(activeIndex); const fill = color ? getIn(theme.colors, color) : defaultColorArray[_index]; const isStacked = rest.stackId !== undefined; - const animationBegin = - isStacked && !isContainerHovered - ? (theme.motion.duration.gentle / totalBars) * _index - : theme.motion.duration.gentle; - const animationDuration = - isStacked && !isContainerHovered - ? theme.motion.duration.gentle / totalBars - : theme.motion.duration.gentle; + const animationBegin = isStacked + ? (theme.motion.duration.gentle / totalBars) * _index + : theme.motion.duration.gentle; + const animationDuration = isStacked + ? theme.motion.duration.gentle / totalBars + : theme.motion.duration.gentle; return ( = ({ const gap = DISTANCE_BETWEEN_STACKED_BARS; const isVertical = orientation === 'vertical'; - const getInitialOpacity = (): number => { - // If container is hovered and it is the first active index (then no need to animate. since the hovered bar will have opacity 1) - // for non hovered bar opactiy will go from 1 to 0.2 - if (isContainerHovered && isFirstActiveIndex) { - return 1; - } - // Now if any bar is changing opactiy that means user is hovering over the bar. it's opacity should go from 0.2 to 1. - if (isContainerHovered && !isFirstActiveIndex) { - return 0.2; - } - // In case of reset. the opacity should go from 0.2 to 1. - if (shouldPlayResetAnimation) { - return 0.2; - } - - return 1; - }; - if (isVertical) { return ( - = ({ rx={BAR_CHART_CORNER_RADIUS} ry={BAR_CHART_CORNER_RADIUS} fillOpacity={fillOpacity} - initialOpacity={getInitialOpacity()} - barIndex={barIndex} /> ); } return ( - = ({ rx={BAR_CHART_CORNER_RADIUS} ry={BAR_CHART_CORNER_RADIUS} fillOpacity={fillOpacity} - initialOpacity={getInitialOpacity()} - barIndex={barIndex} /> ); }} @@ -171,18 +120,6 @@ const ChartBarWrapper: React.FC { const [activeIndex, setActiveIndex] = useState(undefined); - // Whether to play the reset animation. We play reset animation when user is hovering over the bar chart and then moves the mouse away. - const [shouldPlayResetAnimation, setShouldPlayResetAnimation] = useState(false); - const prevActiveIndex = useRef(undefined); - - // Check if this is the first active index (user is hovering over bar chart for the first time) - // We need this to determine the initial opacity of the bar. - const isFirstActiveIndex = isNumber(activeIndex) && prevActiveIndex.current === undefined; - - // Update previous activeIndex - useEffect(() => { - prevActiveIndex.current = activeIndex; - }, [activeIndex]); const { barChartModifiedChildrens, totalBars } = React.useMemo(() => { let BarChartIndex = 0; @@ -213,8 +150,6 @@ const ChartBarWrapper: React.FC { - if (isNumber(prevActiveIndex.current) && !state?.activeIndex) { - setShouldPlayResetAnimation(true); - setTimeout(() => { - setShouldPlayResetAnimation(false); - }, 1000); - } setActiveIndex(state?.activeIndex ? Number(state?.activeIndex) : undefined); }} layout={orientation} diff --git a/packages/blade/src/components/Charts/BarChart/BarChartContext.ts b/packages/blade/src/components/Charts/BarChart/BarChartContext.ts index af9663d572f..570d0785ebd 100644 --- a/packages/blade/src/components/Charts/BarChart/BarChartContext.ts +++ b/packages/blade/src/components/Charts/BarChart/BarChartContext.ts @@ -6,8 +6,6 @@ export const BarChartContext = createContext({ activeIndex: undefined, colorTheme: 'default', totalBars: 0, - isFirstActiveIndex: false, - shouldPlayResetAnimation: false, }); export const useBarChartContext = (): BarChartContextType => useContext(BarChartContext); diff --git a/packages/blade/src/components/Charts/BarChart/__tests__/BarChart.web.test.tsx b/packages/blade/src/components/Charts/BarChart/__tests__/BarChart.web.test.tsx index 6eb1bdff002..c5baa8540c7 100644 --- a/packages/blade/src/components/Charts/BarChart/__tests__/BarChart.web.test.tsx +++ b/packages/blade/src/components/Charts/BarChart/__tests__/BarChart.web.test.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { ChartBarWrapper, ChartBar } from '../BarChart.web'; +import { ChartBarWrapper, ChartBar } from '../BarChart'; import { ChartXAxis, ChartYAxis, diff --git a/packages/blade/src/components/Charts/BarChart/types.ts b/packages/blade/src/components/Charts/BarChart/types.ts index 9b78eb13b37..15d32d10cf2 100644 --- a/packages/blade/src/components/Charts/BarChart/types.ts +++ b/packages/blade/src/components/Charts/BarChart/types.ts @@ -1,6 +1,4 @@ -import type { ComponentProps } from 'react'; import type { BarProps as RechartsBarProps } from 'recharts'; -import type { m } from 'framer-motion'; import type { ChartsCategoricalColorToken, ChartSequentialColorToken, @@ -73,17 +71,6 @@ interface BarChartContextType { activeIndex?: number; colorTheme: colorTheme; totalBars: number; - isFirstActiveIndex?: boolean; - shouldPlayResetAnimation?: boolean; } -type MotionRectProps = ComponentProps & { - initialOpacity?: number; - onFadeInComplete?: () => void; - onFadeOutComplete?: () => void; - onResetComplete?: () => void; - fillOpacity: number; - barIndex: number; -}; - -export type { ChartBarProps, ChartBarWrapperProps, BarChartContextType, MotionRectProps }; +export type { ChartBarProps, ChartBarWrapperProps, BarChartContextType }; From 5328255eda9566c14b35f84e1f6eb5d3da35808d Mon Sep 17 00:00:00 2001 From: tewarig Date: Wed, 24 Sep 2025 15:19:16 +0530 Subject: [PATCH 103/105] review: change orientation to layout --- .../components/Charts/BarChart/BarChart.stories.tsx | 4 ++-- .../src/components/Charts/BarChart/BarChart.web.tsx | 10 +++++----- .../src/components/Charts/BarChart/BarChartContext.ts | 2 +- packages/blade/src/components/Charts/BarChart/types.ts | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/blade/src/components/Charts/BarChart/BarChart.stories.tsx b/packages/blade/src/components/Charts/BarChart/BarChart.stories.tsx index 7b93543ca14..1c175f64346 100644 --- a/packages/blade/src/components/Charts/BarChart/BarChart.stories.tsx +++ b/packages/blade/src/components/Charts/BarChart/BarChart.stories.tsx @@ -259,7 +259,7 @@ GroupedBarChart.parameters = { export const VerticalBarChart: StoryFn = () => { return (
      - + @@ -295,7 +295,7 @@ VerticalBarChart.parameters = { export const BarChartWithDefaultColorTheme: StoryFn = () => { return (
      - + diff --git a/packages/blade/src/components/Charts/BarChart/BarChart.web.tsx b/packages/blade/src/components/Charts/BarChart/BarChart.web.tsx index 4a4aefbf778..2f0144cf703 100644 --- a/packages/blade/src/components/Charts/BarChart/BarChart.web.tsx +++ b/packages/blade/src/components/Charts/BarChart/BarChart.web.tsx @@ -46,7 +46,7 @@ const _ChartBar: React.FC = ({ ...rest }) => { const { theme } = useTheme(); - const { orientation, activeIndex, colorTheme: _colorTheme, totalBars } = useBarChartContext(); + const { layout, activeIndex, colorTheme: _colorTheme, totalBars } = useBarChartContext(); const defaultColorArray = useChartsColorTheme({ colorTheme: _colorTheme ?? 'default' }); const fill = color ? getIn(theme.colors, color) : defaultColorArray[_index]; const isStacked = rest.stackId !== undefined; @@ -73,7 +73,7 @@ const _ChartBar: React.FC = ({ const { fill, x, y, width, height, index: barIndex } = props as RechartsShapeProps; const fillOpacity = isNumber(activeIndex) ? (barIndex === activeIndex ? 1 : 0.2) : 1; const gap = DISTANCE_BETWEEN_STACKED_BARS; - const isVertical = orientation === 'vertical'; + const isVertical = layout === 'vertical'; if (isVertical) { return ( @@ -114,7 +114,7 @@ const ChartBar = assignWithoutSideEffects(_ChartBar, { const ChartBarWrapper: React.FC = ({ children, colorTheme = 'default', - orientation = 'horizontal', + layout = 'horizontal', testID, data = [], ...restProps @@ -148,7 +148,7 @@ const ChartBarWrapper: React.FC { setActiveIndex(state?.activeIndex ? Number(state?.activeIndex) : undefined); }} - layout={orientation} + layout={layout} data={data} > {barChartModifiedChildrens} diff --git a/packages/blade/src/components/Charts/BarChart/BarChartContext.ts b/packages/blade/src/components/Charts/BarChart/BarChartContext.ts index 570d0785ebd..b13de58bcd4 100644 --- a/packages/blade/src/components/Charts/BarChart/BarChartContext.ts +++ b/packages/blade/src/components/Charts/BarChart/BarChartContext.ts @@ -2,7 +2,7 @@ import { createContext, useContext } from 'react'; import type { BarChartContextType } from './types'; export const BarChartContext = createContext({ - orientation: 'horizontal', + layout: 'horizontal', activeIndex: undefined, colorTheme: 'default', totalBars: 0, diff --git a/packages/blade/src/components/Charts/BarChart/types.ts b/packages/blade/src/components/Charts/BarChart/types.ts index 15d32d10cf2..ec90dc39e13 100644 --- a/packages/blade/src/components/Charts/BarChart/types.ts +++ b/packages/blade/src/components/Charts/BarChart/types.ts @@ -59,7 +59,7 @@ type ChartBarWrapperProps = { /** * The layout of the bar chart. */ - orientation?: 'horizontal' | 'vertical'; + layout?: 'horizontal' | 'vertical'; /** * Chart data to be rendered */ @@ -67,7 +67,7 @@ type ChartBarWrapperProps = { } & Partial>; interface BarChartContextType { - orientation?: 'horizontal' | 'vertical'; + layout?: 'horizontal' | 'vertical'; activeIndex?: number; colorTheme: colorTheme; totalBars: number; From 4fc94f2e01f936192fc8e9718dd8c867b980b63a Mon Sep 17 00:00:00 2001 From: tewarig Date: Wed, 24 Sep 2025 15:20:40 +0530 Subject: [PATCH 104/105] chore: update layout --- packages/blade-mcp/knowledgebase/components/BarChart.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/blade-mcp/knowledgebase/components/BarChart.md b/packages/blade-mcp/knowledgebase/components/BarChart.md index 81c32fc708e..b5eedbd863b 100644 --- a/packages/blade-mcp/knowledgebase/components/BarChart.md +++ b/packages/blade-mcp/knowledgebase/components/BarChart.md @@ -66,7 +66,7 @@ type ChartBarWrapperProps = { /** * The orientation of the bar chart. */ - orientation?: 'horizontal' | 'vertical'; + layout?: 'horizontal' | 'vertical'; /** * Chart data to be rendered */ From 37cb2c47dc29190d102e8947e15d6b55172ce80f Mon Sep 17 00:00:00 2001 From: tewarig Date: Wed, 24 Sep 2025 15:30:19 +0530 Subject: [PATCH 105/105] chore: update types --- packages/blade-mcp/knowledgebase/components/AreaChart.md | 4 ++-- packages/blade-mcp/knowledgebase/components/BarChart.md | 2 +- packages/blade-mcp/knowledgebase/components/LineChart.md | 2 +- packages/blade/src/components/Charts/AreaChart/types.ts | 8 ++------ packages/blade/src/components/Charts/BarChart/types.ts | 8 ++------ packages/blade/src/components/Charts/LineChart/types.ts | 8 ++------ 6 files changed, 10 insertions(+), 22 deletions(-) diff --git a/packages/blade-mcp/knowledgebase/components/AreaChart.md b/packages/blade-mcp/knowledgebase/components/AreaChart.md index dc62bc8cfb9..cfdb7076c4c 100644 --- a/packages/blade-mcp/knowledgebase/components/AreaChart.md +++ b/packages/blade-mcp/knowledgebase/components/AreaChart.md @@ -43,7 +43,7 @@ type ChartAreaWrapperProps = { children?: React.ReactNode; colorTheme?: colorTheme; data: data[]; -}; +} & BoxProps; type ChartReferenceLineProps = { /** @@ -136,4 +136,4 @@ function BasicAreaChart() { ); } -``` \ No newline at end of file +``` diff --git a/packages/blade-mcp/knowledgebase/components/BarChart.md b/packages/blade-mcp/knowledgebase/components/BarChart.md index b5eedbd863b..a65e73d1f3d 100644 --- a/packages/blade-mcp/knowledgebase/components/BarChart.md +++ b/packages/blade-mcp/knowledgebase/components/BarChart.md @@ -71,7 +71,7 @@ type ChartBarWrapperProps = { * Chart data to be rendered */ data: data[]; -} & Partial>; +} & BoxProps; type ChartsCategoricalColorToken = `chart.background.categorical.${ChartColorCategories}.${keyof ChartCategoricalEmphasis}`; diff --git a/packages/blade-mcp/knowledgebase/components/LineChart.md b/packages/blade-mcp/knowledgebase/components/LineChart.md index 9d9b17adc44..a2dc9960e3a 100644 --- a/packages/blade-mcp/knowledgebase/components/LineChart.md +++ b/packages/blade-mcp/knowledgebase/components/LineChart.md @@ -77,7 +77,7 @@ type ChartLineWrapperProps = { */ data: data[]; children: React.ReactNode; -} & Partial>; +} & BoxProps; type ChartReferenceLineProps = { /** diff --git a/packages/blade/src/components/Charts/AreaChart/types.ts b/packages/blade/src/components/Charts/AreaChart/types.ts index b9ec328ace4..3b472141e75 100644 --- a/packages/blade/src/components/Charts/AreaChart/types.ts +++ b/packages/blade/src/components/Charts/AreaChart/types.ts @@ -1,11 +1,7 @@ import type { AreaProps as RechartAreaProps } from 'recharts'; import type { ChartsCategoricalColorToken } from '../CommonChartComponents/types'; import type { colorTheme } from '../utils'; -import type { - BaseBoxProps, - FlexboxProps, - GridProps, -} from '~components/Box/BaseBox/types/propsTypes'; +import type { BoxProps } from '~components/Box'; type ChartAreaProps = { /** @@ -68,6 +64,6 @@ type ChartAreaWrapperProps = { * Chart data to be rendered */ data: data[]; -} & Partial>; +} & BoxProps; export type { ChartAreaProps, ChartAreaWrapperProps }; diff --git a/packages/blade/src/components/Charts/BarChart/types.ts b/packages/blade/src/components/Charts/BarChart/types.ts index ec90dc39e13..d9fdce0c1b4 100644 --- a/packages/blade/src/components/Charts/BarChart/types.ts +++ b/packages/blade/src/components/Charts/BarChart/types.ts @@ -4,11 +4,7 @@ import type { ChartSequentialColorToken, } from '../CommonChartComponents/types'; import type { colorTheme } from '../utils'; -import type { - BaseBoxProps, - FlexboxProps, - GridProps, -} from '~components/Box/BaseBox/types/propsTypes'; +import type { BoxProps } from '~components/Box'; type ChartBarProps = { /** @@ -64,7 +60,7 @@ type ChartBarWrapperProps = { * Chart data to be rendered */ data: data[]; -} & Partial>; +} & BoxProps; interface BarChartContextType { layout?: 'horizontal' | 'vertical'; diff --git a/packages/blade/src/components/Charts/LineChart/types.ts b/packages/blade/src/components/Charts/LineChart/types.ts index 550a83d655e..cd195bd5bfd 100644 --- a/packages/blade/src/components/Charts/LineChart/types.ts +++ b/packages/blade/src/components/Charts/LineChart/types.ts @@ -1,11 +1,7 @@ import type { LineProps as RechartsLineProps } from 'recharts'; import type { ChartsCategoricalColorToken } from '../CommonChartComponents/types'; import type { colorTheme } from '../utils'; -import type { - BaseBoxProps, - FlexboxProps, - GridProps, -} from '~components/Box/BaseBox/types/propsTypes'; +import type { BoxProps } from '~components/Box'; interface ChartLineProps { /** @@ -74,6 +70,6 @@ type ChartLineWrapperProps = { */ data: data[]; children: React.ReactNode; -} & Partial>; +} & BoxProps; export type { ChartLineProps, ChartLineWrapperProps };