Skip to content

Conversation

@ErfanKashef
Copy link
Contributor

@ErfanKashef ErfanKashef commented Apr 24, 2025

#264 closee

Summary by CodeRabbit

  • New Features

    • Introduced a rich text editor component with WYSIWYG and HTML source editing modes, supporting image uploads, hyperlink insertion, and advanced formatting options.
    • Added a customizable editor toolbar with controls for headings, lists, formatting, and code blocks.
    • Provided consistent rich text styling through new style provider components.
  • Documentation

    • Added interactive Storybook stories to demonstrate and test the new rich text editor component.
  • Style

    • Enhanced global and editor-specific styles for improved focus and selection visuals.
    • Integrated Tailwind Typography plugin for better typography support.
  • Chores

    • Updated and reorganized dependencies and configuration files for editor functionality and styling consistency.

@pixel-dockploy
Copy link

pixel-dockploy bot commented Apr 24, 2025

Dokploy Preview Deployment

Name Status Preview Updated (UTC)
client 🔄 Building Preview URL 2025-04-24T21:02:04.058Z

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Apr 24, 2025

Walkthrough

This set of changes introduces a new RichTextEditor component based on Tiptap in the UI package, along with its supporting toolbar, style providers, and Storybook stories. The editor supports both WYSIWYG and HTML source editing modes, image uploads, and link insertion. The RichTextEditor replaces the previous FileFormatSection in the test page. Additional dependencies for Tiptap and Tailwind Typography are added, and related CSS and Tailwind configuration updates are included. Several new components are implemented to support toolbar actions and consistent styling for rich text content.

Changes

File(s) Change Summary
apps/core/app/test/page.tsx Replaced FileFormatSection with the new RichTextEditor component; removed related imports.
apps/storybook/src/stories/editor.stories.tsx Added new Storybook stories for RichTextEditor, showcasing default, error, and custom class variants.
package.json Reordered fields: moved devDependencies, engines, and packageManager near the top; moved private below packageManager. No content changes.
packages/ui/package.json Reordered dependencies, added Tiptap-related and Tailwind Typography dependencies.
packages/ui/src/components/atoms/command.tsx Removed unused imports for dialog components and types.
packages/ui/src/components/atoms/rich-text-style-provider.tsx
.../molecules/editor/rich-text-styles-provider.tsx
Added two similar RichTextStylesProvider components for wrapping and styling rich text content using Tailwind classes.
packages/ui/src/components/atoms/textarea.tsx Added missing semicolons and a trailing comma; no logic changes.
packages/ui/src/components/molecules/editor/editor-toolbar-button.tsx Introduced EditorToolbarButton component for toolbar actions with active/disabled states and icons.
packages/ui/src/components/molecules/editor/editor-toolbar-image.tsx Added EditorToolbarImage component for image uploads in the editor toolbar, including upload logic and error handling.
packages/ui/src/components/molecules/editor/editor-toolbar-view-html.tsx Added EditorToolbarViewHtml component for toggling between WYSIWYG and HTML source views.
packages/ui/src/components/molecules/editor/editor-toolbar-view-set-link.tsx Added EditorToolbarSetLink component for inserting hyperlinks via a popover in the editor toolbar.
packages/ui/src/components/molecules/editor/editor-toolbar.tsx Added EditorToolbar component with grouped controls for formatting, lists, code, undo/redo, and view toggling.
packages/ui/src/components/molecules/editor/editor.tsx Introduced RichTextEditor component with Tiptap integration, HTML mode, toolbar, controlled/uncontrolled support, and error/helper text handling.
packages/ui/src/styles/globals.scss Added custom styles for Tiptap ProseMirror focus and selected node states.
packages/ui/tailwind.config.ts Added @tailwindcss/typography plugin to Tailwind configuration.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant RichTextEditor
    participant EditorToolbar
    participant TiptapEditor
    participant Server (Image Upload)
    
    User->>RichTextEditor: Type or interact with editor
    RichTextEditor->>TiptapEditor: Update content
    TiptapEditor-->>RichTextEditor: Trigger onChange (HTML, text)
    RichTextEditor-->>User: Calls onChange handler

    User->>EditorToolbar: Click toolbar button (e.g., Bold, Heading)
    EditorToolbar->>TiptapEditor: Execute command (toggle bold, heading, etc.)
    TiptapEditor-->>RichTextEditor: Update state

    User->>EditorToolbar: Click "Insert Image"
    EditorToolbar->>Server (Image Upload): Upload image file
    Server (Image Upload)-->>EditorToolbar: Return image URL
    EditorToolbar->>TiptapEditor: Insert image with URL

    User->>EditorToolbar: Toggle HTML view
    EditorToolbar->>RichTextEditor: Switch to HTML textarea
    User->>RichTextEditor: Edit HTML source
    RichTextEditor->>TiptapEditor: Update content from HTML
