Skip to content

add bar chart and pie chart #72

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Apr 9, 2025
Merged

add bar chart and pie chart #72

merged 6 commits into from
Apr 9, 2025

Conversation

0xKurt
Copy link
Member

@0xKurt 0xKurt commented Apr 8, 2025

Summary by CodeRabbit

  • New Features
    • Launched a versatile Bar Chart component with multiple configuration options—including daily and multiple series, time series, numeric, categorical views, and customizable legends.
    • Introduced a flexible Pie Chart component featuring several display modes such as basic, donut, custom colors, label-free, and with legend.
    • Enhanced interactive examples in Storybook to guide users through diverse chart configurations and improve the overall visualization experience.

Copy link

vercel bot commented Apr 8, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
gitcoin-ui ✅ Ready (Inspect) Visit Preview 💬 Add feedback Apr 9, 2025 0:29am

Copy link

changeset-bot bot commented Apr 8, 2025

⚠️ No Changeset found

Latest commit: 31a24eb

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Copy link
Contributor

coderabbitai bot commented Apr 8, 2025

Walkthrough

This pull request introduces two new chart components—BarChart and PieChart—each with full Storybook support and accompanying examples. The components, along with their TypeScript interfaces, are defined to accept various data formats and configuration options. Additionally, new index files re-export the components and their types, and the main UI index has been updated to include these exports.

Changes

File(s) Changes
packages/ui/src/components/BarChart/BarChart.stories.tsx, packages/ui/src/components/BarChart/BarChart.tsx, packages/ui/src/components/BarChart/index.ts Added BarChart component with a TypeScript interface, several Storybook stories (Default, WithMultipleSeries, WithNumericXAxis, TimeSeries, MultipleSeries, Categories, NumericAxis, NoLegend), and an index file for re-exporting.
packages/ui/src/components/PieChart/PieChart.stories.tsx, packages/ui/src/components/PieChart/PieChart.tsx, packages/ui/src/components/PieChart/index.ts Added PieChart component with a TypeScript interface, multiple Storybook stories (Basic, DonutChart, CustomColors, NoValues, WithLegend), and an index file for re-exporting.
packages/ui/src/index.ts Updated the main UI index to export both PieChart and BarChart components.

Sequence Diagram(s)

sequenceDiagram
    participant SB as Storybook
    participant BC as BarChart Component
    participant RC as Recharts Library

    SB->>BC: Render BarChart with props (data, configurations)
    BC->>BC: Transform input data to chartData format
    BC->>RC: Invoke rendering of Bar elements (axes, tooltips, legend)
    RC-->>SB: Return rendered BarChart
Loading
sequenceDiagram
    participant SB as Storybook
    participant PC as PieChart Component
    participant RC as Recharts Library

    SB->>PC: Render PieChart with props (data, title, total)
    PC->>PC: Process data and assign default colors
    PC->>RC: Call rendering of Pie, Legend, and label (if total provided)
    RC-->>SB: Return rendered PieChart
Loading

Poem

I'm a rabbit of code, so light on my feet,
Hopping through charts that look pretty neat.
Bars rise in rows, and pies spin around,
With stories and exports all safe and sound.
Leaping with joy at each new data beat!
🐇💻🎉

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

packages/ui/src/components/BarChart/BarChart.tsx

