Skip to content

Conversation

@Palanikannan1437
Copy link
Member

@Palanikannan1437 Palanikannan1437 commented Jan 19, 2026

Description

Adds unique key props to all NodeViewWrapper components in the editor package to ensure stable React rendering and prevent unnecessary re-mounts during editor updates.

Problem:

TipTap NodeViewWrapper components were missing stable key props, which could cause React to re-mount components unnecessarily during re-renders, leading to loss of component state, unnecessary DOM mutations, and potential visual flickering.

Solution:

  1. Added key prop using existing node.attrs.id to all NodeViewWrapper components
  2. Created typed enums (ECodeBlockAttributeNames, EWorkItemEmbedAttributeNames, EPageEmbedAttributeNames) for type-safe attribute access
  3. Added ID attribute to ECalloutAttributeNames enum
  4. Updated all node.attrs.id usages to use typed enum accessors to avoid ESLint "unsafe assignment of any value" errors
  5. Fixed various ESLint warnings including accessibility improvements, floating promises, and proper type assertions

Files affected:

Core extensions: callout, code-block, custom-image, mentions, work-item-embed

Type of Change

[x] Improvement (change that would cause existing functionality to not work as expected)
[x] Code refactoring
[x] Performance improvements

Screenshots and Media (if applicable)

N/A - Internal rendering optimization with no visible UI changes

Test Scenarios

Verified all editor extensions render correctly with unique keys

Summary by CodeRabbit

  • Improvements

    • Improved rendering stability across editor components by stabilizing element identity.
    • Enhanced image handling: better error recovery flow, added alt text, and improved touch-device behavior.
    • More reliable copy-to-clipboard interaction for code blocks.
  • Bug Fixes

    • Fixed UI reconciliation issues that could cause unexpected re-renders for mentions, callouts, embeds, and custom images.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 19, 2026

Warning

Rate limit exceeded

@Palanikannan1437 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 15 minutes and 49 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 6649299 and 0d5f747.

📒 Files selected for processing (1)
  • packages/editor/src/core/extensions/custom-image/components/block.tsx
📝 Walkthrough

Walkthrough

Adds centralized, typed attribute enums and attribute fields across multiple editor extensions, replaces string literal attribute access with enum-based keys (including React keys), and refactors custom-image error/restoration flow while adding an empty alt attribute for images.

Changes