Loading

Possibly related PRs

Suggested reviewers

  • mrbadri

Poem

In fields of code where editors grow,
A rabbit hops where rich texts flow.
With toolbars bright and styles anew,
HTML and WYSIWYG—both in view!
Uploads and links, a click away,
Storybook tales for all to play.
🐇✨ Now type your dreams, the rabbit says!

✨ Finishing Touches
  • 📝 Generate Docstrings

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

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

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

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this 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.

@openzeppelin-code
Copy link

Feat/#264 editor component

Generated at commit: 9bbb32b2610fb1a6b31d0e3ef3a5bce001a8997b

🚨 Report Summary

Severity Level Results
Contracts Critical
High
Medium
Low
Note
Total
0
0
0
0
0
0
Dependencies Critical
High
Medium
Low
Note
Total
0
0
0
0
0
0

For more details view the full report in OpenZeppelin Code Inspector

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

🧹 Nitpick comments (17)
packages/ui/src/components/molecules/editor/rich-text-styles-provider.tsx (1)

4-6: Add support for custom class names

The component currently doesn't allow for customization beyond the default "prose prose-sm" classes. Consider extending the component to accept custom classes:

-export const RichTextStylesProvider = ({ children }: PropsWithChildren) => {
-  return <div className={cn("prose prose-sm")}>{children}</div>;
+export const RichTextStylesProvider = ({ 
+  children, 
+  className 
+}: PropsWithChildren<{ className?: string }>) => {
+  return <div className={cn("prose prose-sm", className)}>{children}</div>;
apps/core/app/test/page.tsx (1)

9-9: Consider adding proper state management instead of console.log

While console.log is acceptable for testing, consider implementing proper state management for the component:

-  onChange={(html, text) => console.log(html, text)}
+  onChange={(html, text) => {
+    // Store in state or perform actions with the content
+    setEditorContent(html);
+  }}
packages/ui/src/components/molecules/editor/editor-toolbar-button.tsx (1)

17-21: Use the cn utility for improved className handling

Similar to other components in your codebase, consider using the cn utility function to handle class name composition for better readability and consistency:

-  className={`flex items-center gap-1 rounded-md px-1 py-1 text-sm transition-colors ${
-    isActive
-      ? "bg-c1-500 text-white"
-      : " text-foreground hover:bg-gray-800"
-  } ${disabled ? "pointer-events-none cursor-not-allowed" : ""}`}
+  className={cn(
+    "flex items-center gap-1 rounded-md px-1 py-1 text-sm transition-colors",
+    isActive ? "bg-c1-500 text-white" : "text-foreground hover:bg-gray-800",
+    disabled && "pointer-events-none cursor-not-allowed"
+  )}

Don't forget to import the cn utility:

+import { cn } from "~/lib/utils";
packages/ui/src/components/molecules/editor/editor-toolbar-view-set-link.tsx (3)

34-34: Fix typo in className

There's a typo in the className "poooopppper" which appears to be unintentional.

-      <PopoverContent className="poooopppper w-64 p-2">
+      <PopoverContent className="w-64 p-2">

35-41: Enhance form usability with keyboard support

Consider adding keyboard support (e.g., submitting the form when the Enter key is pressed in the input field) to improve accessibility and user experience.

      <Input
        type="url"
        placeholder="Enter URL"
        value={url}
        onChange={(e) => setUrl(e.target.value)}
+       onKeyDown={(e) => {
+         if (e.key === 'Enter') {
+           e.preventDefault();
+           onSubmit();
+         }
+       }}
      />

16-21: Consider adding URL validation

The current implementation doesn't validate if the provided URL is properly formatted before setting the link.

  const onSubmit = () => {
    if (!editor || !url) return;
+   const urlPattern = /^(https?:\/\/)?([\da-z.-]+)\.([a-z.]{2,6})([/\w .-]*)*\/?$/;
+   if (!urlPattern.test(url)) {
+     // Consider using toast.error("Please enter a valid URL");
+     return;
+   }
    editor.chain().focus().extendMarkRange("link").setLink({ href: url }).run();
    setOpen(false);
    setUrl("");
  };
packages/ui/src/components/molecules/editor/editor-toolbar-image.tsx (2)

1-1: Justify or remove eslint disable comment

Please add a comment explaining why the no-restricted-imports eslint rule is disabled or consider addressing the underlying issue.

-/* eslint-disable no-restricted-imports */
+/* eslint-disable no-restricted-imports */ // Needed because we require direct axios import for file upload

16-20: Make upload endpoint configurable

The API endpoint for image uploads is hardcoded, which limits flexibility and complicates testing.

-      const response = await axios.post('/api/upload', formData, {
+      const uploadUrl = process.env.NEXT_PUBLIC_UPLOAD_API_URL || '/api/upload';
+      const response = await axios.post(uploadUrl, formData, {
        headers: { 'Content-Type': 'multipart/form-data' }
      });
apps/storybook/src/stories/editor.stories.tsx (1)

23-29: Add a controlled example story

The current examples only showcase the uncontrolled mode using defaultValue. Consider adding a controlled example using value and onChange to demonstrate both usage patterns.

export const Default: Story = {
  args: {
    label: "Rich Text Editor",
    helperText: "You can enter rich text here.",
    defaultValue: "<p>Initial content</p>",
  },
};

+export const Controlled: Story = {
+  render: () => {
+    const [value, setValue] = React.useState("<p>Controlled content</p>");
+    return (
+      <RichTextEditor
+        label="Controlled Rich Text Editor"
+        helperText="This editor uses controlled value state."
+        value={value}
+        onChange={(html) => setValue(html)}
+      />
+    );
+  }
+};
packages/ui/src/components/molecules/editor/editor-toolbar.tsx (4)

1-22: Remove unused icon imports

Several icon imports are not used in the component implementation:

  • Link2Off, List, Quote, Underline, Heading1, Heading5, Heading6

This can reduce bundle size and improve code cleanliness.

import {
  Bold,
  Italic,
-  Link2Off,
  List,
  ListOrdered,
-  Quote,
  Redo,
  Strikethrough,
-  Underline,
  Undo,
-  Heading1,
  Heading2,
  Heading3,
  Heading4,
-  Heading6,
-  Heading5,
  ChevronsLeftRightEllipsis,
  Code,
} from "lucide-react";

24-28: Remove unused Separator import

The Separator component is imported but not used in the EditorToolbar.

-import { Separator } from "./../../atoms/separator";
import { EditorToolbarButton } from "./editor-toolbar-button";

63-64: Remove or implement "to do normal text" comment

This TODO comment should either be implemented or removed.

-      {/* to do normal text */}
+      {/* Typography controls */}

86-99: Remove or implement commented-out heading levels

There are commented-out heading buttons (levels 5 and 6) that should either be implemented or removed to maintain clean code.

Either remove these commented blocks entirely or uncomment and implement them if these heading levels are needed.

packages/ui/src/components/molecules/editor/editor.tsx (4)

8-22: Inconsistent imports and commented code

There are a few issues with the imports:

  1. There are commented-out imports (BubbleMenu, FloatingMenu) that should be removed if not used
  2. There's a commented-out import for RichTextStylesProvider on line 22 while the same component is imported on line 8 with a different path

Clean up the imports for better maintainability.

import { RichTextStylesProvider } from "@repo/ui/components/rich-text-style-provider";
import Image from "@tiptap/extension-image";
import Link from "@tiptap/extension-link";
import TextAlign from "@tiptap/extension-text-align";
import {
-  // BubbleMenu,
  EditorContent,
-  // FloatingMenu,
  useEditor,
} from "@tiptap/react";
import StarterKit from "@tiptap/starter-kit";
import { cva } from "class-variance-authority";
import { LabelWraper } from "../label-wrapper";
import { EditorToolbar } from "./editor-toolbar";
-// import { RichTextStylesProvider } from "@/components/providers/rich-text-style-provider";

61-65: Review commented-out extension

There's a commented-out Highlight extension. Either implement it or remove the comment for cleaner code.

extensions: [
  StarterKit,
  TextAlign.configure({ types: ["heading", "paragraph"] }),
-  // Highlight,
  Image,
  Link,
],

58-78: Configure link extension for better UX

The Link extension is added but lacks configuration for important UX aspects like opening links in new tabs by default and allowing link editing.

Consider configuring the Link extension with better defaults:

extensions: [
  StarterKit,
  TextAlign.configure({ types: ["heading", "paragraph"] }),
  Image,
-  Link,
+  Link.configure({
+    openOnClick: false,
+    linkOnPaste: true,
+    HTMLAttributes: {
+      rel: 'noopener noreferrer',
+      target: '_blank',
+    },
+  }),
],

80-84: Improve HTML content update method

The updateEditorFromHtml method only runs if showHtml is true, but should ideally trigger an update regardless of the current view mode to ensure content consistency.

const updateEditorFromHtml = () => {
-  if (editor && showHtml) {
+  if (editor) {
    editor.commands.setContent(htmlContent, false);
  }
};
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between e230944 and 9bbb32b.

⛔ Files ignored due to path filters (1)
  • bun.lockb is excluded by !**/bun.lockb
📒 Files selected for processing (16)
  • apps/core/app/test/page.tsx (1 hunks)
  • apps/storybook/src/stories/editor.stories.tsx (1 hunks)
  • package.json (1 hunks)
  • packages/ui/package.json (1 hunks)
  • packages/ui/src/components/atoms/command.tsx (1 hunks)
  • packages/ui/src/components/atoms/rich-text-style-provider.tsx (1 hunks)
  • packages/ui/src/components/atoms/textarea.tsx (2 hunks)
  • packages/ui/src/components/molecules/editor/editor-toolbar-button.tsx (1 hunks)
  • packages/ui/src/components/molecules/editor/editor-toolbar-image.tsx (1 hunks)
  • packages/ui/src/components/molecules/editor/editor-toolbar-view-html.tsx (1 hunks)
  • packages/ui/src/components/molecules/editor/editor-toolbar-view-set-link.tsx (1 hunks)
  • packages/ui/src/components/molecules/editor/editor-toolbar.tsx (1 hunks)
  • packages/ui/src/components/molecules/editor/editor.tsx (1 hunks)
  • packages/ui/src/components/molecules/editor/rich-text-styles-provider.tsx (1 hunks)
  • packages/ui/src/styles/globals.scss (1 hunks)
  • packages/ui/tailwind.config.ts (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (4)
packages/ui/src/components/molecules/editor/rich-text-styles-provider.tsx (1)
packages/ui/src/lib/utils.ts (1)
  • cn (4-6)
packages/ui/src/components/molecules/editor/editor-toolbar-image.tsx (2)
packages/ui/src/components/molecules/editor/editor-toolbar.tsx (1)
  • ToolbarProps (30-35)
packages/ui/src/components/molecules/editor/editor-toolbar-button.tsx (1)
  • EditorToolbarButton (1-26)
apps/storybook/src/stories/editor.stories.tsx (1)
packages/ui/src/components/molecules/editor/editor.tsx (1)
  • RichTextEditor (45-147)
packages/ui/src/components/molecules/editor/editor-toolbar.tsx (1)
packages/ui/src/components/molecules/editor/editor-toolbar-button.tsx (1)
  • EditorToolbarButton (1-26)
🔇 Additional comments (8)
packages/ui/tailwind.config.ts (1)

189-189: Added Tailwind Typography plugin
The @tailwindcss/typography plugin has been correctly introduced to enhance rich text styling.

packages/ui/src/components/atoms/textarea.tsx (1)

1-1: Trailing semicolons and commas for consistency
The added semicolons after imports, return JSX, function definitions, and exports, as well as the trailing comma in the cn call, align with the project's style guide.

Also applies to: 14-14, 19-19, 21-21, 24-24

packages/ui/src/styles/globals.scss (2)

234-237: Remove default ProseMirror focus outline
Applying outline: none !important; ensures that focus styles are controlled via Tailwind utilities or custom styles.


239-241: Verify CSS variable usage for selected node outline
The rule uses var(--c1-400), but this variable isn't defined in this file. Please confirm that --c1-400 is provided by imported design-system styles or update it to a valid CSS variable (e.g., --primary-400).

package.json (1)

4-10: Reorder configuration fields for clarity
Moving devDependencies, engines, packageManager, and private closer to the top improves readability without altering functionality.

Also applies to: 11-13, 15-16

packages/ui/package.json (1)

52-68: Added Tiptap and Typography dependencies
The new dependencies (@tailwindcss/typography and @tiptap/*) support the rich text editor features. Versions align with existing setup.

packages/ui/src/components/molecules/editor/editor-toolbar-view-html.tsx (1)

5-27: Looks good!

The component is well-structured and correctly implements the HTML view toggle functionality. It properly synchronizes the HTML content state when switching between views.

apps/storybook/src/stories/editor.stories.tsx (1)

4-47: Good job with story variations and controls!

The different story variations (Default, WithError, CustomClass) provide good coverage of the component's features and demonstrate proper usage patterns with meaningful examples.


import * as React from "react"
import { type DialogProps } from "@radix-ui/react-dialog"

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

Critical: Missing import for DialogProps.
The CommandDialogProps interface extends DialogProps, but the import for that type was removed in this hunk. This will cause unresolved type errors. Please re-add the correct import, for example:

+import type { DialogProps } from "@repo/ui/components/ui/dialog"

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


import { cn } from "@repo/ui/lib/utils"
import { Dialog, DialogContent } from "@repo/ui/components/ui/dialog"

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

Critical: Missing imports for Dialog and DialogContent.
The CommandDialog component still references <Dialog> and <DialogContent>, but those components were removed from the import list. This will break the build. Please restore their import from the appropriate module, for example:

+import { Dialog, DialogContent } from "@repo/ui/components/ui/dialog"

Or, if the dialog wrapper is no longer needed, remove/replace the CommandDialog implementation accordingly.

📝 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 { Dialog, DialogContent } from "@repo/ui/components/ui/dialog"

Comment on lines +4 to +6
export const RichTextStylesProvider = ({ children }: PropsWithChildren) => {
return <div className={cn("prose prose-sm")}>{children}</div>;
};
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

Consider potential duplication with the atomic component

I notice there's another RichTextStylesProvider component in packages/ui/src/components/atoms/rich-text-style-provider.tsx with different styling implementation. This could cause confusion for developers. Consider:

  1. Using a different name for one of these components
  2. Consolidating them into a single, more configurable component
  3. Clearly documenting the purpose of each if they're intended to serve different use cases

Comment on lines +8 to +14
<RichTextEditor
onChange={(html, text) => console.log(html, text)}
defaultValue="hello"
value="salam"
label="badry"
helperText="ali"
/>
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

Mixing controlled and uncontrolled props can cause unpredictable behavior

The RichTextEditor component is currently receiving both value (controlled) and defaultValue (uncontrolled) props simultaneously. React components should typically be either fully controlled or uncontrolled, not both.

Choose one approach:

  • For controlled: Provide value and onChange
  • For uncontrolled: Provide only defaultValue
 <RichTextEditor
   onChange={(html, text) => console.log(html, text)}
   defaultValue="hello"
-  value="salam"
   label="badry"
   helperText="ali"
 />
📝 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
<RichTextEditor
onChange={(html, text) => console.log(html, text)}
defaultValue="hello"
value="salam"
label="badry"
helperText="ali"
/>
<RichTextEditor
onChange={(html, text) => console.log(html, text)}
defaultValue="hello"
label="badry"
helperText="ali"
/>

Comment on lines +3 to +10
export const RichTextStylesProvider = ({ children }: PropsWithChildren) => {
return (
// INFO: "prose" class has max-width and we should remove it by max-w-none
<div className="prose prose-sm max-w-none prose-h2:mb-1 prose-h2:mt-1 prose-h3:mt-1 prose-h4:mt-1 prose-p:my-0 prose-p:mb-1 prose-ul:my-1 prose-ul:list-none prose-ul:pl-1 prose-hr:my-5 prose:text-froground dark:prose-invert">
{children}
</div>
);
};
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

Resolve duplication with the molecular component

There are two components named RichTextStylesProvider in different directories with different styling implementations:

  1. This one in atoms with extensive styling
  2. Another in molecules/editor with minimal styling

This duplication makes maintenance more difficult and could confuse developers. Consider:

  1. Merging them into one configurable component
  2. Renaming one of them to better reflect its purpose
  3. Adding JSDoc comments to clarify the intended use case for each

Comment on lines +26 to +28
import { EditorToolbarImage } from "./editor-toolbar-image";
import { EditorToolbarViewHtml } from "./editor-toolbar-view-html";
import { EditorToolbarSetLink } from "./editor-toolbar-view-set-link";
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

Missing toolbar components in implementation

Several toolbar components are imported but not used in the render method:

  • EditorToolbarImage
  • EditorToolbarViewHtml
  • EditorToolbarSetLink

This suggests missing functionality for image uploading, HTML view toggling, and link insertion, which are typically essential for a complete rich text editor.

Consider adding these components to the toolbar implementation:

<div className="flex flex-wrap gap-2 rounded-md p-2 shadow">
  {/* Existing toolbar buttons */}
  
  {/* Add the missing toolbar components */}
+  <Separator orientation="vertical" className="mx-1 h-6" />
+  <EditorToolbarSetLink editor={editor} disabled={showHtml} />
+  <EditorToolbarImage editor={editor} disabled={showHtml} />
+  <EditorToolbarViewHtml showHtml={showHtml} setShowHtml={setShowHtml} setHtmlContent={setHtmlContent} editor={editor} />
</div>

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

Comment on lines +46 to +146
<div className="flex flex-wrap gap-2 rounded-md p-2 shadow">
{/* Typography */}
<EditorToolbarButton
disabled={showHtml}
onClick={() => editor.chain().focus().undo().run()}
isActive={false}
icon={Undo}
label="Undo"
/>
<EditorToolbarButton
disabled={showHtml}
onClick={() => editor.chain().focus().redo().run()}
isActive={false}
icon={Redo}
label="Redo"
/>

{/* to do normal text */}

<EditorToolbarButton
disabled={showHtml}
onClick={() => editor.chain().focus().toggleHeading({ level: 2 }).run()}
isActive={editor.isActive("heading2")}
icon={Heading2}
label="heading 2"
/>
<EditorToolbarButton
disabled={showHtml}
onClick={() => editor.chain().focus().toggleHeading({ level: 3 }).run()}
isActive={editor.isActive("heading3")}
icon={Heading3}
label="heading 3"
/>
<EditorToolbarButton
disabled={showHtml}
onClick={() => editor.chain().focus().toggleHeading({ level: 4 }).run()}
isActive={editor.isActive("heading4")}
icon={Heading4}
label="heading 4"
/>
{/* <EditorToolbarButton
disabled={showHtml}
onClick={() => editor.chain().focus().toggleHeading({ level: 5 }).run()}
isActive={editor.isActive("heading5")}
icon={Heading5}
label="heading 5"
/>
<EditorToolbarButton
disabled={showHtml}
onClick={() => editor.chain().focus().toggleHeading({ level: 6 }).run()}
isActive={editor.isActive("heading6")}
icon={Heading6}
label="heading 5"
/> */}

<EditorToolbarButton
disabled={showHtml}
onClick={() => editor.chain().focus().toggleBold().run()}
isActive={editor.isActive("bold")}
icon={Bold}
label="Bold"
/>
<EditorToolbarButton
disabled={showHtml}
onClick={() => editor.chain().focus().toggleItalic().run()}
isActive={editor.isActive("italic")}
icon={Italic}
label="Italic"
/>

<EditorToolbarButton
disabled={showHtml}
onClick={() => editor.chain().focus().toggleStrike().run()}
isActive={editor.isActive("strike")}
icon={Strikethrough}
label="Strike"
/>
{/* to do code text */}
<EditorToolbarButton
disabled={showHtml}
onClick={() => editor.chain().focus().toggleCodeBlock().run()}
isActive={editor.isActive("codeBlock")}
icon={ChevronsLeftRightEllipsis}
label="Strike"
/>

<EditorToolbarButton
disabled={showHtml}
onClick={() => editor.chain().focus().toggleOrderedList().run()}
isActive={editor.isActive("orderedList")}
icon={ListOrdered}
label="Ordered List"
/>
<EditorToolbarButton
disabled={showHtml}
onClick={() => editor.chain().focus().toggleCode().run()}
isActive={editor.isActive("code")}
icon={Code}
label="Code"
/>
</div>
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

Missing essential formatting options

The toolbar lacks several essential rich text formatting options:

  1. Bullet lists (unordered lists)
  2. Blockquote
  3. Text alignment options
  4. Link insertion/removal

These are standard features in rich text editors and would enhance the usability of the component.

Add the missing formatting options:

+ <EditorToolbarButton
+   disabled={showHtml}
+   onClick={() => editor.chain().focus().toggleBulletList().run()}
+   isActive={editor.isActive("bulletList")}
+   icon={List}
+   label="Bullet List"
+ />

+ <EditorToolbarButton
+   disabled={showHtml}
+   onClick={() => editor.chain().focus().toggleBlockquote().run()}
+   isActive={editor.isActive("blockquote")}
+   icon={Quote}
+   label="Blockquote"
+ />

Comment on lines +128 to +131
onChange={(e) => {
setHtmlContent(e.target.value);
onChange?.(e.target.value, "");
}} // Update state
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

Incorrect text value in HTML mode onChange handler

When the HTML content changes in the source editor, the onChange function is called with an empty string as the text value. This should be extracted from the HTML content to provide consistent behavior.

onChange={(e) => {
  setHtmlContent(e.target.value);
-  onChange?.(e.target.value, "");
+  // Extract text from HTML to maintain consistent behavior
+  const tempDiv = document.createElement('div');
+  tempDiv.innerHTML = e.target.value;
+  const textContent = tempDiv.textContent || tempDiv.innerText || '';
+  onChange?.(e.target.value, textContent);
}}
📝 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
onChange={(e) => {
setHtmlContent(e.target.value);
onChange?.(e.target.value, "");
}} // Update state
onChange={(e) => {
setHtmlContent(e.target.value);
// Extract text from HTML to maintain consistent behavior
const tempDiv = document.createElement('div');
tempDiv.innerHTML = e.target.value;
const textContent = tempDiv.textContent || tempDiv.innerText || '';
onChange?.(e.target.value, textContent);
}} // Update state

Comment on lines +107 to +112
<EditorToolbar
editor={editor}
setShowHtml={setShowHtml}
showHtml={showHtml}
setHtmlContent={setHtmlContent}
/>
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Missing toolbar functionality implementation

The EditorToolbar is used but based on the editor-toolbar.tsx file, it doesn't include implementation for image uploading, link insertion, or HTML view toggling despite importing these components.

Ensure that all necessary toolbar features are properly exposed and implemented.


🏁 Script executed:

#!/bin/bash
# Check if the toolbar components are actually used somewhere else

echo "Checking usage of toolbar components..."
rg -A 5 "EditorToolbarImage|EditorToolbarViewHtml|EditorToolbarSetLink" --type tsx

Length of output: 201


🏁 Script executed:

#!/bin/bash
# Locate and display the EditorToolbar implementation to verify the imported but unused components
file=$(find . -type f -iname "editor-toolbar.tsx" -print -quit)
echo "Found EditorToolbar file at: $file"
echo "---- File Content Start ----"
sed -n '1,200p' "$file"
echo "---- File Content End ----"

Length of output: 4669


Implement missing image upload, link insertion, and HTML‐view toggles in the toolbar

The EditorToolbar component imports EditorToolbarImage, EditorToolbarSetLink, and EditorToolbarViewHtml but never renders them—so users can’t insert images, links, or toggle HTML view. Please update:

• File: packages/ui/src/components/molecules/editor/editor-toolbar.tsx
– Inside the <div className="flex …"> add the three imported components, for example:

   {/* existing buttons… */}

+  <Separator />
+  <EditorToolbarSetLink
+    disabled={showHtml}
+    editor={editor}
+  />
+  <EditorToolbarImage
+    disabled={showHtml}
+    editor={editor}
+  />
+  <Separator />
+  <EditorToolbarViewHtml
+    showHtml={showHtml}
+    setShowHtml={setShowHtml}
+    setHtmlContent={setHtmlContent}
+  />

Ensure you import Separator (if needed) and pass the correct props so each feature is activated when clicked.

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

Comment on lines +116 to +142
<RichTextStylesProvider>
<div
className={cn(
" flex min-h-[200px] flex-col bg-background ",

className,
)}
>
{showHtml ? (
<Textarea
className="flex-1 border-none"
value={htmlContent}
onChange={(e) => {
setHtmlContent(e.target.value);
onChange?.(e.target.value, "");
}} // Update state
onBlur={updateEditorFromHtml} // Update editor on blur
/>
) : (
<>
<RichTextStylesProvider>
<EditorContent className="p-2" editor={editor} />
</RichTextStylesProvider>
</>
)}
</div>
</RichTextStylesProvider>
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

Nested RichTextStylesProvider components

There are two nested instances of RichTextStylesProvider (lines 116 and 136), which is redundant and may cause styling issues.

Remove the nested provider:

<div>
  <RichTextStylesProvider>
    <div
      className={cn(
        " flex min-h-[200px] flex-col bg-background ",
        className,
      )}
    >
      {showHtml ? (
        <Textarea
          className="flex-1 border-none"
          value={htmlContent}
          onChange={(e) => {
            setHtmlContent(e.target.value);
            onChange?.(e.target.value, "");
          }}
          onBlur={updateEditorFromHtml}
        />
      ) : (
        <>
-          <RichTextStylesProvider>
            <EditorContent className="p-2" editor={editor} />
-          </RichTextStylesProvider>
        </>
      )}
    </div>
  </RichTextStylesProvider>
</div>

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.

2 participants