Oops! Something went wrong! :(

ESLint: 8.57.1

ESLint couldn't find the plugin "eslint-plugin-storybook".

(The package "eslint-plugin-storybook" was not found when loaded as a Node module from the directory "/packages/ui".)

It's likely that the plugin isn't installed correctly. Try reinstalling by running the following:

npm install eslint-plugin-storybook@latest --save-dev

The plugin "eslint-plugin-storybook" was referenced from the config file in "packages/ui/package.json".

If you still can't figure out the problem, please stop by https://eslint.org/chat/help to chat with the team.

✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai plan to trigger planning for file edits and PR creation.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🧹 Nitpick comments (6)
packages/ui/src/components/PieChart/PieChart.stories.tsx (2)

70-83: Consider adding a visible indication for the NoValues story.

While the NoValues story is appropriately named and described, the actual implementation might not be immediately clear from the story args. Consider adding a showValues: false prop to make it more explicit how this functionality is controlled.

  args: {
    width: 800,
    data: [
      { name: "Category A", value: 30 },
      { name: "Category B", value: 20 },
      { name: "Category C", value: 15 },
      { name: "Category D", value: 25 },
      { name: "Category E", value: 10 },
    ],
    title: "No Values",
    description: "Hiding value labels",
+   showValues: false,
  },

85-98: Legend control isn't explicitly shown in WithLegend story.

Similar to the NoValues story, it's not clear from the args how the legend is actually enabled. Consider adding a showLegend: true prop to make it more explicit.

  args: {
    width: 800,
    data: [
      { name: "Category A", value: 30 },
      { name: "Category B", value: 20 },
      { name: "Category C", value: 15 },
      { name: "Category D", value: 25 },
      { name: "Category E", value: 10 },
    ],
    title: "Distribution with Legend",
    description: "Showing data distribution with a legend",
+   showLegend: true,
  },
packages/ui/src/components/BarChart/BarChart.tsx (1)

96-161: Add responsive width handling

The chart container has a fixed height of 400px but doesn't adjust width responsively. This may cause rendering issues on different screen sizes.

- <ChartContainer config={chartConfig} className="mx-auto aspect-auto h-[400px] w-full">
+ <ChartContainer config={chartConfig} className="mx-auto aspect-auto h-[400px] w-full lg:w-auto">

Also, consider adding a responsive prop to the RechartsBarChart component to make it adapt to its container:

  <RechartsBarChart
    data={chartData}
+   responsive={true}
    margin={{
      top: 20,
      right: 30,
      left: 20,
      bottom: 60,
    }}
  >
packages/ui/src/components/PieChart/PieChart.tsx (2)

8-30: Consider extracting color constants

The DEFAULT_COLORS array is quite large. If it will be used across multiple components, consider extracting it to a shared constants file.

You could move this to something like packages/ui/src/constants/colors.ts:

export const DEFAULT_CHART_COLORS = [
  "#D9D6FF",
  "#B3B0FF",
  // ...rest of colors
];

Then import it with:

+ import { DEFAULT_CHART_COLORS } from "../../constants/colors";

- const DEFAULT_COLORS = [
-   "#D9D6FF",
-   "#B3B0FF",
-   // ...etc
- ];

86-106: Handle potential unexpected values

The component doesn't handle cases where data items might have zero or negative values. Consider adding validation or handling for these edge cases.

You could add validation to ensure that the chart displays correctly even with zero or negative values:

  <Pie
    data={chartData}
    dataKey="value"
    nameKey="name"
-   label
+   label={({value}) => value > 0 ? value.toLocaleString() : ''}
    outerRadius={total ? 120 : 140}
    innerRadius={total ? 60 : 0}
    paddingAngle={total ? 2 : 0}
  >
packages/ui/src/components/BarChart/BarChart.stories.tsx (1)

28-40: Ensure consistent data generation for time series

The current implementation generates random values for each data point, which makes it difficult to visually test and verify the component behavior consistently.

Consider using deterministic values or a seeded random generator for more predictable story rendering:

function generateTimeSeriesData(days = 30) {
  const data = [];
  const now = new Date();
+  // Use a seeded value for consistent random generation
+  let seed = 42;
+  const random = () => {
+    const x = Math.sin(seed++) * 10000;
+    return Math.floor((x - Math.floor(x)) * 1000) + 500;
+  };
  
  for (let i = days - 1; i >= 0; i--) {
    const date = new Date(now);
    date.setDate(date.getDate() - i);
    data.push({
      x: date.toISOString().split("T")[0],
-     y: Math.floor(Math.random() * 1000) + 500,
+     y: random(),
    });
  }
  return data;
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8450b60 and e360dad.

📒 Files selected for processing (7)
  • packages/ui/src/components/BarChart/BarChart.stories.tsx (1 hunks)
  • packages/ui/src/components/BarChart/BarChart.tsx (1 hunks)
  • packages/ui/src/components/BarChart/index.ts (1 hunks)
  • packages/ui/src/components/PieChart/PieChart.stories.tsx (1 hunks)
  • packages/ui/src/components/PieChart/PieChart.tsx (1 hunks)
  • packages/ui/src/components/PieChart/index.ts (1 hunks)
  • packages/ui/src/index.ts (1 hunks)
🧰 Additional context used
🧬 Code Definitions (3)
packages/ui/src/components/BarChart/BarChart.tsx (1)
packages/ui/src/components/BarChart/index.ts (2)
  • BarChartProps (2-2)
  • BarChart (1-1)
packages/ui/src/components/BarChart/BarChart.stories.tsx (1)
packages/ui/src/components/BarChart/BarChart.tsx (1)
  • BarChart (36-165)
packages/ui/src/components/PieChart/PieChart.tsx (1)
packages/ui/src/components/PieChart/index.ts (2)
  • PieChartProps (2-2)
  • PieChart (1-1)
🪛 Biome (1.9.4)
packages/ui/src/components/BarChart/BarChart.tsx

[error] 63-63: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)

packages/ui/src/components/PieChart/PieChart.tsx

[error] 63-63: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)

🔇 Additional comments (9)
packages/ui/src/components/BarChart/index.ts (1)

1-2: Barrel file export pattern implemented correctly.

This follows the standard barrel file pattern for exporting the component and its props type, which aligns with the project's existing structure.

packages/ui/src/components/PieChart/index.ts (1)

1-2: Barrel file export pattern implemented correctly.

This follows the standard barrel file pattern for exporting the component and its props type, consistent with the project's structure.

packages/ui/src/index.ts (1)