Cohort / File(s) Summary
Callout Extension
packages/editor/src/core/extensions/callout/types.ts, packages/editor/src/core/extensions/callout/utils.ts, packages/editor/src/core/extensions/callout/block.tsx
Added ID = "id" enum member and `[ECalloutAttributeNames.ID]: string
Code Extension
packages/editor/src/core/extensions/code/types.ts, packages/editor/src/core/extensions/code/code-block-node-view.tsx
New ECodeBlockAttributeNames enum and TCodeBlockAttributes type (ID, LANGUAGE); node view imports types, reads attrs, and sets key={attrs[ECodeBlockAttributeNames.ID]}; copyToClipboard click wrapped with void.
Custom-Image Extension
packages/editor/src/core/extensions/custom-image/components/block.tsx, packages/editor/src/core/extensions/custom-image/components/node-view.tsx
Switched to ECustomImageAttributeNames.ID for ID reads and props; added alt="" on <img>; refactored onError to an async guarded flow that attempts restoration, updates error/restore flags, and refreshes src for touch devices; safer isTouchDevice and maxFileSize access; added component key.
Mentions Extension
packages/editor/src/core/extensions/mentions/mention-node-view.tsx
Added React key to NodeViewWrapper using attrs[EMentionComponentAttributeNames.ID].
Work-Item-Embed Extension
packages/editor/src/core/extensions/work-item-embed/types.ts, packages/editor/src/core/extensions/work-item-embed/extension.tsx
New EWorkItemEmbedAttributeNames enum and TWorkItemEmbedAttributes type; renderer now casts node.attrs to typed attrs, reads values via enum keys, and sets key={attrs[EWorkItemEmbedAttributeNames.ID]}.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant Img as Image Element
participant NV as NodeView / Component
participant RS as RestorationService
participant ES as EditorStorage
participant UI as ImageUploadStatus / UI

Img->>NV: onError event
NV->>NV: check hasTriedRestoringImageOnce / hasErroredOnFirstLoad
NV->>RS: attemptRestore(node.attrs[id], src)
RS-->>NV: restore result (success/failure)
NV->>ES: query isTouchDevice (if needed)
alt restore success
NV->>Img: update src / refresh
NV->>UI: clear error state
else restore failure
NV->>UI: set error state / show fallback
end

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🐰
I found an ID tucked in a row,
Replaced the strings so keys can grow.
I nibbled bugs, then hopped to mend,
Images heal and components blend.
Hooray — typed attrs, a tidy end! 🥕

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'fix: node view renders' is vague and does not clearly convey the main change, which involves adding stable React keys to NodeViewWrapper components and introducing typed attribute enums. Provide a more specific title that describes the primary change, such as 'fix: add stable React keys to NodeViewWrapper components' or 'fix: stabilize node view rendering with keyed elements'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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

❤️ Share

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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@packages/editor/src/core/extensions/custom-image/components/block.tsx`:
- Line 222: The JSX is always rendering an id because of the `?? ""` fallback,
causing duplicate DOM ids when `node.attrs[ECustomImageAttributeNames.ID]` is
missing; update the rendering logic around
`getImageBlockId(node.attrs[ECustomImageAttributeNames.ID] ?? "")` to only pass
an id prop when the attribute is present and non-empty (or ensure a unique id is
generated/assigned beforehand). Locate the usage of `getImageBlockId` in the
component (the JSX id prop) and change it to conditional rendering (omit the id
prop when `node.attrs[ECustomImageAttributeNames.ID]` is falsy) so duplicate
`editor-image-block-` ids are no longer produced.
- Around line 229-235: The interactive image container with role="button" (the
element using containerRef and onKeyDown) is missing an accessible name and
doesn't prevent default browser behavior for Space/Enter; update the element to
provide an accessible name (e.g., add aria-label or aria-labelledby) and modify
the onKeyDown handler (the function that checks e.key === "Enter" || e.key === "
") to call e.preventDefault() before invoking handleImageMouseDown so Space
doesn't scroll the page and the control is announced by screen readers.
🧹 Nitpick comments (6)
packages/editor/src/core/extensions/callout/types.ts (1)

3-12: Prefer erasable attribute-name constants over enum.

Project guidance prefers erasable syntax; adding new enum members entrenches the pattern. Consider moving to a const object + union type and updating usages over time. Based on learnings, ...

♻️ Example direction (requires follow-up updates across callout usage)
-export enum ECalloutAttributeNames {
-  ID = "id",
-  ICON_COLOR = "data-icon-color",
-  ICON_NAME = "data-icon-name",
-  EMOJI_UNICODE = "data-emoji-unicode",
-  EMOJI_URL = "data-emoji-url",
-  LOGO_IN_USE = "data-logo-in-use",
-  BACKGROUND = "data-background",
-  BLOCK_TYPE = "data-block-type",
-}
+export const CALLOUT_ATTRIBUTE_NAMES = {
+  ID: "id",
+  ICON_COLOR: "data-icon-color",
+  ICON_NAME: "data-icon-name",
+  EMOJI_UNICODE: "data-emoji-unicode",
+  EMOJI_URL: "data-emoji-url",
+  LOGO_IN_USE: "data-logo-in-use",
+  BACKGROUND: "data-background",
+  BLOCK_TYPE: "data-block-type",
+} as const;
+export type TCalloutAttributeName =
+  typeof CALLOUT_ATTRIBUTE_NAMES[keyof typeof CALLOUT_ATTRIBUTE_NAMES];

Also applies to: 24-30

packages/editor/src/core/extensions/code/types.ts (1)

1-9: Prefer erasable const objects over enums for attribute names.

The repo guidance discourages enums in TS. Consider a const object + union type to avoid runtime enum emission and keep types erasable.

♻️ Proposed refactor
-export enum ECodeBlockAttributeNames {
-  ID = "id",
-  LANGUAGE = "language",
-}
+export const CODE_BLOCK_ATTRIBUTE_NAMES = {
+  ID: "id",
+  LANGUAGE: "language",
+} as const;
+
+export type TCodeBlockAttributeName =
+  (typeof CODE_BLOCK_ATTRIBUTE_NAMES)[keyof typeof CODE_BLOCK_ATTRIBUTE_NAMES];

 export type TCodeBlockAttributes = {
-  [ECodeBlockAttributeNames.ID]: string | null;
-  [ECodeBlockAttributeNames.LANGUAGE]: string | null;
+  [CODE_BLOCK_ATTRIBUTE_NAMES.ID]: string | null;
+  [CODE_BLOCK_ATTRIBUTE_NAMES.LANGUAGE]: string | null;
 };

Based on learnings, avoid enums if the project prefers erasable syntax.

packages/editor/src/core/extensions/work-item-embed/types.ts (1)

1-15: Prefer erasable const object + mapped type over enum.

This repo’s TS guidance favors erasable syntax; the enum will emit runtime code. Consider replacing it with a const object and a mapped type to keep types-only. Based on learnings and as per coding guidelines, avoid enums.

♻️ Proposed refactor (erasable types)
-export enum EWorkItemEmbedAttributeNames {
-  ID = "id",
-  ENTITY_IDENTIFIER = "entity_identifier",
-  PROJECT_IDENTIFIER = "project_identifier",
-  WORKSPACE_IDENTIFIER = "workspace_identifier",
-  ENTITY_NAME = "entity_name",
-}
-
-export type TWorkItemEmbedAttributes = {
-  [EWorkItemEmbedAttributeNames.ID]: string | undefined;
-  [EWorkItemEmbedAttributeNames.ENTITY_IDENTIFIER]: string | undefined;
-  [EWorkItemEmbedAttributeNames.PROJECT_IDENTIFIER]: string | undefined;
-  [EWorkItemEmbedAttributeNames.WORKSPACE_IDENTIFIER]: string | undefined;
-  [EWorkItemEmbedAttributeNames.ENTITY_NAME]: string | undefined;
-};
+export const EWorkItemEmbedAttributeNames = {
+  ID: "id",
+  ENTITY_IDENTIFIER: "entity_identifier",
+  PROJECT_IDENTIFIER: "project_identifier",
+  WORKSPACE_IDENTIFIER: "workspace_identifier",
+  ENTITY_NAME: "entity_name",
+} as const satisfies Record<string, string>;
+
+export type TWorkItemEmbedAttributeName =
+  (typeof EWorkItemEmbedAttributeNames)[keyof typeof EWorkItemEmbedAttributeNames];
+
+export type TWorkItemEmbedAttributes =
+  Partial<Record<TWorkItemEmbedAttributeName, string>>;
packages/editor/src/core/extensions/custom-image/components/node-view.tsx (2)

6-6: Prefer const-object/union attribute names over enums (if applicable).
If ECustomImageAttributeNames is a TS enum in ../types, the repo guidance prefers erasable const objects/unions to avoid runtime enum output. Consider switching the definition while keeping usage unchanged.

Based on learnings, the repo prefers const objects/unions over enums.


74-76: Re-check the exhaustive-deps suppression.
With getImageSource and getImageDownloadSource explicitly listed, confirm the lint disable is still required; remove it if the rule passes to keep hooks linting active.

As per coding guidelines, keep ESLint rules enabled where possible.

packages/editor/src/core/extensions/custom-image/components/block.tsx (1)

250-286: Log the caught error for diagnostics.
The catch block drops the actual error; include it so failures are actionable in logs.

♻️ Suggested tweak
-              } catch {
+              } catch (error) {
                 setFailedToLoadImage(true);
-                console.error("Error while loading image", e);
+                console.error("Error while loading image", error, e);
               } finally {

As per coding guidelines, log caught errors with the error object.

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

🤖 Fix all issues with AI agents
In `@packages/editor/src/core/extensions/custom-image/components/block.tsx`:
- Around line 245-276: The catch block in the onError handler for the image
(inside the anonymous async function in onError) currently swallows the thrown
restore error and logs the React event variable e; change the catch to capture
the thrown error (e.g., catch (err)) and log that error with
console.error("Error while loading image", err) while preserving the existing
setFailedToLoadImage(true) behavior; update references to the restore helpers
(extension.options.restoreImage, extension.options.getImageSource) only insofar
as needed to ensure the caught error is the one logged.
♻️ Duplicate comments (1)
packages/editor/src/core/extensions/custom-image/components/block.tsx (1)

222-222: Avoid duplicate DOM ids when the attribute is missing.

Line 222 still falls back to "", which can collapse multiple nodes to the same editor-image-block- id. Prefer omitting the id prop when the attribute is absent (or ensure IDs are always assigned upstream).

@sriramveeraghanta sriramveeraghanta merged commit 20e266c into preview Jan 23, 2026
9 checks passed
@sriramveeraghanta sriramveeraghanta deleted the fix/node-rerenders branch January 23, 2026 08:17
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.

4 participants