Skip to content

Conversation

@visz11
Copy link

@visz11 visz11 commented Nov 10, 2025

CodeAnt-AI Description

Add interactive chart legend with toggle, isolate, and reset

What Changed

  • Legend entries can be clicked to toggle each metric on or off; disabled metrics appear greyed out and struck through.
  • Double‑clicking a legend entry isolates that single metric in the chart; a visible "Reset" link restores all metrics when any are hidden.
  • Hovering a visible legend entry highlights its metric in the chart; the first toggle click shows a brief tooltip explaining the double‑click isolation.

Impact

✅ Easier metric toggling in charts
✅ Faster focus on a single metric
✅ Simple restore of all metrics via Reset

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

Summary by CodeRabbit

  • New Features
    • Enhanced legend with interactive controls for item visibility toggling
    • Isolate specific metrics using double-click functionality
    • Reset all items to their default active state with one click
    • Color-coded indicators and visual styling reflect item active/inactive status
    • Contextual tooltip guidance for first-time users

@codeant-ai
Copy link

codeant-ai bot commented Nov 10, 2025

CodeAnt AI is reviewing your PR.


Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

@gemini-code-assist
Copy link

Summary of Changes

Hello @visz11, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request enhances data visualization capabilities by adding a new CustomLegend component. This component provides users with interactive controls to manage the visibility and focus of different data series within a chart, significantly improving the user experience for exploring complex datasets.

Highlights

  • New CustomLegend Component: Introduced a new CustomLegend component to provide interactive legend functionality for data visualizations.
  • Interactive Functionality: The legend allows users to click on an entry to toggle its visibility, double-click to isolate a single metric, and hover to highlight. It also includes a 'Reset' option to show all metrics.
  • Chart Type Support: The component supports various chart types, including line, dashed line, bar, and dashed bar charts, rendering appropriate visual indicators for each.
  • Tooltip Guidance: A tooltip is displayed on the first interaction to guide users on how to isolate metrics using a double-click.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@coderabbitai
Copy link

coderabbitai bot commented Nov 10, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

A new CustomLegend React component is added to index.tsx that renders an interactive legend with items, a reset button, and support for toggling item visibility, isolating metrics via double-click, and displaying a one-time tooltip hint.

Changes

Cohort / File(s) Summary
New Legend Component
index.tsx
Introduces CustomLegend component with interactive legend items, reset functionality, double-click isolation, click-to-toggle behavior, hover state management, and conditional tooltip display.

Sequence Diagram

sequenceDiagram
    participant User
    participant CustomLegend as CustomLegend<br/>Component
    participant State as Internal State<br/>(hover, showTooltip)
    participant Parent as Parent<br/>(setBarProps)

    User->>CustomLegend: Hover over item
    CustomLegend->>State: Update hover state
    CustomLegend-->>User: Highlight item visually

    User->>CustomLegend: Click item (first time)
    CustomLegend->>State: Show tooltip
    CustomLegend->>Parent: setBarProps (toggle active)
    CustomLegend-->>User: Display tooltip hint
    CustomLegend->>State: Auto-hide tooltip (1.5s)

    User->>CustomLegend: Double-click item
    CustomLegend->>Parent: setBarProps (isolate metric)
    CustomLegend-->>User: Only clicked item active

    User->>CustomLegend: Click Reset button
    CustomLegend->>Parent: setBarProps (all active)
    CustomLegend-->>User: All items restored
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

  • Single file addition with self-contained component logic
  • Multiple interaction patterns (click, double-click, hover, tooltip) are straightforward state transitions
  • Conditional rendering based on active states is standard React patterns
  • No cross-file dependencies or complex control flow

Poem

🐰 A legend springs forth, colorful and bright,
With clicks and double-taps to set items right,
Reset with a bounce, hover with grace,
Tooltips that vanish without a trace! ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely summarizes the main change: introducing a new CustomLegend component for interactive legend functionality.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch Add-CustomLegend-component-for-interactive-legend

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@visz11
Copy link
Author

visz11 commented Nov 10, 2025

/refacto-visz

@refacto-visz
Copy link

refacto-visz bot commented Nov 10, 2025

Refacto is reviewing this PR. Please wait for the review comments to be posted.

@codeant-ai codeant-ai bot added the size:L This PR changes 100-499 lines, ignoring generated files label Nov 10, 2025
@@ -0,0 +1,161 @@
import { Box, Flex, Text, Tooltip } from '@chakra-ui/react';
import { useEffect, useState } from 'react';
Copy link

Choose a reason for hiding this comment

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

Suggestion: Remove the unused useEffect import from React to prevent linter warnings and keep imports minimal. [maintainability]

Severity Level: Minor ⚠️

Suggested change
import { useEffect, useState } from 'react';
import { useState } from 'react';
Why it matters? ⭐

The file index.tsx imports useEffect but there is no usage of useEffect anywhere in the file. I inspected the current file (shown in the PR) and confirmed only useState is referenced — removing useEffect will eliminate a linter warning and is a safe, minimal cleanup.

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** index.tsx
**Line:** 2:2
**Comment:**
	*Maintainability: Remove the unused `useEffect` import from React to prevent linter warnings and keep imports minimal.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.

@codeant-ai
Copy link

codeant-ai bot commented Nov 10, 2025

CodeAnt AI finished reviewing your PR.

@refacto-visz
Copy link

refacto-visz bot commented Nov 10, 2025

Code Review: CustomLegend Component - Security, Reliability & Type Safety

👍 Well Done
Interactive State Management

Well-structured state management with proper hover and selection handling for legend interactions

Clean Event Handling

Clear mouse event patterns with appropriate state synchronization for user interactions

📁 Selected files for review (1)
  • index.tsx
🎯 Custom Instructions
✅ Applied Instructions
Organization Guidelines
  • Keep pull requests small and focused (prefer < 400 lines of code).
  • All CI/CD checks, linting, and unit tests must pass before merge.
  • Use feature flags for new functionality and include a clear rollback plan.
  • Follow the company security checklist:
    • No hard-coded secrets or credentials.
    • Validate all external inputs.
    • Use parameterized queries for DB access.

Scope: All files

📝 Additional Comments
index.tsx (3)
Complex Event Handler

Click handler performs multiple unrelated operations: tooltip management and chart state updates. This creates complex logic that's difficult to test and modify independently requiring extraction of separate functions.

Standards:

  • SOLID-SRP
  • Clean-Code-Functions
  • Refactoring-Extract-Method
Duplicate Array Reduction

Identical reduce pattern duplicated in onDoubleClick handler creates O(n) complexity per double-click event. Code duplication increases bundle size and maintenance overhead requiring extraction of common reduction logic.

Standards:

  • ISO-IEC-25010-Performance-Efficiency-Time-Behaviour
  • Optimization-Pattern-Function-Extraction
  • Code-Reuse-Performance
State Manipulation Risk

Dynamic property access using unvalidated entry.key enables object pollution attacks through prototype manipulation. Attackers could inject keys like 'proto' or 'constructor' to modify object prototypes requiring validation against allowlist.

Standards:

  • CWE-1321
  • OWASP-A03
  • NIST-SSDF-PW.1

Comment on lines +83 to +85
setTimeout(() => {
setShowTooltip(false);
}, 1500);
Copy link

Choose a reason for hiding this comment

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

Memory Leak Risk

setTimeout creates timer without cleanup mechanism in component lifecycle. Component unmounting before timeout completion leaves dangling timer reference causing memory leaks and accumulated timers degrade application performance.

              const timeoutId = setTimeout(() => {
                setShowTooltip(false);
              }, 1500);
              return () => clearTimeout(timeoutId);
Commitable Suggestion
Suggested change
setTimeout(() => {
setShowTooltip(false);
}, 1500);
const timeoutId = setTimeout(() => {
setShowTooltip(false);
}, 1500);
return () => clearTimeout(timeoutId);
Standards
  • ISO-IEC-25010-Reliability-Fault-Tolerance
  • ISO-IEC-25010-Functional-Correctness-Appropriateness

color={barProps[entry?.key] ? entry?.color : 'grey'}
decoration={barProps[entry?.key] ? 'auto' : 'line-through'}
>
{entry?.name}
Copy link

Choose a reason for hiding this comment

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

DOM-based XSS Risk

User-controlled entry name data rendered directly in JSX without sanitization enables DOM-based XSS attacks. If entry.name contains malicious script content from external sources, it will execute in user browsers.

            <Text
              _hover={
                barProps[entry?.key]
                  ? { color: entry?.color, fontWeight: 600 }
                  : { color: 'grey' }
              }
              color={barProps[entry?.key] ? entry?.color : 'grey'}
              decoration={barProps[entry?.key] ? 'auto' : 'line-through'}
            >
              {entry?.name ? String(entry.name).replace(/<[^>]*>/g, '') : ''}
            </Text>
Commitable Suggestion
Suggested change
{entry?.name}
<Text
_hover={
barProps[entry?.key]
? { color: entry?.color, fontWeight: 600 }
: { color: 'grey' }
}
color={barProps[entry?.key] ? entry?.color : 'grey'}
decoration={barProps[entry?.key] ? 'auto' : 'line-through'}
>
{entry?.name ? String(entry.name).replace(/<[^>]*>/g, '') : ''}
</Text>
Standards
  • CWE-79
  • OWASP-A03
  • NIST-SSDF-PW.1

Comment on lines +4 to +10
export default function CustomLegend({
barProps,
setBarProps,
keys,
w,
...props
}: any) {
Copy link

Choose a reason for hiding this comment

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

Missing Type Safety

Using 'any' type eliminates TypeScript benefits and reduces maintainability. Props lack proper interface definition making component contract unclear and future modifications risk runtime errors due to missing compile-time validation.

Standards
  • Clean-Code-Meaningful-Names
  • Maintainability-Quality-Analyzability

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a new CustomLegend component for interactive chart legends. The implementation is functional, but there are several areas for improvement, primarily around TypeScript usage and code clarity. The component props and many internal variables are typed as any, which should be replaced with specific types to leverage TypeScript's benefits. I've also suggested refactoring some logic to be more concise and readable, such as using Object.fromEntries instead of reduce, and removing magic numbers and strings by defining constants. Finally, there are some minor cleanups like managing imports.

Comment on lines +4 to +10
export default function CustomLegend({
barProps,
setBarProps,
keys,
w,
...props
}: any) {

Choose a reason for hiding this comment

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

high

The component props are typed as any, which undermines the benefits of TypeScript. You should define specific types for the props to improve type safety and code clarity. Additionally, ...props are unused and should be removed, and a default value can be set for w.

First, define these interfaces and constants at the top of your file (or in a separate types file):

const DEFAULT_LEGEND_WIDTH = 220;

interface LegendKey {
  key: string;
  name: string;
  color: string;
  chartType: string;
}

interface BarProps {
  [key: string]: boolean | string | null;
  hover: string | null;
}

interface CustomLegendProps {
  barProps: BarProps;
  setBarProps: Dispatch<SetStateAction<BarProps>>;
  keys: LegendKey[];
  w?: number;
}

Then, apply the type to your component props. You should also replace all other occurrences of any with the appropriate specific types (e.g., in reduce callbacks and useState setters).

Suggested change
export default function CustomLegend({
barProps,
setBarProps,
keys,
w,
...props
}: any) {
export default function CustomLegend({
barProps,
setBarProps,
keys,
w = DEFAULT_LEGEND_WIDTH,
}: CustomLegendProps) {

@@ -0,0 +1,161 @@
import { Box, Flex, Text, Tooltip } from '@chakra-ui/react';
import { useEffect, useState } from 'react';

Choose a reason for hiding this comment

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

medium

The useEffect hook is imported but not used and should be removed. To properly type this component's props as suggested in another comment, you'll need Dispatch and SetStateAction from React, so they should be imported instead.

Suggested change
import { useEffect, useState } from 'react';
import { Dispatch, SetStateAction, useState } from 'react';

Comment on lines +16 to +26
setBarProps(
keys?.reduce(
(a: any, { key }: any) => {
a[key] = true;
return a;
},
{
hover: null,
}
)
);

Choose a reason for hiding this comment

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

medium

Using reduce to build this object is a bit verbose. You can achieve the same result more concisely and readably with Object.fromEntries.

    setBarProps({
      ...Object.fromEntries(keys.map(({ key }) => [key, true])),
      hover: null,
    });

};

return (
<Box flexDirection={'column'} padding={'0 20px'} w={w ? w : 220}>

Choose a reason for hiding this comment

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

medium

Since a default value for w is now provided in the component signature, you can simplify this to just w={w}.

    <Box flexDirection={'column'} padding={'0 20px'} w={w}>

Comment on lines +57 to +59
const isLine = ['line_chart', 'dashed_line_chart'].includes(
entry?.chartType
);

Choose a reason for hiding this comment

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

medium

The strings 'line_chart' and 'dashed_line_chart' are magic strings. It's better to define them as constants or as part of an enum. This prevents typos and makes the code easier to maintain.

For example, you could define:

const CHART_TYPE = {
  LINE: 'line_chart',
  DASHED_LINE: 'dashed_line_chart',
  // ... other types
}

And then use [CHART_TYPE.LINE, CHART_TYPE.DASHED_LINE].includes(entry.chartType).

Comment on lines +67 to +77
setBarProps(
keys?.reduce(
(a: any, { key }: any) => {
a[key] = entry?.key === key ? true : false;
return a;
},
{
hover: null,
}
)
);

Choose a reason for hiding this comment

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

medium

Using reduce here to build the new props object is verbose and can be harder to read. Consider refactoring for clarity using Object.fromEntries, which is more declarative for this use case.

              setBarProps({
                ...Object.fromEntries(
                  keys.map(({ key }) => [key, entry.key === key])
                ),
                hover: null,
              });

setShowTooltip(true);
setTimeout(() => {
setShowTooltip(false);
}, 1500);

Choose a reason for hiding this comment

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

medium

The timeout duration 1500 is a magic number. It's better to define it as a named constant at the top of the component. This improves readability and makes it easier to change the value if needed.

Example:

const TOOLTIP_DISPLAY_DURATION = 1500;
                }, TOOLTIP_DISPLAY_DURATION);

Comment on lines +80 to +86
if (!hasShownTooltip) {
setHasShownTooltip(true);
setShowTooltip(true);
setTimeout(() => {
setShowTooltip(false);
}, 1500);
}
Copy link

Choose a reason for hiding this comment

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

Tooltip State Race

Tooltip logic creates potential race condition where rapid clicks can trigger multiple setTimeout callbacks. State updates are not synchronized, allowing overlapping timeout executions that cause inconsistent tooltip visibility state.

Standards
  • Algorithm-Correctness-State-Management
  • Business-Rule-UI-Consistency
  • Logic-Verification-Race-Conditions

Comment on lines +16 to +26
setBarProps(
keys?.reduce(
(a: any, { key }: any) => {
a[key] = true;
return a;
},
{
hover: null,
}
)
);
Copy link

Choose a reason for hiding this comment

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

Inefficient Array Reduction

Array.reduce() creates new object on every resetSelected call with O(n) complexity. For frequent legend resets with large key arrays, this causes unnecessary object allocation and GC pressure affecting performance.

Standards
  • ISO-IEC-25010-Performance-Efficiency-Resource-Utilization
  • Optimization-Pattern-Memoization
  • Algorithmic-Complexity-Linear-Optimization

Copy link

@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: 5

🧹 Nitpick comments (2)
index.tsx (2)

15-27: Extract duplicate barProps building logic.

The logic for constructing barProps using reduce is duplicated in both resetSelected and the onDoubleClick handler. Extracting this to a helper function would improve maintainability.

+const buildBarProps = (keys: any[], activePredicate: (key: string) => boolean) => {
+  return keys?.reduce(
+    (a: any, { key }: any) => {
+      a[key] = activePredicate(key);
+      return a;
+    },
+    { hover: null }
+  );
+};
+
 const resetSelected = () => {
-  setBarProps(
-    keys?.reduce(
-      (a: any, { key }: any) => {
-        a[key] = true;
-        return a;
-      },
-      {
-        hover: null,
-      }
-    )
-  );
+  setBarProps(buildBarProps(keys, () => true));
 };

 ...

 onDoubleClick={() => {
-  setBarProps(
-    keys?.reduce(
-      (a: any, { key }: any) => {
-        a[key] = entry?.key === key ? true : false;
-        return a;
-      },
-      {
-        hover: null,
-      }
-    )
-  );
+  setBarProps(buildBarProps(keys, (key) => entry?.key === key));
 }}

Also applies to: 66-78


44-50: Redundant visibility check in onClick handler.

The early return when !showReset (lines 45-47) is redundant since the element's visibility is already set to 'hidden' when showReset is false.

Consider using pointerEvents for a cleaner approach:

 <Text
   fontWeight={600}
   fontSize={'xs'}
   textDecor={'underline'}
   w={'fit-content'}
   mb={1}
   onClick={() => {
-    if (!showReset) {
-      return;
-    }
     resetSelected();
   }}
   visibility={showReset ? 'visible' : 'hidden'}
+  pointerEvents={showReset ? 'auto' : 'none'}
   cursor={'pointer'}
 >
   Reset
 </Text>
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e184110 and 4a2eb1d.

📒 Files selected for processing (1)
  • index.tsx (1 hunks)

Comment on lines +4 to +10
export default function CustomLegend({
barProps,
setBarProps,
keys,
w,
...props
}: any) {
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Define proper TypeScript types instead of using any.

The component signature uses any for all parameters, which disables TypeScript's type checking and eliminates the benefits of static typing. This can lead to runtime errors and makes the component's contract unclear.

Define proper interfaces:

+interface LegendKey {
+  key: string;
+  name: string;
+  color: string;
+  chartType: 'line_chart' | 'dashed_line_chart' | 'bar_chart' | 'dashed_bar_chart';
+}
+
+interface BarProps {
+  [key: string]: boolean;
+  hover: string | null;
+}
+
+interface CustomLegendProps {
+  barProps: BarProps;
+  setBarProps: React.Dispatch<React.SetStateAction<BarProps>>;
+  keys: LegendKey[];
+  w?: number;
+  [key: string]: any;
+}
+
 export default function CustomLegend({
   barProps,
   setBarProps,
   keys,
   w,
   ...props
-}: any) {
+}: CustomLegendProps) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export default function CustomLegend({
barProps,
setBarProps,
keys,
w,
...props
}: any) {
interface LegendKey {
key: string;
name: string;
color: string;
chartType: 'line_chart' | 'dashed_line_chart' | 'bar_chart' | 'dashed_bar_chart';
}
interface BarProps {
[key: string]: boolean;
hover: string | null;
}
interface CustomLegendProps {
barProps: BarProps;
setBarProps: React.Dispatch<React.SetStateAction<BarProps>>;
keys: LegendKey[];
w?: number;
[key: string]: any;
}
export default function CustomLegend({
barProps,
setBarProps,
keys,
w,
...props
}: CustomLegendProps) {
🤖 Prompt for AI Agents
In index.tsx around lines 4 to 10, the component currently types all params as
`any`; replace that with a proper typed props interface (e.g. export an
interface CustomLegendProps) and use it as the function parameter type. Define
barProps with a concrete shape (or a Record<string, number|string> if dynamic),
type setBarProps as React.Dispatch<React.SetStateAction<ThatBarPropsType>>, type
keys as string[] (or string[]|number[] if needed), type w as number, and type
the rest props as React.HTMLAttributes<HTMLElement> or a more specific prop
type; update the function signature to use CustomLegendProps instead of `any`
and import React types if necessary.

Comment on lines +38 to +54
<Text
fontWeight={600}
fontSize={'xs'}
textDecor={'underline'}
w={'fit-content'}
mb={1}
onClick={() => {
if (!showReset) {
return;
}
resetSelected();
}}
visibility={showReset ? 'visible' : 'hidden'}
cursor={'pointer'}
>
Reset
</Text>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Add keyboard accessibility to interactive elements.

The Reset button and legend items are not keyboard-accessible. Users who rely on keyboard navigation cannot interact with the legend, which is an accessibility violation.

Add keyboard support:

 <Text
   fontWeight={600}
   fontSize={'xs'}
   textDecor={'underline'}
   w={'fit-content'}
   mb={1}
+  tabIndex={showReset ? 0 : -1}
+  role="button"
+  aria-label="Reset legend selection"
   onClick={() => {
     if (!showReset) {
       return;
     }
     resetSelected();
   }}
+  onKeyDown={(e) => {
+    if ((e.key === 'Enter' || e.key === ' ') && showReset) {
+      e.preventDefault();
+      resetSelected();
+    }
+  }}
   visibility={showReset ? 'visible' : 'hidden'}
   cursor={'pointer'}
 >
   Reset
 </Text>

 ...

 <Flex
   key={entry?.key}
   align={'center'}
   w={'fit-content'}
   cursor={'pointer'}
+  tabIndex={0}
+  role="button"
+  aria-label={`Toggle ${entry?.name} visibility`}
+  aria-pressed={barProps[entry?.key]}
   onDoubleClick={() => { ... }}
   onClick={() => { ... }}
+  onKeyDown={(e) => {
+    if (e.key === 'Enter' || e.key === ' ') {
+      e.preventDefault();
+      // Trigger click behavior
+      if (!hasShownTooltip) {
+        setHasShownTooltip(true);
+        setShowTooltip(true);
+        setTimeout(() => setShowTooltip(false), 1500);
+      }
+      setBarProps((barProps: any) => ({
+        ...barProps,
+        [entry?.key]: !barProps[entry?.key],
+        hover: null,
+      }));
+    }
+  }}
   onMouseEnter={() => { ... }}
   onMouseLeave={() => { ... }}
 >

Also applies to: 61-108

Comment on lines +66 to +95
onDoubleClick={() => {
setBarProps(
keys?.reduce(
(a: any, { key }: any) => {
a[key] = entry?.key === key ? true : false;
return a;
},
{
hover: null,
}
)
);
}}
onClick={() => {
if (!hasShownTooltip) {
setHasShownTooltip(true);
setShowTooltip(true);
setTimeout(() => {
setShowTooltip(false);
}, 1500);
}

setBarProps((barProps: any) => {
return {
...barProps,
[entry?.key]: !barProps[entry?.key],
hover: null,
};
});
}}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Click and double-click handlers may conflict.

When a user double-clicks a legend item, the onClick handler fires twice before the onDoubleClick handler executes. This causes the item to toggle on/off twice before being isolated, which may result in visual flickers or unexpected intermediate states.

Consider using a click delay pattern or debouncing to distinguish between single and double clicks:

const clickTimeoutRef = useRef<NodeJS.Timeout | null>(null);

// In the JSX:
onClick={(e) => {
  if (clickTimeoutRef.current) {
    // This is part of a double-click, ignore
    clearTimeout(clickTimeoutRef.current);
    clickTimeoutRef.current = null;
    return;
  }
  
  clickTimeoutRef.current = setTimeout(() => {
    // This is a single click
    if (!hasShownTooltip) {
      setHasShownTooltip(true);
      setShowTooltip(true);
      setTimeout(() => setShowTooltip(false), 1500);
    }
    setBarProps((barProps: any) => ({
      ...barProps,
      [entry?.key]: !barProps[entry?.key],
      hover: null,
    }));
    clickTimeoutRef.current = null;
  }, 250); // Delay to detect double-click
}}
🤖 Prompt for AI Agents
In index.tsx around lines 66 to 95, the onClick and onDoubleClick handlers
conflict because onClick fires twice before onDoubleClick, causing flicker; fix
this by implementing a click-delay pattern: add a ref (e.g. clickTimeoutRef) to
hold a timeout ID, change onClick to start a short timeout (≈250ms) which
performs the single-click logic (tooltip and toggling barProps) and then clears
the ref, and change onDoubleClick to clear any existing click timeout and
immediately run the double-click isolation logic; also add cleanup to clear the
timeout on unmount and type the ref appropriately so clicks that are part of a
double-click are ignored and single-clicks execute only after the delay.

Comment on lines +79 to +95
onClick={() => {
if (!hasShownTooltip) {
setHasShownTooltip(true);
setShowTooltip(true);
setTimeout(() => {
setShowTooltip(false);
}, 1500);
}

setBarProps((barProps: any) => {
return {
...barProps,
[entry?.key]: !barProps[entry?.key],
hover: null,
};
});
}}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Memory leak: setTimeout not cleaned up on unmount.

The setTimeout on line 83 will attempt to call setShowTooltip(false) even if the component has unmounted before 1.5 seconds elapse, causing a React warning and potential memory leak.

Store the timeout ID and clean it up:

+import { useEffect, useState, useRef } from 'react';

 export default function CustomLegend({
   ...
 }: any) {
+  const tooltipTimeoutRef = useRef<NodeJS.Timeout | null>(null);
   const showReset = !keys.every((item: any) => barProps[item.key]);
   const [hasShownTooltip, setHasShownTooltip] = useState(false);
   const [showTooltip, setShowTooltip] = useState(false);
+
+  useEffect(() => {
+    return () => {
+      if (tooltipTimeoutRef.current) {
+        clearTimeout(tooltipTimeoutRef.current);
+      }
+    };
+  }, []);

   ...
   
   onClick={() => {
     if (!hasShownTooltip) {
       setHasShownTooltip(true);
       setShowTooltip(true);
-      setTimeout(() => {
+      tooltipTimeoutRef.current = setTimeout(() => {
         setShowTooltip(false);
+        tooltipTimeoutRef.current = null;
       }, 1500);
     }
     ...
   }}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
onClick={() => {
if (!hasShownTooltip) {
setHasShownTooltip(true);
setShowTooltip(true);
setTimeout(() => {
setShowTooltip(false);
}, 1500);
}
setBarProps((barProps: any) => {
return {
...barProps,
[entry?.key]: !barProps[entry?.key],
hover: null,
};
});
}}
onClick={() => {
if (!hasShownTooltip) {
setHasShownTooltip(true);
setShowTooltip(true);
tooltipTimeoutRef.current = setTimeout(() => {
setShowTooltip(false);
tooltipTimeoutRef.current = null;
}, 1500);
}
setBarProps((barProps: any) => {
return {
...barProps,
[entry?.key]: !barProps[entry?.key],
hover: null,
};
});
}}
🤖 Prompt for AI Agents
In index.tsx around lines 79 to 95, the onClick handler creates a setTimeout
that calls setShowTooltip(false) but never clears the timer, risking a memory
leak and React state update after unmount; store the timeout ID (useRef<number |
null> or similar), assign the ID when creating setTimeout, clear any existing
timeout before setting a new one, and call clearTimeout in a useEffect cleanup
(or componentWillUnmount equivalent) so the timer is cancelled if the component
unmounts or a new timeout is created.

Comment on lines +145 to +155
<Text
_hover={
barProps[entry?.key]
? { color: entry?.color, fontWeight: 600 }
: { color: 'grey' }
}
color={barProps[entry?.key] ? entry?.color : 'grey'}
decoration={barProps[entry?.key] ? 'auto' : 'line-through'}
>
{entry?.name}
</Text>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix incorrect prop name: decoration should be textDecoration.

Chakra UI's Text component does not recognize the decoration prop. It should be textDecoration to apply the line-through style to inactive legend items.

Apply this diff:

 <Text
   _hover={
     barProps[entry?.key]
       ? { color: entry?.color, fontWeight: 600 }
       : { color: 'grey' }
   }
   color={barProps[entry?.key] ? entry?.color : 'grey'}
-  decoration={barProps[entry?.key] ? 'auto' : 'line-through'}
+  textDecoration={barProps[entry?.key] ? 'none' : 'line-through'}
 >
   {entry?.name}
 </Text>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<Text
_hover={
barProps[entry?.key]
? { color: entry?.color, fontWeight: 600 }
: { color: 'grey' }
}
color={barProps[entry?.key] ? entry?.color : 'grey'}
decoration={barProps[entry?.key] ? 'auto' : 'line-through'}
>
{entry?.name}
</Text>
<Text
_hover={
barProps[entry?.key]
? { color: entry?.color, fontWeight: 600 }
: { color: 'grey' }
}
color={barProps[entry?.key] ? entry?.color : 'grey'}
textDecoration={barProps[entry?.key] ? 'none' : 'line-through'}
>
{entry?.name}
</Text>
🤖 Prompt for AI Agents
In index.tsx around lines 145 to 155, the Chakra UI Text prop `decoration` is
incorrect and should be `textDecoration`; update the prop name to
`textDecoration` and keep the same conditional value (`barProps[entry?.key] ?
'auto' : 'line-through'`) so the inactive legend items render with a
line-through.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L This PR changes 100-499 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants