Skip to content

Conversation

@anmolsinghbhatia
Copy link
Collaborator

@anmolsinghbhatia anmolsinghbhatia commented Feb 9, 2026

Description

This PR resolves the browser alert that appears when opening or expanding a work item peek view without making any edits.

Type of Change

  • Bug fix

Summary by CodeRabbit

Bug Fixes

  • Rich text editor now efficiently detects when content hasn't changed, preventing unnecessary processing and state updates.
  • Improved auto-save behavior to skip redundant saves when the description remains unchanged.
  • Enhanced stability of empty value handling across different editor states.

@anmolsinghbhatia anmolsinghbhatia self-assigned this Feb 9, 2026
Copilot AI review requested due to automatic review settings February 9, 2026 09:27
@makeplane
Copy link

makeplane bot commented Feb 9, 2026

Linked to Plane Work Item(s)

This comment was auto-generated by Plane

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 9, 2026

📝 Walkthrough

Walkthrough

The change adds content change detection to a rich text editor by introducing a ref that tracks the last saved content state, allowing the component to skip processing onChange events when content hasn't actually changed, thereby optimizing unnecessary state updates and debounced saves.

Changes

Cohort / File(s) Summary
Rich Text Editor Optimization
apps/web/core/components/editor/rich-text/description-input/root.tsx
Introduced lastSavedContent ref to track previously saved editor state and skip onChange processing when incoming content matches saved state. Normalizes empty values to <p></p> placeholder for consistent comparison.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 A rabbit's ode to clever skips

With refs we guard against the flips,
No change? We simply skip and sail,
The editor hops, but guards prevail! 🐇✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically identifies the fix for the workitem description input initial load issue, directly matching the main change.
Description check ✅ Passed The description includes the main objective and identifies it as a bug fix, but omits several template sections like test scenarios, screenshots, and detailed change details.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix-work-item-description-input-inital-load

No actionable comments were generated in the recent review. 🎉

🧹 Recent nitpick comments
apps/web/core/components/editor/rich-text/description-input/root.tsx (1)

115-123: Initial state and lastSavedContent use different normalization than the useEffect reset.

Lines 117 and 134 still use the old initialValue?.trim() ?? "" while the useEffect at line 153 normalizes empty values to "<p></p>". On the first render, if initialValue is "" or undefined, localDescription.description_html will be "" (falsy), rendering <DescriptionInputLoader /> (line 280). The useEffect then immediately sets it to "<p></p>" (truthy), switching to the editor. This causes an unnecessary render cycle and a brief loader flash.

Consider applying the same normalization inline:

Suggested fix
+  const normalizedInitialValue = initialValue?.trim() === "" ? "<p></p>" : (initialValue ?? "<p></p>");
   // states
   const [localDescription, setLocalDescription] = useState<TFormData>({
     id: entityId,
-    description_html: initialValue?.trim() ?? "",
+    description_html: normalizedInitialValue,
     isMigrationUpdate: false,
   });
   // ref to track if there are unsaved changes
   const hasUnsavedChanges = useRef(false);
   // ref to track last saved content (to skip onChange when content hasn't actually changed)
-  const lastSavedContent = useRef(initialValue?.trim() === "" ? "<p></p>" : (initialValue ?? "<p></p>"));
+  const lastSavedContent = useRef(normalizedInitialValue);

And similarly for defaultValues at line 135:

     defaultValues: {
       id: entityId,
-      description_html: initialValue?.trim() ?? "",
+      description_html: normalizedInitialValue,
       isMigrationUpdate: false,
     },

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

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Fixes the “unsaved changes” browser alert triggered on initial open/expand of a work item peek view by preventing autosave/dirty-state logic from firing due to editor normalization on init.

Changes:

  • Track a lastSavedContent ref to represent the currently-saved HTML.
  • Normalize empty initial values to "<p></p>" consistently during reset/init.
  • Skip onChange handling when emitted HTML equals the last saved HTML (to avoid false dirty state on init).
Comments suppressed due to low confidence (1)

apps/web/core/components/editor/rich-text/description-input/root.tsx:235

  • The early return when description_html === lastSavedContent.current skips updating the react-hook-form field and doesn’t cancel any already-scheduled debouncedFormSave(). If a user makes an edit (scheduling a debounced save) and then undoes back to the last-saved content, this handler will return, leaving the form state on the edited value and allowing the pending debounced save to persist the wrong content. Consider always syncing the form state, and when the content matches lastSavedContent.current, cancel the debounced save and reset the unsaved/submitting flags instead of returning before state updates.
              onChange={(_description, description_html, options) => {
                // Skip if content hasn't actually changed (handles editor normalization on init)
                if (description_html === lastSavedContent.current) return;
                setIsSubmitting("submitting");
                onChange(description_html);
                setValue("isMigrationUpdate", options?.isMigrationUpdate ?? false);
                hasUnsavedChanges.current = true;
                debouncedFormSave();

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