45-46: Chart component exports added properly.

The new exports for PieChart and BarChart components have been correctly added to the UI package's main index file, making them available to consumers of the package.

packages/ui/src/components/PieChart/PieChart.stories.tsx (5)

5-18: Storybook meta configuration looks good.

The meta configuration for the PieChart stories follows Storybook best practices with appropriate title, component reference, and layout settings.


24-37: Basic story provides a good starting example.

The Basic story provides a clear example with sample data that demonstrates the component's core functionality.


39-53: DonutChart story shows support for showing totals.

This story effectively demonstrates the component's ability to display total values, which is a common use case for pie/donut charts.


55-68: CustomColors story demonstrates color customization.

The story showcases how individual segments can have custom colors applied, enhancing the chart's visual customization options.


100-100: Export pattern follows Storybook conventions.

The named exports of all story variants follows Storybook's recommended patterns.

packages/ui/src/components/BarChart/BarChart.tsx (1)

73-77: Check if input values are dates consistently

The component has a flag isDateAxis but also has a separate isDate function that checks if values are dates. These should work together consistently.

Consider using the isDateAxis flag to determine how to handle date parsing to avoid confusion:

  const isDate = (value: string) => {
+    if (!isDateAxis) return false;
    const date = new Date(value);
    return !isNaN(date.getTime());
  };

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (7)
packages/ui/src/components/PieChart/PieChart.tsx (4)

64-73: Add explicit typing for the accumulator in the reduce function

The reduce function doesn't have a type for the accumulator, which could lead to TypeScript not properly inferring the return type.

const chartConfig = data.reduce(
-  (acc, item, index) => {
+  (acc: Record<string, { label: string; color: string }>, item, index) => {
    acc[item.name] = {
      label: item.name, 
      color: item.color || DEFAULT_COLORS[index % DEFAULT_COLORS.length],
    };
    return acc;
  },
  {},
);
🧰 Tools
🪛 Biome (1.9.4)

[error] 64-64: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)


58-73: Optimize data processing to avoid iterating over the data twice

Currently, the data is processed twice - once for chartData and once for chartConfig. This could be optimized into a single pass.

- const chartData = data.map((item, index) => ({
-   name: item.name,
-   value: item.value,
-   fill: item.color || DEFAULT_COLORS[index % DEFAULT_COLORS.length],
- }));
- 
- const chartConfig = data.reduce(
-   (acc, item, index) => {
-     acc[item.name] = {
-       label: item.name, 
-       color: item.color || DEFAULT_COLORS[index % DEFAULT_COLORS.length],
-     };
-     return acc;
-   },
-   {},
- );

+ const chartData: { name: string; value: number; fill: string }[] = [];
+ const chartConfig: Record<string, { label: string; color: string }> = {};
+ 
+ data.forEach((item, index) => {
+   const color = item.color || DEFAULT_COLORS[index % DEFAULT_COLORS.length];
+   
+   chartData.push({
+     name: item.name,
+     value: item.value,
+     fill: color,
+   });
+   
+   chartConfig[item.name] = {
+     label: item.name,
+     color: color,
+   };
+ });
🧰 Tools
🪛 Biome (1.9.4)

[error] 64-64: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)


76-120: Enhance accessibility with ARIA attributes

The chart lacks ARIA attributes that would make it more accessible to users with screen readers.

Consider adding:

  1. aria-label to the chart container
  2. role="img" to the chart
  3. aria-describedby to connect the chart with its description

Example implementation:

- <Card className="flex flex-col" style={{ width }}>
+ <Card className="flex flex-col" style={{ width }} aria-label={title || "Pie Chart"}>
  <CardHeader className="items-center pb-0">
    {title && <CardTitle>{title}</CardTitle>}
-   {description && <CardDescription>{description}</CardDescription>}
+   {description && <CardDescription id="chart-description">{description}</CardDescription>}
  </CardHeader>
  <CardContent className="flex-1 pb-0">
-   <div style={{ width: "100%", height: height }}>
+   <div 
+     style={{ width: "100%", height: height }}
+     role="img"
+     aria-describedby={description ? "chart-description" : undefined}
+   >

87-117: Consider providing a way to customize the legend position

The current implementation has a fixed legend position (right side, middle). For better flexibility, consider making the legend position configurable through props.

export interface PieChartProps {
  width?: number;
  height?: number;
  data: {
    name: string;
    value: number;
    color?: string;
  }[];
  title?: string;
  description?: string;
  total?: string;
  totalLabel?: string;
+ legendPosition?: {
+   verticalAlign?: "top" | "middle" | "bottom";
+   align?: "left" | "center" | "right";
+   layout?: "horizontal" | "vertical";
+ };
}

// Then in the component:
export function PieChart({
  width = 500,
  height = 400,
  data,
  title,
  description,
  total,
  totalLabel = "Total",
+ legendPosition = { verticalAlign: "middle", align: "right", layout: "vertical" },
}: PieChartProps) {
  
  // And in the JSX:
  <Legend
-   verticalAlign="middle"
-   align="right"
-   layout="vertical"
+   verticalAlign={legendPosition.verticalAlign}
+   align={legendPosition.align}
+   layout={legendPosition.layout}
    wrapperStyle={{ paddingLeft: "20px" }}
  />
packages/ui/src/components/BarChart/BarChart.tsx (3)

15-34: Ensure data consistency for each series' x and y arrays.

Each series is assumed to have an aligned x and y array of the same length, but this constraint isn't explicitly validated in BarChartProps. For robustness, consider validating or documenting that x and y must always match in length and that all series in data use the same set of x values.


52-58: Check for multi-series alignment.

This logic maps x values from only the first series (data[0].x). While this is valid if all series share the same x array, it could lead to misalignments if future usage allows series with distinct or misaligned x arrays. Consider either enforcing a single x array across all series or merging them safely if partial overlaps are expected.


72-77: Potential time zone handling.

new Date(value) relies on the environment's local time zone. If the dataset contains dates from multiple time zones, the displayed date may differ from the intended source. If needed, consider using a dedicated library for precise date handling or clarifying in docs that local time zone conversion is acceptable.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e360dad and 60c9062.

📒 Files selected for processing (3)
  • packages/ui/src/components/BarChart/BarChart.stories.tsx (1 hunks)
  • packages/ui/src/components/BarChart/BarChart.tsx (1 hunks)
  • packages/ui/src/components/PieChart/PieChart.tsx (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (3)
packages/ui/src/components/BarChart/BarChart.tsx (1)
packages/ui/src/components/BarChart/index.ts (2)
  • BarChartProps (2-2)
  • BarChart (1-1)
packages/ui/src/components/PieChart/PieChart.tsx (1)
packages/ui/src/components/PieChart/index.ts (2)
  • PieChartProps (2-2)
  • PieChart (1-1)
packages/ui/src/components/BarChart/BarChart.stories.tsx (1)
packages/ui/src/components/BarChart/BarChart.tsx (1)
  • BarChart (36-165)
🪛 Biome (1.9.4)
packages/ui/src/components/BarChart/BarChart.tsx

[error] 62-62: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)

packages/ui/src/components/PieChart/PieChart.tsx

[error] 64-64: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)

🔇 Additional comments (16)
packages/ui/src/components/BarChart/BarChart.tsx (5)

1-4: No concerns with module setup.

The "use client" directive and initial imports are straightforward, indicating that this component is intended for client-side rendering in a Next.js app. The setup looks good.


36-50: Props initialization receives a thumbs-up.

Defaulting values for width, height, and barGap/barGroupGap is a good practice, ensuring consistent visuals even if a user omits these props. Everything looks fine here.


60-70: Good switch from spread syntax to direct assignment.

By using direct object assignment in the .reduce() callback, you avoid the O(n^2) performance pitfall flagged by static analysis tools. This is an efficient approach for building the chartConfig.

🧰 Tools
🪛 Biome (1.9.4)

[error] 62-62: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)


78-88: Format logic is straightforward.

formatXAxis appropriately toggles between date formatting and raw values based on isDateAxis. The fallback to returning the string for numeric data is concise and should work as intended.


89-164: Overall chart rendering looks clean.

The structure of the card, grid, axes, tooltip, legend, and bar elements is logically organized. The usage of ChartContainer and ChartTooltipContent is consistent, making for a well-structured chart component. The code is readable and maintainable.

packages/ui/src/components/BarChart/BarChart.stories.tsx (11)

1-4: Basic Storybook setup is clear.

Imports from Storybook and React are concise. Tagging the component for autodocs is standard practice and keeps documentation organized.


5-24: Story metadata structure is well-defined.

Setting up decorators to center the story in an 800px-wide container is a common approach for consistent previews. This metadata ensures a uniform layout for all stories.


27-40: Utility functions for data generation are straightforward.

Both generateTimeSeriesData and generateNumericData are neatly implemented. They produce random data for illustrative purposes in the stories, which is common for Storybook demos.


50-58: Reference sample data is well-chosen.

The dateData array provides a neat example showcasing a single series with simple labeled x-values. Great for a default scenario.


59-88: Props for Default and WithMultipleSeries stories look good.

They cover basic date-based usage, as well as the multi-series scenario. Everything is consistent with the underlying BarChart props.


90-106: WithNumericXAxis exemplifies numeric handling.

Demonstrates how isDateAxis = false can be used with numeric categories. The approach is in line with the BarChart prop design.


108-131: TimeSeries example is well-structured.

It uses generated time series data, properly assigning x and y arrays for daily donations over 30 days. Intervals for labels are a nice addition.


133-169: Use of a single dataset array for multiple series.

You've adopted a single generated dataset (multipleSeriesData) for the MultipleSeries story, ensuring consistent x-values across series. This addresses earlier reviewer feedback on inconsistent data.


171-199: Quarterly categories look correct.

Categories story demonstrates a scenario with textual quarters and different yearly series. It nicely highlights how the chart handles multiple label sets.


201-236: NumericAxis story is improved.

By reusing a single numericData array for both series, you avoid mismatched indices. This aligns well with the best practice of consistent x-values per story.


238-265: NoLegend story effectively shows a legend-free scenario.

Again, good job using a shared dataset (noLegendData) to eliminate potential mismatch. This final story thoroughly demonstrates different chart variations without the legend.

0xKurt and others added 2 commits April 9, 2025 10:48
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (6)
packages/ui/src/components/PieChart/PieChart.tsx (6)

88-89: Consider adding aria attributes for accessibility

The chart doesn't have any accessibility attributes, which could make it difficult for screen reader users to understand the content.

- <RechartsPieChart width={width} height={height}>
+ <RechartsPieChart 
+   width={width} 
+   height={height} 
+   aria-label={title || "Pie Chart"} 
+   role="img">

90-98: Customize chart tooltip for better user experience

The tooltip is using the default configuration, but you could enhance it to show both the value and its percentage of the total.

- <ChartTooltip content={<ChartTooltipContent hideLabel />} />
+ <ChartTooltip 
+   content={
+     <ChartTooltipContent 
+       hideLabel 
+       formatter={(value, name, props) => {
+         const total = chartData.reduce((sum, item) => sum + item.value, 0);
+         const percent = ((value / total) * 100).toFixed(1);
+         return [`${value} (${percent}%)`, name];
+       }}
+     />
+   } 
+ />

32-44: Consider additional configuration options for the pie chart

The interface is solid, but you might want to add options for animation, padding, and legend positioning that users might need.

export interface PieChartProps {
  width?: number;
  height?: number;
  data: {
    name: string;
    value: number;
    color?: string;
  }[];
  title?: string;
  description?: string;
  total?: string;
  totalLabel?: string;
+ legendPosition?: 'right' | 'bottom' | 'left' | 'top';
+ showAnimation?: boolean;
+ showPercentage?: boolean;
}

59-63: Add type safety to the chartData transformation

The current implementation lacks explicit typing for the transform operation, which would improve type safety and code readability.

+ interface ChartDataItem {
+   name: string;
+   value: number;
+   fill: string;
+ }

- const chartData = data.map((item, index) => ({
+ const chartData: ChartDataItem[] = data.map((item, index) => ({
    name: item.name,
    value: item.value,
    fill: item.color || DEFAULT_COLORS[index % DEFAULT_COLORS.length],
  }));

108-109: Consider fallback styles for className not working

The className uses square bracket notation for styling which relies on Tailwind's JIT compiler. Consider adding fallback styles to ensure compatibility across environments.

  className="[&>tspan:nth-child(2)]:text-xl [&>tspan:nth-child(2)]:font-bold"
+ style={{
+   // Fallback styles if the className doesn't apply
+   fontFamily: "sans-serif"
+ }}

111-116: Make legend position configurable through props

Currently, the legend position is hard-coded. Consider making it configurable through component props to increase flexibility.

// In the props interface
+ legendPosition?: 'right' | 'bottom' | 'left' | 'top';
+ legendAlign?: 'center' | 'left' | 'right';

// In the component props
+ legendPosition = 'right',
+ legendAlign = 'center',

// In the Legend component
<Legend
-  verticalAlign="middle"
-  align="right"
-  layout="vertical"
+  verticalAlign={legendPosition === 'top' || legendPosition === 'bottom' ? legendPosition : 'middle'}
+  align={legendPosition === 'left' || legendPosition === 'right' ? legendPosition : legendAlign}
+  layout={legendPosition === 'left' || legendPosition === 'right' ? 'vertical' : 'horizontal'}
   wrapperStyle={{ paddingLeft: "20px" }}
/>
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 60c9062 and 1845429.

📒 Files selected for processing (1)
  • packages/ui/src/components/PieChart/PieChart.tsx (1 hunks)
🔇 Additional comments (3)
packages/ui/src/components/PieChart/PieChart.tsx (3)

55-58: Good fix on returning null for empty data!

Changing the return value from an empty array to null for the empty data case is the correct approach. Empty arrays are not valid React nodes and can cause rendering issues.


65-74: Well implemented chartConfig without spread operator!

The implementation avoids using the spread operator in the accumulator, which prevents O(n²) time complexity for large datasets. This is more performant than spreading objects in the reducer.


83-83: Good implementation of responsive container!

Adding maxWidth: "100%" and overflow: "auto" improves the chart's responsiveness for different screen sizes.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (4)
packages/ui/src/components/BarChart/BarChart.stories.tsx (4)

114-117: 🛠️ Refactor suggestion

Use consistent data for the TimeSeries example.

Generating separate random data for x and y values can lead to inconsistent data points which could confuse users of the component.

+ const timeSeriesData = generateTimeSeriesData(30);
  {
    name: "Donations",
-   x: generateTimeSeriesData(30).map((d) => d.x),
-   y: generateTimeSeriesData(30).map((d) => d.y),
+   x: timeSeriesData.map((d) => d.x),
+   y: timeSeriesData.map((d) => d.y),
    color: "#25BDCE",
  },
🧰 Tools
🪛 Biome (1.9.4)

[error] 114-115: expected : but instead found timeSeriesData

Remove timeSeriesData

(parse)


[error] 115-115: expected , but instead found ;

Remove ;

(parse)


[error] 116-116: expected , but instead found (

Remove (

(parse)


[error] 117-117: expected , but instead found :

Remove :

(parse)


137-145: 🛠️ Refactor suggestion

Use consistent data for the MultipleSeries example.

Similar to the TimeSeries story, the MultipleSeries story also generates separate random data which can lead to inconsistencies between series.

+ const multipleSeriesData = generateTimeSeriesData(30);
  {
    name: "Donations",
-   x: generateTimeSeriesData(30).map((d) => d.x),
-   y: generateTimeSeriesData(30).map((d) => d.y),
+   x: multipleSeriesData.map((d) => d.x),
+   y: multipleSeriesData.map((d) => d.y),
    color: "#25BDCE",
  },
  {
    name: "Matching Funds",
-   x: generateTimeSeriesData(30).map((d) => d.x),
-   y: generateTimeSeriesData(30).map((d) => d.y * 0.5),
+   x: multipleSeriesData.map((d) => d.x),
+   y: multipleSeriesData.map((d) => d.y * 0.5),
    color: "#FF7043",
  },
🧰 Tools
🪛 Biome (1.9.4)

[error] 138-138: Expected a property, a shorthand property, a getter, a setter, or a method but instead found '+'.

Expected a property, a shorthand property, a getter, a setter, or a method here.

(parse)


[error] 138-138: Expected an expression but instead found 'const'.

Expected an expression here.

(parse)


[error] 139-139: expected , but instead found (

Remove (

(parse)


[error] 139-139: expected , but instead found ;

Remove ;

(parse)


[error] 142-142: expected , but instead found (

Remove (

(parse)


[error] 143-143: expected , but instead found :

Remove :

(parse)


[error] 143-143: expected , but instead found (

Remove (

(parse)


[error] 144-144: expected , but instead found :

Remove :

(parse)


[error] 144-144: expected , but instead found (

Remove (

(parse)


[error] 144-144: expected , but instead found :

Remove :

(parse)


[error] 145-145: expected , but instead found (

Remove (

(parse)


197-205: 🛠️ Refactor suggestion

Use consistent data for the NumericAxis example.

The NumericAxis story generates separate random data for both series, which could lead to inconsistent x-axis values.

+ const numericData = generateNumericData(10);
  {
    name: "Series A",
-   x: generateNumericData(10).map((d) => d.x),
-   y: generateNumericData(10).map((d) => d.y),
+   x: numericData.map((d) => d.x),
+   y: numericData.map((d) => d.y),
    color: "#25BDCE",
  },
  {
    name: "Series B",
-   x: generateNumericData(10).map((d) => d.x),
-   y: generateNumericData(10).map((d) => d.y * 0.7),
+   x: numericData.map((d) => d.x),
+   y: numericData.map((d) => d.y * 0.7), // Creating variation between series
    color: "#FF7043",
  },
🧰 Tools
🪛 Biome (1.9.4)

[error] 204-204: Expected a property, a shorthand property, a getter, a setter, or a method but instead found '+'.

Expected a property, a shorthand property, a getter, a setter, or a method here.

(parse)


[error] 204-204: Expected an expression but instead found 'const'.

Expected an expression here.

(parse)


227-229: 🛠️ Refactor suggestion

Use consistent data for the NoLegend example.

The NoLegend story also generates separate random data for x and y values, which could lead to inconsistencies.

+ const noLegendData = generateTimeSeriesData(30);
  {
    name: "Donations",
-   x: generateTimeSeriesData(30).map((d) => d.x),
-   y: generateTimeSeriesData(30).map((d) => d.y),
+   x: noLegendData.map((d) => d.x),
+   y: noLegendData.map((d) => d.y),
    color: "#25BDCE",
  },
🧰 Tools
🪛 Biome (1.9.4)

[error] 228-228: expected , but instead found :

Remove :

(parse)

🧹 Nitpick comments (3)
packages/ui/src/components/PieChart/PieChart.tsx (2)

89-109: Consider adding fallback for empty values in pie segments.

The Pie component doesn't handle zero-value segments differently. For improved UX, consider filtering out zero values or implementing a special case visualization.

<Pie
  data={chartData}
+ data={chartData.filter(item => item.value > 0)}
  dataKey="value"
  nameKey="name"
  label
  outerRadius={total ? 120 : 140}
  innerRadius={total ? 60 : 0}
  paddingAngle={total ? 2 : 0}
>

110-115: Consider making legend position configurable.

The Legend component has fixed positioning with hardcoded alignment values. Consider making this configurable through props for more flexible use cases.

<Legend
- verticalAlign="middle"
- align="right"
- layout="vertical"
+ verticalAlign={legendPosition?.vertical || "middle"}
+ align={legendPosition?.horizontal || "right"}
+ layout={legendPosition?.layout || "vertical"}
  wrapperStyle={{ paddingLeft: "20px" }}
/>

This would require adding new props to the PieChartProps interface:

legendPosition?: {
  vertical?: "top" | "middle" | "bottom";
  horizontal?: "left" | "center" | "right";
  layout?: "horizontal" | "vertical";
};
packages/ui/src/components/BarChart/BarChart.stories.tsx (1)

28-40: Consider adding a seed parameter to generateTimeSeriesData for consistent testing.

Adding an optional seed parameter to the random data generation would allow for reproducible examples and consistent testing.

- function generateTimeSeriesData(days = 30) {
+ function generateTimeSeriesData(days = 30, seed = Math.random()) {
    const data = [];
    const now = new Date();
+   const random = (min: number, max: number) => {
+     const x = Math.sin(seed++) * 10000;
+     return Math.floor((x - Math.floor(x)) * (max - min + 1)) + min;
+   };
    for (let i = days - 1; i >= 0; i--) {
      const date = new Date(now);
      date.setDate(date.getDate() - i);
      data.push({
        x: date.toISOString().split("T")[0],
-       y: Math.floor(Math.random() * 1000) + 500,
+       y: random(500, 1500),
      });
    }
    return data;
  }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1845429 and 0d16e74.

📒 Files selected for processing (2)
  • packages/ui/src/components/BarChart/BarChart.stories.tsx (1 hunks)
  • packages/ui/src/components/PieChart/PieChart.tsx (1 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
packages/ui/src/components/PieChart/PieChart.tsx

[error] 56-56: Expected a semicolon or an implicit semicolon after a statement, but found none

An explicit or implicit semicolon is expected here...

...Which is required to end this statement

(parse)


[error] 57-58: Expected a semicolon or an implicit semicolon after a statement, but found none

An explicit or implicit semicolon is expected here...

...Which is required to end this statement

(parse)


[error] 56-58: This code is unreachable

... because this statement will return from the function beforehand

(lint/correctness/noUnreachable)

packages/ui/src/components/BarChart/BarChart.stories.tsx

[error] 114-115: expected : but instead found timeSeriesData

Remove timeSeriesData

(parse)


[error] 115-115: expected , but instead found ;

Remove ;

(parse)


[error] 116-116: expected , but instead found (

Remove (

(parse)


[error] 117-117: expected , but instead found :

Remove :

(parse)


[error] 119-119: expected , but instead found (

Remove (

(parse)


[error] 120-120: expected , but instead found :

Remove :

(parse)


[error] 138-138: Expected a property, a shorthand property, a getter, a setter, or a method but instead found '+'.

Expected a property, a shorthand property, a getter, a setter, or a method here.

(parse)


[error] 138-138: Expected an expression but instead found 'const'.

Expected an expression here.

(parse)


[error] 139-139: expected , but instead found (

Remove (

(parse)


[error] 139-139: expected , but instead found ;

Remove ;

(parse)


[error] 142-142: expected , but instead found (

Remove (

(parse)


[error] 143-143: expected , but instead found :

Remove :

(parse)


[error] 143-143: expected , but instead found (

Remove (

(parse)


[error] 144-144: expected , but instead found :

Remove :

(parse)


[error] 144-144: expected , but instead found (

Remove (

(parse)


[error] 144-144: expected , but instead found :

Remove :

(parse)


[error] 145-145: expected , but instead found (

Remove (

(parse)


[error] 148-148: expected , but instead found :

Remove :

(parse)


[error] 149-149: expected , but instead found (

Remove (

(parse)


[error] 149-149: expected , but instead found :

Remove :

(parse)


[error] 150-150: expected , but instead found (

Remove (

(parse)


[error] 151-151: expected , but instead found :

Remove :

(parse)


[error] 152-152: expected , but instead found (

Remove (

(parse)


[error] 153-153: expected , but instead found :

Remove :

(parse)


[error] 153-153: expected , but instead found (

Remove (

(parse)


[error] 155-155: expected , but instead found :

Remove :

(parse)


[error] 204-204: Expected a property, a shorthand property, a getter, a setter, or a method but instead found '+'.

Expected a property, a shorthand property, a getter, a setter, or a method here.

(parse)


[error] 204-204: Expected an expression but instead found 'const'.

Expected an expression here.

(parse)


[error] 206-206: expected , but instead found (

Remove (

(parse)


[error] 206-206: expected , but instead found ;

Remove ;

(parse)


[error] 209-209: expected , but instead found (

Remove (

(parse)


[error] 209-209: expected , but instead found :

Remove :

(parse)


[error] 209-209: expected , but instead found (

Remove (

(parse)


[error] 210-210: expected , but instead found :

Remove :

(parse)


[error] 211-211: expected , but instead found (

Remove (

(parse)


[error] 212-212: expected , but instead found :

Remove :

(parse)


[error] 212-212: expected , but instead found (

Remove (

(parse)


[error] 213-213: expected , but instead found :

Remove :

(parse)


[error] 216-217: expected , but instead found (

Remove (

(parse)


[error] 220-220: expected , but instead found :

Remove :

(parse)


[error] 221-221: expected , but instead found (

Remove (

(parse)


[error] 223-223: expected , but instead found :

Remove :

(parse)


[error] 224-224: expected , but instead found (

Remove (

(parse)


[error] 226-226: expected , but instead found :

Remove :

(parse)


[error] 226-226: expected , but instead found (

Remove (

(parse)


[error] 228-228: expected , but instead found :

Remove :

(parse)

🔇 Additional comments (3)
packages/ui/src/components/PieChart/PieChart.tsx (3)

55-57: Good implementation of proper error handling for empty data.

The early return for null or empty data is properly implemented, ensuring the component gracefully handles cases where no data is available for visualization.

🧰 Tools
🪛 Biome (1.9.4)

[error] 56-56: Expected a semicolon or an implicit semicolon after a statement, but found none

An explicit or implicit semicolon is expected here...

...Which is required to end this statement

(parse)


64-73: Good optimization using object assignment instead of spread.

The chartConfig creation properly implements the recommended performance optimization by using direct object assignment instead of spread syntax in the reduce method's accumulator, avoiding O(n²) time complexity for large datasets.


82-82: Good implementation of responsive container.

The responsive container with maxWidth: "100%" and overflow: "auto" ensures the chart adapts well to different screen sizes.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (4)
packages/ui/src/components/BarChart/BarChart.tsx (4)

36-45: Unused height prop.
The function signature accepts height but never applies it. Either remove the prop or apply it (e.g., in the chart container style).

-export function BarChart({
-  width = 800,
-  data,
-  title,
-  description,
-  xAxisTitle,
-  yAxisTitle,
-  showLegend = true,
-  isDateAxis = false,
-}: BarChartProps) {
+export function BarChart({
+  width = 800,
+  height = 400,
+  data,
+  title,
+  description,
+  xAxisTitle,
+  yAxisTitle,
+  showLegend = true,
+  isDateAxis = false,
+}: BarChartProps) {
+  ...
+  <Card className="flex flex-col" style={{ width, height }}>
+    ...
+  </Card>
 }

58-65: Remove unused index variable in the reducer.
The index parameter is unused. For clarity, consider removing it.

-const chartConfig = data.reduce<ChartConfig>((acc, series, index) => {
+const chartConfig = data.reduce<ChartConfig>((acc, series) => {
   acc[series.name || "value"] = {
     label: series.name || "Value",
     color: series.color || "#25BDCE",
   };
   return acc;
 }, {});

67-71: Consider removing or fully enabling isDate.
Currently, isDate is only invoked in commented-out code. Clear up whether it’s needed or can be removed.


147-154: Prevent naming collisions for series.
Multiple series without a name prop default to "value", potentially overwriting each other’s data keys. Consider generating unique placeholders.

- dataKey={series.name || "value"}
+ dataKey={series.name || `value-${index}`}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0d16e74 and 31a24eb.

📒 Files selected for processing (1)
  • packages/ui/src/components/BarChart/BarChart.tsx (1 hunks)
🔇 Additional comments (6)
packages/ui/src/components/BarChart/BarChart.tsx (6)

1-2: "use client" block is appropriate for Next.js.
No issues with these lines.


5-6: All imported Recharts components appear to be in use.
No concerns regarding these imports.


15-34: Confirm array length consistency and prop usage.
The interface uses parallel arrays for x and y, with no guarantees they match in length. This can create undefined behaviors at runtime. Also note that height, barGap, barGroupGap, xAxisLabelInterval, and yAxisLabelInterval are declared but not visibly applied within the component.


47-56: Verify possible index out-of-bounds.
Using data[0].x.map(...) may lead to undefined references if subsequent series have fewer y values than the first series has x entries.


73-82: Simple and effective x-axis formatter.
This date vs. string approach is concise and clear.


84-158: Unused props: barGap, barGroupGap, xAxisLabelInterval, yAxisLabelInterval.
These props appear in the interface but aren’t applied in <RechartsBarChart> or related elements. Confirm if this is intended or implement them for flexible chart spacing and label intervals.

Copy link
Member

@nijoe1 nijoe1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@0xKurt 0xKurt merged commit 7357c15 into main Apr 9, 2025
4 checks passed
@0xKurt 0xKurt deleted the metric-updates branch April 9, 2025 12:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants