Skip to content
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

Add CopyPrimitive components for copying text functionality #566

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

kotAPI
Copy link
Collaborator

@kotAPI kotAPI commented Nov 17, 2024

This PR adds Copy Primitive

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced a new CopyPrimitive module, including components for managing copy operations.
    • Added CopyPrimitiveRoot, CopyPrimitiveTrigger, and CopyPrimitiveFeedback for enhanced clipboard interaction.
    • Users can now copy text to the clipboard with visual feedback indicating success.
    • New Copy component created to simplify the use of copy functionality with customizable options.
  • Documentation

    • Added Storybook stories for both CopyPrimitive and Copy components to demonstrate usage and functionality.
  • Styles

    • Introduced new styles for the Copy component, enhancing its visual presentation and interactivity.

Copy link
Contributor

coderabbitai bot commented Nov 17, 2024

Walkthrough

The changes introduce a new context and several components related to copy functionality within a React application. The CopyPrimitiveContext is created to manage the copied state, while CopyPrimitiveRoot, CopyPrimitiveTrigger, and CopyPrimitiveFeedback components are implemented to handle the user interface and interactions for copying text to the clipboard. Additionally, a Copy component is created to integrate these primitives, along with Storybook stories for showcasing their usage.

Changes

File Path Change Summary
src/core/primitives/Copy/contexts/CopyPrimitiveContext.tsx Added a new context CopyPrimitiveContext using createContext.
src/core/primitives/Copy/fragments/CopyPrimitiveFeedback.tsx Introduced CopyPrimitiveFeedback component that displays feedback based on isCopied state.
src/core/primitives/Copy/fragments/CopyPrimitiveRoot.tsx Added CopyPrimitiveRoot component to manage copy state and provide context to children.
src/core/primitives/Copy/fragments/CopyPrimitiveTrigger.tsx Created CopyPrimitiveTrigger component for copying text to clipboard and managing the copied state.
src/core/primitives/Copy/index.tsx Aggregated exports of CopyPrimitiveRoot, CopyPrimitiveTrigger, and CopyPrimitiveFeedback into CopyPrimitive.
src/components/ui/Copy/Copy.tsx Introduced new Copy component that utilizes CopyPrimitive components for copy functionality.
src/components/ui/Copy/stories/Copy.stories.tsx Added a Storybook story for the Copy component, showcasing its usage with Trigger and Feedback.
styles/themes/components/copy.scss Added new CSS classes for styling the copy components, including .rad-ui-copy, .rad-ui-copy-trigger, and .rad-ui-copy-feedback.
styles/themes/default.scss Included a new import for copy.scss to apply the styles to the theme.
src/components/ui/Avatar/Avatar.tsx Modified props handling in the Avatar component to allow additional properties for flexibility.

Possibly related PRs

  • AvatarPrimitive Component + Base AvatarGroup component #425: The introduction of the AvatarGroup component is directly related to the main PR, as it involves the creation of a new context and components that manage avatar states, similar to how CopyPrimitiveContext manages copy states.
  • AvatarGroup Component #503: This PR also introduces the AvatarGroup component, which is a direct implementation of the concepts discussed in the main PR regarding context and component structure.
  • Added Copy component #499: The addition of the Copy component, which utilizes context for managing copy functionality, aligns with the context management introduced in the main PR.
  • AlertDialog Component #448: The AlertDialog component's introduction of context management parallels the context creation in the main PR, indicating a similar approach to managing component states.
  • Tabs Improvements and Refactor #521: The refactoring of the Tabs component to improve accessibility and functionality reflects the same principles of component design and state management as seen in the main PR.

Poem

In the land of code where rabbits hop,
A context was born, and it won't stop!
With triggers and feedback, oh what a delight,
Copying text, making it right!
So click that button, give it a try,
Watch "Copied!" appear, oh my, oh my! 🐇✨

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.

src/components/ui/Avatar/Avatar.tsx

Oops! Something went wrong! :(

ESLint: 8.56.0

Error: Failed to load parser '@babel/eslint-parser' declared in '.eslintrc.cjs': Cannot find module '@babel/core/package.json'
Require stack:

  • /node_modules/@babel/eslint-parser/lib/parse.cjs
  • /node_modules/@babel/eslint-parser/lib/index.cjs
  • /node_modules/@eslint/eslintrc/dist/eslintrc.cjs
    at Module._resolveFilename (node:internal/modules/cjs/loader:1248:15)
    at Function.resolve (node:internal/modules/helpers:145:19)
    at Object. (/node_modules/@babel/eslint-parser/lib/parse.cjs:14:19)
    at Module._compile (node:internal/modules/cjs/loader:1546:14)
    at Module._extensions..js (node:internal/modules/cjs/loader:1691:10)
    at Module.load (node:internal/modules/cjs/loader:1317:32)
    at Module._load (node:internal/modules/cjs/loader:1127:12)
    at TracingChannel.traceSync (node:diagnostics_channel:315:14)
    at wrapModuleLoad (node:internal/modules/cjs/loader:217:24)
    at Module.require (node:internal/modules/cjs/loader:1339:12)

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 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 resolve resolve all the CodeRabbit review comments.
  • @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

codecov bot commented Nov 17, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 92.10%. Comparing base (3ad695f) to head (b192cc0).
Report is 4 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #566      +/-   ##
==========================================
- Coverage   92.30%   92.10%   -0.21%     
==========================================
  Files          12       13       +1     
  Lines          65       76      +11     
  Branches       15       16       +1     
==========================================
+ Hits           60       70      +10     
- Misses          5        6       +1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

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: 9

🧹 Outside diff range and nitpick comments (4)
src/core/primitives/Copy/fragments/CopyPrimitiveFeedback.tsx (1)

5-13: Remove unused children prop

The component accepts a children prop but never uses it in the render method. This could be misleading to developers trying to use the component.

Consider removing the children prop from the destructuring since it's not being used:

-const CopyPrimitiveFeedback = ({ children, isCopiedText = 'Copied!', ...props }: any) => {
+const CopyPrimitiveFeedback = ({ isCopiedText = 'Copied!', ...props }: any) => {
src/core/primitives/Copy/fragments/CopyPrimitiveRoot.tsx (2)

1-4: Consider adding proper TypeScript types for imports

The presence of TypeScript in the project suggests we should properly type our imports and dependencies.

Consider adding type definitions:

-import Primitive from '~/core/primitives/Primitive';
+import type { PrimitiveButtonProps } from '~/core/primitives/Primitive';
+import Primitive from '~/core/primitives/Primitive';

-import CopyPrimitiveContext from '../contexts/CopyPrimitiveContext';
+import type { CopyPrimitiveContextValue } from '../contexts/CopyPrimitiveContext';
+import CopyPrimitiveContext from '../contexts/CopyPrimitiveContext';

9-12: Optimize context value with useMemo

The context value object is recreated on every render, which could cause unnecessary rerenders in consuming components.

Consider memoizing the context value:

+import React, { useState, useMemo } from 'react';
 
-    const values = {
+    const values = useMemo(() => ({
         isCopied,
         setIsCopied
-    };
+    }), [isCopied]);
src/core/primitives/Copy/fragments/CopyPrimitiveTrigger.tsx (1)

7-7: Add error handling for missing context.

The component might break if used outside of CopyPrimitiveContext provider.

-    const { setIsCopied, isCopied } = useContext(CopyPrimitiveContext);
+    const context = useContext(CopyPrimitiveContext);
+    if (!context) {
+        throw new Error('CopyTrigger must be used within a CopyPrimitiveContext.Provider');
+    }
+    const { setIsCopied, isCopied } = context;
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 2583bc3 and 7c74d17.

📒 Files selected for processing (6)
  • src/core/primitives/Copy/contexts/CopyPrimitiveContext.tsx (1 hunks)
  • src/core/primitives/Copy/fragments/CopyPrimitiveFeedback.tsx (1 hunks)
  • src/core/primitives/Copy/fragments/CopyPrimitiveRoot.tsx (1 hunks)
  • src/core/primitives/Copy/fragments/CopyPrimitiveTrigger.tsx (1 hunks)
  • src/core/primitives/Copy/index.tsx (1 hunks)
  • src/core/primitives/Copy/stories/CopyPrimitive.stories.tsx (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • src/core/primitives/Copy/contexts/CopyPrimitiveContext.tsx
  • src/core/primitives/Copy/stories/CopyPrimitive.stories.tsx
🔇 Additional comments (5)
src/core/primitives/Copy/index.tsx (2)

11-11: LGTM: Clean default export

The default export is appropriate for this compound component.


5-9: Well-structured compound component implementation

The compound component pattern is well implemented here, providing a clean API for the Copy Primitive feature. The use of as const ensures proper type inference and immutability.

Let's verify the component structure and usage:

✅ Verification successful

Well-implemented compound component pattern confirmed

The implementation shows a proper compound component structure with:

  • A Root component managing shared state through React Context
  • A Trigger component handling copy functionality with clipboard API
  • A Feedback component for copy status indication
  • Proper context sharing between components
  • Clean separation of concerns

The component structure follows React best practices and the as const assertion in the original code ensures type safety for the compound component API.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the component structure and potential usage patterns

# Check if the fragment files exist
echo "Verifying fragment files existence:"
fd "CopyPrimitive(Root|Trigger|Feedback).tsx" src/core/primitives/Copy/fragments

# Look for any existing usage patterns
echo "\nChecking usage patterns:"
rg -A 2 "CopyPrimitive\." --type tsx

Length of output: 471


Script:

#!/bin/bash
# Let's verify the usage patterns with the correct file extension
echo "Checking usage patterns:"
rg -A 2 "CopyPrimitive\." --type ts --type tsx

# Let's also check the implementation of each component to verify proper compound structure
echo "\nChecking CopyPrimitiveRoot implementation:"
cat src/core/primitives/Copy/fragments/CopyPrimitiveRoot.tsx

echo "\nChecking CopyPrimitiveTrigger implementation:"
cat src/core/primitives/Copy/fragments/CopyPrimitiveTrigger.tsx

echo "\nChecking CopyPrimitiveFeedback implementation:"
cat src/core/primitives/Copy/fragments/CopyPrimitiveFeedback.tsx

Length of output: 2521

src/core/primitives/Copy/fragments/CopyPrimitiveFeedback.tsx (1)

1-3: Verify CopyPrimitiveContext implementation

Let's verify that the context being imported provides the expected shape and types.

src/core/primitives/Copy/fragments/CopyPrimitiveTrigger.tsx (2)

1-3: LGTM! Imports are clean and well-organized.

The necessary dependencies are properly imported, and the relative paths are correctly used for local components.


26-26: Verify component integration and usage.

Let's ensure consistent naming and proper exports across the codebase.

✅ Verification successful

Let me gather more information about the usage of this component across the codebase.


Let me try one more verification with different patterns to check the component's usage:


Component export structure is properly implemented

The verification shows that:

  • CopyTrigger is correctly imported and exported as part of the compound component CopyPrimitive.Trigger
  • The naming is consistent across the files
  • The component follows the established pattern of compound components with Root, Trigger, and Feedback subcomponents
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for consistent naming and exports
rg -g '*.tsx' -g '*.ts' "CopyTrigger|CopyPrimitiveTrigger" 

# Verify the component is properly imported and used
ast-grep --pattern 'import { $_ } from "~/core/primitives/Copy"'

Length of output: 516


Script:

#!/bin/bash
# Check for Copy component usage patterns
rg -g '*.tsx' -g '*.ts' '<Copy.Trigger' -A 2

# Check if there are any other imports from Copy primitive
ast-grep --pattern 'import { Copy } from "~/core/primitives"'

Length of output: 110


Script:

#!/bin/bash
# Check for Copy component exports and structure
cat src/core/primitives/Copy/index.tsx

# Look for any Copy component usage with different patterns
rg -g '*.tsx' -g '*.ts' 'from.*primitives/Copy'

Length of output: 425

Comment on lines +1 to +3
import CopyPrimitiveRoot from './fragments/CopyPrimitiveRoot';
import CopyTrigger from './fragments/CopyPrimitiveTrigger';
import CopyFeedback from './fragments/CopyPrimitiveFeedback';
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Maintain consistent naming conventions across imports

The import names should match their usage in the CopyPrimitive object for better maintainability and clarity.

Apply this diff to maintain consistency:

-import CopyTrigger from './fragments/CopyPrimitiveTrigger';
-import CopyFeedback from './fragments/CopyPrimitiveFeedback';
+import CopyPrimitiveTrigger from './fragments/CopyPrimitiveTrigger';
+import CopyPrimitiveFeedback from './fragments/CopyPrimitiveFeedback';
📝 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
import CopyPrimitiveRoot from './fragments/CopyPrimitiveRoot';
import CopyTrigger from './fragments/CopyPrimitiveTrigger';
import CopyFeedback from './fragments/CopyPrimitiveFeedback';
import CopyPrimitiveRoot from './fragments/CopyPrimitiveRoot';
import CopyPrimitiveTrigger from './fragments/CopyPrimitiveTrigger';
import CopyPrimitiveFeedback from './fragments/CopyPrimitiveFeedback';

return null;
}

return <Primitive.span {...props}>{isCopiedText}</Primitive.span>;
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance accessibility for copy feedback

The feedback message should be announced to screen readers when the copy action is successful. Consider adding ARIA attributes for better accessibility.

-    return <Primitive.span {...props}>{isCopiedText}</Primitive.span>;
+    return (
+        <Primitive.span
+            role="status"
+            aria-live="polite"
+            {...props}
+        >
+            {isCopiedText}
+        </Primitive.span>
+    );
📝 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
return <Primitive.span {...props}>{isCopiedText}</Primitive.span>;
return (
<Primitive.span
role="status"
aria-live="polite"
{...props}
>
{isCopiedText}
</Primitive.span>
);

import Primitive from '~/core/primitives/Primitive';
import CopyPrimitiveContext from '../contexts/CopyPrimitiveContext';

const CopyPrimitiveFeedback = ({ children, isCopiedText = 'Copied!', ...props }: any) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Define proper TypeScript interfaces for component props

The component is using the any type which defeats the purpose of TypeScript's type safety. Consider defining a proper interface for the props.

+interface CopyPrimitiveFeedbackProps extends React.HTMLAttributes<HTMLSpanElement> {
+  isCopiedText?: string;
+  children?: never; // since children prop is not used
+}

-const CopyPrimitiveFeedback = ({ children, isCopiedText = 'Copied!', ...props }: any) => {
+const CopyPrimitiveFeedback = ({ isCopiedText = 'Copied!', ...props }: CopyPrimitiveFeedbackProps) => {
📝 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
const CopyPrimitiveFeedback = ({ children, isCopiedText = 'Copied!', ...props }: any) => {
interface CopyPrimitiveFeedbackProps extends React.HTMLAttributes<HTMLSpanElement> {
isCopiedText?: string;
children?: never; // since children prop is not used
}
const CopyPrimitiveFeedback = ({ isCopiedText = 'Copied!', ...props }: CopyPrimitiveFeedbackProps) => {

Comment on lines 6 to 8
const CopyPrimitiveRoot = ({ children, ...props }: any) => {
const [isCopied, setIsCopied] = useState(false);

Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add proper TypeScript types for component props and state

The component uses any type which bypasses TypeScript's type checking benefits. Additionally, the state management could be more explicit.

Consider this improvement:

-const CopyPrimitiveRoot = ({ children, ...props }: any) => {
+interface CopyPrimitiveRootProps extends PrimitiveButtonProps {
+  children: React.ReactNode;
+}
+
+const CopyPrimitiveRoot = ({ children, ...props }: CopyPrimitiveRootProps) => {
-    const [isCopied, setIsCopied] = useState(false);
+    const [isCopied, setIsCopied] = useState<boolean>(false);

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines 14 to 18
return (
<CopyPrimitiveContext.Provider value={values}>
<Primitive.button {...props}>{children}</Primitive.button>
</CopyPrimitiveContext.Provider>
);
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance accessibility for copy functionality

The button component should have proper ARIA attributes to indicate its purpose and state to assistive technologies.

Consider adding these accessibility improvements:

     return (
         <CopyPrimitiveContext.Provider value={values}>
-            <Primitive.button {...props}>{children}</Primitive.button>
+            <Primitive.button
+                role="button"
+                aria-label="Copy to clipboard"
+                aria-pressed={isCopied}
+                {...props}
+            >
+                {children}
+            </Primitive.button>
         </CopyPrimitiveContext.Provider>
     );

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines 1 to 21
import React, { useState } from 'react';
import Primitive from '~/core/primitives/Primitive';

import CopyPrimitiveContext from '../contexts/CopyPrimitiveContext';

const CopyPrimitiveRoot = ({ children, ...props }: any) => {
const [isCopied, setIsCopied] = useState(false);

const values = {
isCopied,
setIsCopied
};

return (
<CopyPrimitiveContext.Provider value={values}>
<Primitive.button {...props}>{children}</Primitive.button>
</CopyPrimitiveContext.Provider>
);
};

export default CopyPrimitiveRoot;
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Codebase verification

Add error handling for clipboard operations

The clipboard implementation lacks error handling for potential failures in navigator.clipboard.writeText(). This could lead to unhandled promise rejections since:

  • The clipboard API returns a Promise that's not being handled
  • No try-catch block surrounds the clipboard operation
  • No error state is maintained in the context

Key locations requiring attention:

  • src/core/primitives/Copy/fragments/CopyPrimitiveTrigger.tsx: Add async/await with try-catch for clipboard operation
  • src/core/primitives/Copy/contexts/CopyPrimitiveContext.tsx: Consider adding error state to the context
🔗 Analysis chain

Verify clipboard API implementation

The component seems to be part of a copy functionality implementation, but the actual clipboard interaction isn't visible in this file.

Let's verify the clipboard implementation in related files:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for clipboard API usage in related files
echo "Searching for clipboard API usage..."
rg -t typescript -t javascript "navigator\.clipboard|document\.execCommand.*copy" src/core/primitives/Copy/

# Look for error handling patterns
echo "Checking error handling..."
rg -t typescript -t javascript "try.*catch.*clipboard" src/core/primitives/Copy/

Length of output: 408


Script:

#!/bin/bash
# Search for clipboard API usage with correct file extensions
echo "Searching for clipboard API usage..."
rg "navigator\.clipboard|document\.execCommand.*copy" --type-add 'ts:*.{ts,tsx}' --type=ts src/core/primitives/Copy/

# Look for error handling patterns
echo "Checking error handling..."
rg "try.*catch" --type-add 'ts:*.{ts,tsx}' --type=ts src/core/primitives/Copy/

# List all files in the Copy directory to understand the structure
echo "Listing Copy directory files..."
fd . src/core/primitives/Copy/ -t f

Length of output: 896


Script:

#!/bin/bash
# Let's check the implementation of CopyPrimitiveTrigger to see the full context
echo "Checking CopyPrimitiveTrigger implementation..."
cat src/core/primitives/Copy/fragments/CopyPrimitiveTrigger.tsx

# Also check if there are any error boundaries or error states in the context
echo "Checking CopyPrimitiveContext..."
cat src/core/primitives/Copy/contexts/CopyPrimitiveContext.tsx

Length of output: 1264

import CopyPrimitiveContext from '../contexts/CopyPrimitiveContext';

// The triggering action (button) is logically part of the copying mechanism
const CopyTrigger = ({ children, text = '', resetDelay = 2000, ...props }: any) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Define proper TypeScript interface for component props.

Using any type reduces type safety. Consider defining a proper interface:

+interface CopyTriggerProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
+  children: React.ReactNode;
+  text?: string;
+  resetDelay?: number;
+}
-const CopyTrigger = ({ children, text = '', resetDelay = 2000, ...props }: any) => {
+const CopyTrigger = ({ children, text = '', resetDelay = 2000, ...props }: CopyTriggerProps) => {
📝 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
const CopyTrigger = ({ children, text = '', resetDelay = 2000, ...props }: any) => {
interface CopyTriggerProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
children: React.ReactNode;
text?: string;
resetDelay?: number;
}
const CopyTrigger = ({ children, text = '', resetDelay = 2000, ...props }: CopyTriggerProps) => {

Comment on lines 19 to 23
if (!text || isCopied) {
return null;
}

return <Primitive.button {...props} onClick={handleClick}>{children}</Primitive.button>;
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance accessibility and user feedback.

The button should have proper ARIA attributes and loading state indication.

-    return <Primitive.button {...props} onClick={handleClick}>{children}</Primitive.button>;
+    return (
+        <Primitive.button
+            {...props}
+            onClick={handleClick}
+            aria-label="Copy to clipboard"
+            aria-pressed={isCopied}
+            disabled={!text}
+        >
+            {children}
+        </Primitive.button>
+    );
📝 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
if (!text || isCopied) {
return null;
}
return <Primitive.button {...props} onClick={handleClick}>{children}</Primitive.button>;
if (!text || isCopied) {
return null;
}
return (
<Primitive.button
{...props}
onClick={handleClick}
aria-label="Copy to clipboard"
aria-pressed={isCopied}
disabled={!text}
>
{children}
</Primitive.button>
);

Comment on lines 9 to 17
const handleClick = () => {
if (text) {
setIsCopied(true);
navigator.clipboard.writeText(text);
setTimeout(() => {
setIsCopied(false);
}, resetDelay);
}
};
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Improve error handling and cleanup for clipboard operations.

The current implementation has several potential issues:

  1. No error handling for clipboard operations
  2. Possible memory leak from setTimeout
  3. No cleanup on component unmount
+    const timeoutRef = React.useRef<NodeJS.Timeout>();
+
     const handleClick = () => {
         if (text) {
             setIsCopied(true);
-            navigator.clipboard.writeText(text);
-            setTimeout(() => {
-                setIsCopied(false);
-            }, resetDelay);
+            navigator.clipboard.writeText(text)
+                .catch((error) => {
+                    console.error('Failed to copy text:', error);
+                    setIsCopied(false);
+                });
+            timeoutRef.current = setTimeout(() => {
+                setIsCopied(false);
+            }, resetDelay);
         }
     };
+
+    React.useEffect(() => {
+        return () => {
+            if (timeoutRef.current) {
+                clearTimeout(timeoutRef.current);
+            }
+        };
+    }, []);
📝 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
const handleClick = () => {
if (text) {
setIsCopied(true);
navigator.clipboard.writeText(text);
setTimeout(() => {
setIsCopied(false);
}, resetDelay);
}
};
const timeoutRef = React.useRef<NodeJS.Timeout>();
const handleClick = () => {
if (text) {
setIsCopied(true);
navigator.clipboard.writeText(text)
.catch((error) => {
console.error('Failed to copy text:', error);
setIsCopied(false);
});
timeoutRef.current = setTimeout(() => {
setIsCopied(false);
}, resetDelay);
}
};
React.useEffect(() => {
return () => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
};
}, []);

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

🧹 Outside diff range and nitpick comments (11)
src/components/ui/Copy/Copy.tsx (1)

15-20: Export types and add documentation for subcomponents

Consider adding type exports and documentation for the compound components.

Here's a suggested implementation:

+/** Copy component for clipboard operations */
 Copy.displayName = COMPONENT_NAME;
+
+/** Container component for copy functionality */
 Copy.Root = CopyPrimitive.Root;
+
+/** Button that triggers the copy operation */
 Copy.Trigger = CopyPrimitive.Trigger;
+
+/** Component that displays feedback after copying */
 Copy.Feedback = CopyPrimitive.Feedback;
 
+export type { 
+  CopyProps,
+  CopyRootProps,
+  CopyTriggerProps,
+  CopyFeedbackProps,
+};
 export default Copy;
src/components/ui/Avatar/Avatar.tsx (2)

Line range hint 8-14: Fix incorrect type definition for props

The props type is currently defined as Record<string, any>[] (array of records), but it's being spread as an object. This will cause runtime errors.

Apply this fix to the type definition:

export type AvatarProps = {
    customRootClass?: string,
    fallback?: string,
    className?: string,
    src?: string,
    alt?: string,
-   props?: Record<string, any>[]
+   [key: string]: any  // or better, extend from React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>
}

Line range hint 31-34: Consider enhancing compound component pattern

The component correctly implements the compound pattern by exposing subcomponents. Since this PR adds copy functionality, consider documenting the integration with CopyPrimitive if avatars need to be copyable.

Example usage pattern that could be documented:

<Avatar>
  <CopyPrimitive.Root>
    <Avatar.Image src="..." />
    <CopyPrimitive.Trigger>Copy Avatar URL</CopyPrimitive.Trigger>
  </CopyPrimitive.Root>
</Avatar>
src/core/primitives/Copy/fragments/CopyPrimitiveTrigger.tsx (3)

6-6: Standardize prop naming for consistency.

The prop name copyContent differs from the suggested text prop in the TypeScript interface. Consider using consistent naming across the codebase.

-const CopyTrigger = ({ children, className = '', copyContent = '', resetDelay = 2000, ...props }: any) => {
+const CopyTrigger = ({ children, className = '', text = '', resetDelay = 2000, ...props }: any) => {

23-23: Improve className handling with a utility.

Consider using a className utility like clsx or classnames for better className management.

+import clsx from 'clsx';
 
-    return <Primitive.button className={`${rootClass}-trigger ${className}`} {...props} onClick={handleClick}>{children}</Primitive.button>;
+    return <Primitive.button className={clsx(`${rootClass}-trigger`, className)} {...props} onClick={handleClick}>{children}</Primitive.button>;

5-5: Add comprehensive component documentation.

The current comment is insufficient. Add proper JSDoc documentation including:

  • Component description
  • Props documentation
  • Usage examples
-// The triggering action (button) is logically part of the copying mechanism
+/**
+ * CopyTrigger - A button component that triggers copying text to clipboard
+ *
+ * @component
+ * @example
+ * ```jsx
+ * <CopyTrigger text="Text to copy">
+ *   Click to copy
+ * </CopyTrigger>
+ * ```
+ *
+ * @param {Object} props
+ * @param {React.ReactNode} props.children - Button content
+ * @param {string} [props.text=''] - Text to copy to clipboard
+ * @param {string} [props.className=''] - Additional CSS classes
+ * @param {number} [props.resetDelay=2000] - Delay in ms before resetting copied state
+ */
styles/themes/default.scss (2)

10-10: Maintain alphabetical ordering of imports

The new import breaks the alphabetical sequence. Consider moving it between "code" and "heading" imports to maintain consistent ordering.

 @import "./components/card.scss";
-@import "./components/copy.scss";
 @import "./components/table.scss";
 @import "./components/code.scss";
+@import "./components/copy.scss";
 @import "./components/heading.scss";

Line range hint 31-78: Consider cleaning up unused code and empty rulesets

The file contains:

  • Commented-out CSS rules that appear to be unused
  • Empty rulesets (e.g., .rad-ui-tooltip, .rad-ui-tooltip-reference-element)
  • Unnecessary empty spaces between sections

Consider removing or documenting these to improve maintainability.

src/components/ui/Copy/stories/Copy.stories.tsx (3)

1-3: Consider using consistent import paths and adding type definitions.

The imports mix relative and absolute paths. Consider using consistent import paths throughout the codebase.

-import Copy from '../Copy';
-import SandboxEditor from '~/components/tools/SandboxEditor/SandboxEditor';
+import { Copy } from '~/components/ui/Copy';
+import { SandboxEditor } from '~/components/tools/SandboxEditor';

7-9: Enhance SVG icons accessibility and organization.

Consider the following improvements:

  1. Move icons to a dedicated icons directory
  2. Add ARIA labels and roles for accessibility
-const CopyIcon = () => <svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg">
+const CopyIcon = () => <svg 
+  width="15" 
+  height="15" 
+  viewBox="0 0 15 15" 
+  fill="none" 
+  xmlns="http://www.w3.org/2000/svg"
+  role="img"
+  aria-label="Copy"
+>

35-39: Enhance story with more comprehensive examples.

The current story only demonstrates basic usage. Consider adding:

  1. Different states (success, error)
  2. Various content lengths
  3. Different trigger styles
  4. Accessibility examples
export const Default = {
  args: {
    copyContent: 'Hello, world!',
    children: 'Copy'
  }
};

export const WithLongContent = {
  args: {
    copyContent: 'This is a very long text that demonstrates how the component handles lengthy content...',
    children: 'Copy'
  }
};

export const CustomTrigger = {
  args: {
    copyContent: 'Custom trigger example',
    children: <button className="custom-button">Copy me!</button>
  }
};
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 7c74d17 and b192cc0.

📒 Files selected for processing (9)
  • src/components/ui/Avatar/Avatar.tsx (1 hunks)
  • src/components/ui/Copy/Copy.tsx (1 hunks)
  • src/components/ui/Copy/stories/Copy.stories.tsx (1 hunks)
  • src/core/primitives/Copy/fragments/CopyPrimitiveFeedback.tsx (1 hunks)
  • src/core/primitives/Copy/fragments/CopyPrimitiveRoot.tsx (1 hunks)
  • src/core/primitives/Copy/fragments/CopyPrimitiveTrigger.tsx (1 hunks)
  • src/core/primitives/Copy/stories/CopyPrimitive.stories.tsx (1 hunks)
  • styles/themes/components/copy.scss (1 hunks)
  • styles/themes/default.scss (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • styles/themes/components/copy.scss
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/core/primitives/Copy/fragments/CopyPrimitiveFeedback.tsx
  • src/core/primitives/Copy/stories/CopyPrimitive.stories.tsx
🔇 Additional comments (9)
src/components/ui/Copy/Copy.tsx (2)

1-20: Verify test coverage and Storybook stories

Please ensure that this component has adequate test coverage and Storybook stories.

#!/bin/bash
# Check for test files
echo "Checking for test files..."
fd -e test.tsx -e spec.tsx "Copy" src/

# Check for story files
echo "Checking for story files..."
fd -e stories.tsx "Copy" src/

1-2: Verify the import alias configuration

The import path uses the ~ alias. Let's verify this alias is properly configured in the build system.

✅ Verification successful

The ~ alias is properly configured in the build system

The import path using the ~ alias is correctly configured in the tsconfig.json file where it maps ~/* to src/*. This is a valid and working configuration.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for tsconfig.json or other config files that might define path aliases
fd -e json -e js "tsconfig|webpack.config|vite.config" -x cat {} \; | grep -A 5 "paths\|alias"

Length of output: 303


Script:

#!/bin/bash
# Let's check for tsconfig.json files first
fd tsconfig.json -x cat {}

# Also check for any vite config files
fd "vite.config" -x cat {}

Length of output: 1389

src/core/primitives/Copy/fragments/CopyPrimitiveRoot.tsx (4)

1-6: LGTM! Imports are clean and well-organized.


8-8: ⚠️ Potential issue

Previous typing suggestion remains unaddressed

The any type is still being used, which was highlighted in a previous review. Additionally, spreading unknown props could lead to prop pollution.

Consider this improvement:

-const CopyPrimitiveRoot = ({ customRootClass = '', className = '', children, ...props }: any) => {
+interface CopyPrimitiveRootProps {
+  customRootClass?: string;
+  className?: string;
+  children: React.ReactNode;
+  // Only allow valid HTML span attributes
+  [key: string]: unknown;
+}
+
+const CopyPrimitiveRoot = ({
+  customRootClass = '',
+  className = '',
+  children,
+  ...props
+}: CopyPrimitiveRootProps) => {

9-16: ⚠️ Potential issue

Add error handling state

The context should include error handling for clipboard operations as mentioned in the previous review.

Consider this improvement:

-const [isCopied, setIsCopied] = useState(false);
+const [isCopied, setIsCopied] = useState<boolean>(false);
+const [error, setError] = useState<Error | null>(null);
 const rootClass = customClassSwitcher(customRootClass, 'Copy');

 const values = {
     isCopied,
     setIsCopied,
+    error,
+    setError,
     rootClass
 };

1-25: Verify component integration

Let's verify how this component is integrated with other copy primitive components.

✅ Verification successful

Let me gather more information about the component integration by examining the story files and searching for imports with a different approach.


Let me check the content of the related Copy primitive components to understand the complete integration.


Let me check the correct paths for the context and index files.


Based on the gathered information, I can now provide the final response:

Component integration is properly implemented

The CopyPrimitiveRoot component is correctly integrated within a compound component pattern:

  • Properly exports context through CopyPrimitiveContext
  • Correctly composed with CopyTrigger and CopyFeedback components
  • Successfully demonstrated in both Copy and CopyPrimitive stories
  • Follows React best practices for compound components

The implementation shows proper integration with:

  • Context sharing between components
  • Compound component structure in index.tsx
  • Working examples in storybook files
  • Consistent class naming convention
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check component integration and usage patterns

# Check for imports of CopyPrimitiveRoot
echo "Checking usage of CopyPrimitiveRoot..."
rg "import.*CopyPrimitiveRoot" --type=typescript

# Check story files for usage examples
echo "Checking story files..."
fd "Copy.*\.(story|stories)\.(tsx|ts)" src/

# Check test coverage
echo "Checking test files..."
fd "Copy.*\.(test|spec)\.(tsx|ts)" src/

Length of output: 479


Script:

#!/bin/bash
# Check content of story files to understand usage
echo "=== Content of Copy.stories.tsx ==="
cat "src/components/ui/Copy/stories/Copy.stories.tsx"

echo -e "\n=== Content of CopyPrimitive.stories.tsx ==="
cat "src/core/primitives/Copy/stories/CopyPrimitive.stories.tsx"

# Check imports using ripgrep with different file extensions
echo -e "\n=== Checking imports in TypeScript/React files ==="
rg "import.*CopyPrimitiveRoot" -t tsx -t ts -t jsx -t js

# Check for any Copy primitive related components
echo -e "\n=== Checking Copy primitive related files ==="
fd "Copy.*\.(tsx|ts|jsx|js)" src/core/primitives/Copy/

Length of output: 4399


Script:

#!/bin/bash
# Check the implementation of related components
echo "=== Content of CopyPrimitiveTrigger.tsx ==="
cat "src/core/primitives/Copy/fragments/CopyPrimitiveTrigger.tsx"

echo -e "\n=== Content of CopyPrimitiveFeedback.tsx ==="
cat "src/core/primitives/Copy/fragments/CopyPrimitiveFeedback.tsx"

echo -e "\n=== Content of CopyPrimitiveContext.tsx ==="
cat "src/core/primitives/Copy/fragments/CopyPrimitiveContext.tsx"

# Check the index file to see how components are exported
echo -e "\n=== Content of index file ==="
cat "src/core/primitives/Copy/index.ts"

Length of output: 2232


Script:

#!/bin/bash
# Check the context file in the correct location
echo "=== Content of CopyPrimitiveContext.tsx ==="
cat "src/core/primitives/Copy/contexts/CopyPrimitiveContext.tsx"

# Find and check the index file
echo -e "\n=== Looking for index files ==="
fd "index\.(ts|tsx)" src/core/primitives/Copy/

echo -e "\n=== Content of index file if found ==="
cat "src/core/primitives/Copy/index.tsx" 2>/dev/null || cat "src/core/primitives/Copy/index.ts" 2>/dev/null

Length of output: 919

src/components/ui/Avatar/Avatar.tsx (1)

Line range hint 19-27: Consider removing duplicate src prop

The src prop is being passed to both AvatarPrimitive.Root and AvatarPrimitive.Image. This could lead to confusion about which one takes precedence.

Let's check how src is used in the primitive:

Consider passing src only to AvatarPrimitive.Image unless there's a specific need for it in Root:

- <AvatarPrimitive.Root src={src} customRootClass={customRootClass} {...props}>
+ <AvatarPrimitive.Root customRootClass={customRootClass} {...props}>
src/core/primitives/Copy/fragments/CopyPrimitiveTrigger.tsx (1)

7-7: LGTM! Context usage is clean and efficient.

src/components/ui/Copy/stories/Copy.stories.tsx (1)

2-2: Verify Copy component implementation and accessibility.

Let's ensure the Copy component is properly implemented and follows accessibility best practices.

Comment on lines +7 to +12
return <CopyPrimitive.Root asChild customRootClass={customRootClass} {...props}>
<CopyPrimitive.Trigger resetDelay={resetDelay} copyContent={copyContent} >{children}</CopyPrimitive.Trigger>
<CopyPrimitive.Feedback >
{isCopiedContent}
</CopyPrimitive.Feedback>
</CopyPrimitive.Root>;
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance component implementation with error handling and accessibility

The current implementation could benefit from several improvements:

  1. Error handling for clipboard operations
  2. Accessibility attributes
  3. Loading and error states

Here's a suggested implementation:

-    return <CopyPrimitive.Root asChild customRootClass={customRootClass} {...props}>
-        <CopyPrimitive.Trigger resetDelay={resetDelay} copyContent={copyContent} >{children}</CopyPrimitive.Trigger>
-        <CopyPrimitive.Feedback >
-            {isCopiedContent}
-        </CopyPrimitive.Feedback>
-    </CopyPrimitive.Root>;
+    return (
+        <CopyPrimitive.Root 
+            asChild 
+            customRootClass={customRootClass} 
+            {...props}
+        >
+            <CopyPrimitive.Trigger 
+                resetDelay={resetDelay} 
+                copyContent={copyContent}
+                aria-label="Copy to clipboard"
+                onError={(error) => console.error('Failed to copy:', error)}
+            >
+                {children}
+            </CopyPrimitive.Trigger>
+            <CopyPrimitive.Feedback 
+                role="status"
+                aria-live="polite"
+            >
+                {isCopiedContent}
+            </CopyPrimitive.Feedback>
+        </CopyPrimitive.Root>
+    );
📝 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
return <CopyPrimitive.Root asChild customRootClass={customRootClass} {...props}>
<CopyPrimitive.Trigger resetDelay={resetDelay} copyContent={copyContent} >{children}</CopyPrimitive.Trigger>
<CopyPrimitive.Feedback >
{isCopiedContent}
</CopyPrimitive.Feedback>
</CopyPrimitive.Root>;
return (
<CopyPrimitive.Root
asChild
customRootClass={customRootClass}
{...props}
>
<CopyPrimitive.Trigger
resetDelay={resetDelay}
copyContent={copyContent}
aria-label="Copy to clipboard"
onError={(error) => console.error('Failed to copy:', error)}
>
{children}
</CopyPrimitive.Trigger>
<CopyPrimitive.Feedback
role="status"
aria-live="polite"
>
{isCopiedContent}
</CopyPrimitive.Feedback>
</CopyPrimitive.Root>
);

Comment on lines +6 to +13
const Copy = ({ customRootClass = '', children, isCopiedContent = 'Copied!', resetDelay = 2000, copyContent = '', ...props }: any) => {
return <CopyPrimitive.Root asChild customRootClass={customRootClass} {...props}>
<CopyPrimitive.Trigger resetDelay={resetDelay} copyContent={copyContent} >{children}</CopyPrimitive.Trigger>
<CopyPrimitive.Feedback >
{isCopiedContent}
</CopyPrimitive.Feedback>
</CopyPrimitive.Root>;
};
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Define proper TypeScript interfaces for props

The component uses any type which defeats TypeScript's type safety. Consider defining a proper interface for the props.

Here's a suggested implementation:

+interface CopyProps {
+  /** Custom class name for the root element */
+  customRootClass?: string;
+  /** Content to be displayed in the trigger */
+  children: React.ReactNode;
+  /** Text shown when content is copied */
+  isCopiedContent?: string;
+  /** Delay in milliseconds before resetting the copied state */
+  resetDelay?: number;
+  /** Content to be copied to clipboard */
+  copyContent: string;
+}
+
-const Copy = ({ customRootClass = '', children, isCopiedContent = 'Copied!', resetDelay = 2000, copyContent = '', ...props }: any) => {
+const Copy = ({ 
+  customRootClass = '', 
+  children, 
+  isCopiedContent = 'Copied!', 
+  resetDelay = 2000, 
+  copyContent,
+  ...props 
+}: CopyProps) => {
📝 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
const Copy = ({ customRootClass = '', children, isCopiedContent = 'Copied!', resetDelay = 2000, copyContent = '', ...props }: any) => {
return <CopyPrimitive.Root asChild customRootClass={customRootClass} {...props}>
<CopyPrimitive.Trigger resetDelay={resetDelay} copyContent={copyContent} >{children}</CopyPrimitive.Trigger>
<CopyPrimitive.Feedback >
{isCopiedContent}
</CopyPrimitive.Feedback>
</CopyPrimitive.Root>;
};
interface CopyProps {
/** Custom class name for the root element */
customRootClass?: string;
/** Content to be displayed in the trigger */
children: React.ReactNode;
/** Text shown when content is copied */
isCopiedContent?: string;
/** Delay in milliseconds before resetting the copied state */
resetDelay?: number;
/** Content to be copied to clipboard */
copyContent: string;
}
const Copy = ({
customRootClass = '',
children,
isCopiedContent = 'Copied!',
resetDelay = 2000,
copyContent,
...props
}: CopyProps) => {
return <CopyPrimitive.Root asChild customRootClass={customRootClass} {...props}>
<CopyPrimitive.Trigger resetDelay={resetDelay} copyContent={copyContent} >{children}</CopyPrimitive.Trigger>
<CopyPrimitive.Feedback >
{isCopiedContent}
</CopyPrimitive.Feedback>
</CopyPrimitive.Root>;
};

Comment on lines +18 to +22
return (
<CopyPrimitiveContext.Provider value={values}>
<Primitive.span className={`${rootClass} ${className}`} {...props}>{children}</Primitive.span>
</CopyPrimitiveContext.Provider>
);
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Use semantic HTML and improve accessibility

The component uses a span element which might not be the most semantic choice for copy functionality. Consider using a more appropriate element and adding accessibility attributes.

Consider this improvement:

 return (
     <CopyPrimitiveContext.Provider value={values}>
-        <Primitive.span className={`${rootClass} ${className}`} {...props}>{children}</Primitive.span>
+        <Primitive.div
+            role="region"
+            aria-label="Copyable content"
+            className={`${rootClass}`.trim() + (className ? ` ${className}` : '')}
+            {...props}
+        >
+            {children}
+        </Primitive.div>
     </CopyPrimitiveContext.Provider>
 );
📝 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
return (
<CopyPrimitiveContext.Provider value={values}>
<Primitive.span className={`${rootClass} ${className}`} {...props}>{children}</Primitive.span>
</CopyPrimitiveContext.Provider>
);
return (
<CopyPrimitiveContext.Provider value={values}>
<Primitive.div
role="region"
aria-label="Copyable content"
className={`${rootClass}`.trim() + (className ? ` ${className}` : '')}
{...props}
>
{children}
</Primitive.div>
</CopyPrimitiveContext.Provider>
);

Comment on lines +19 to +21
if (!copyContent || isCopied) {
return null;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Reconsider UX for button visibility.

Hiding the button after copying might confuse users or prevent them from copying the same content multiple times. Consider:

  1. Keeping the button visible and updating its text/icon to indicate the copied state
  2. Allowing users to copy content multiple times without waiting for the reset
-    if (!copyContent || isCopied) {
+    if (!copyContent) {
         return null;
     }
📝 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
if (!copyContent || isCopied) {
return null;
}
if (!copyContent) {
return null;
}

Comment on lines +14 to +31
render: (args: any) => <SandboxEditor>
<div >
<Copy.Root {...args}>
<Copy.Trigger copyContent="Hello, world!" {...args}>
<div className="flex items-center gap-2">
<span>Copy</span>
<CopyIcon />
</div>
</Copy.Trigger>
<Copy.Feedback {...args}>
<div className="flex items-center gap-2">
<span>Copied!</span>
<TickIcon />
</div>
</Copy.Feedback>
</Copy.Root>
</div>
</SandboxEditor>
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve type safety and component flexibility.

Several improvements could enhance this story:

  1. Replace any with proper type definitions
  2. Make copy content configurable via args
  3. Consider using design system tokens for spacing instead of utility classes
-    render: (args: any) => <SandboxEditor>
+    render: (args: CopyProps) => <SandboxEditor>
         <div >
             <Copy.Root {...args}>
-                <Copy.Trigger copyContent="Hello, world!" {...args}>
+                <Copy.Trigger copyContent={args.copyContent} {...args}>
                     <div className="flex items-center gap-2">

Also, consider extracting the hardcoded styles into your design system's spacing tokens:

-    <div className="flex items-center gap-2">
+    <div className={styles.container}>

Committable suggestion skipped: line range outside the PR's diff.

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.

1 participant