bito-code-review[bot] commented on code in PR #44858:
URL: https://github.com/apache/superset/pull/44858#discussion_r4159084924


##########
superset-frontend/src/explore/components/controls/TextAreaControl.test.tsx:
##########
@@ -61,4 +62,143 @@ describe('TextArea', () => {
     fireEvent.change(textArea, { target: { value: 'x' } });
     expect(defaultProps.onChange).toHaveBeenCalledWith('x');
   });
+
+  test('picks up an externally changed initialValue without remounting the 
AceEditor', async () => {
+    function Wrapper() {
+      const [initialValue, setInitialValue] = useState('first');
+      return (
+        <>
+          <button
+            type="button"
+            onClick={() => setInitialValue('synced-from-source')}
+          >
+            sync
+          </button>
+          <TextAreaControl
+            name="expr"
+            language="sql"
+            initialValue={initialValue}
+            onChange={() => {}}
+          />
+        </>
+      );
+    }
+    const { container } = render(<Wrapper />);
+    await waitFor(() => {
+      expect(container.querySelector('.ace_text-input')).toBeInTheDocument();
+    });
+    // react-ace keeps a reference to the live editor instance on the
+    // container node's `env`, which lets us read the underlying Ace
+    // document directly; jsdom doesn't paint Ace's text layer, so the
+    // rendered DOM has no visible text to assert against.
+    const editorNode = container.querySelector('.ace_editor') as HTMLElement & 
{
+      env: { editor: { getValue: () => string } };
+    };
+    expect(editorNode.env.editor.getValue()).toBe('first');
+
+    fireEvent.click(screen.getByText('sync'));
+
+    await waitFor(() => {
+      expect(editorNode.env.editor.getValue()).toBe('synced-from-source');
+    });
+    // Same editor instance picked up the new value; it was not remounted.
+    expect(container.querySelector('.ace_editor')).toBe(editorNode);
+  });
+
+  test('does not re-notify onChange when an external sync sets the editor 
value', async () => {
+    const onChange = jest.fn();
+    function Wrapper() {
+      const [initialValue, setInitialValue] = useState('first');
+      return (
+        <>
+          <button
+            type="button"
+            onClick={() => setInitialValue('synced-from-source')}
+          >
+            sync
+          </button>
+          <TextAreaControl
+            name="expr"
+            language="sql"
+            initialValue={initialValue}
+            onChange={onChange}
+          />
+        </>
+      );
+    }
+    const { container } = render(<Wrapper />);
+    await waitFor(() => {
+      expect(container.querySelector('.ace_text-input')).toBeInTheDocument();
+    });
+    const editorNode = container.querySelector('.ace_editor') as HTMLElement & 
{
+      env: { editor: { getValue: () => string } };
+    };
+
+    fireEvent.click(screen.getByText('sync'));
+
+    await waitFor(() => {
+      expect(editorNode.env.editor.getValue()).toBe('synced-from-source');
+    });
+    // Ace's setValue() fires its own change event; that echo must not be
+    // mistaken for the user typing and bounced back up through onChange.
+    expect(onChange).not.toHaveBeenCalled();
+  });
+
+  test('keeps syncing the inline editor after the edit-in-modal dialog is 
opened and closed', async () => {
+    function Wrapper() {
+      const [initialValue, setInitialValue] = useState('first');
+      return (
+        <>
+          <button
+            type="button"
+            onClick={() => setInitialValue('synced-from-source')}
+          >
+            sync
+          </button>
+          <TextAreaControl
+            name="expr"
+            language="sql"
+            initialValue={initialValue}
+            onChange={() => {}}
+          />
+        </>
+      );
+    }

Review Comment:
   <div>
   
   
   <div id="suggestion">
   <div id="issue"><b>Triplicated test Wrapper</b></div>
   <div id="fix">
   
   Tests at lines 66, 108, and 147 each re-declare a near-identical `Wrapper` 
(lines 67-85, 110-128, 148-166); only the `onChange` prop differs (`() => {}` 
vs the `onChange` mock). Extract one shared `Wrapper` taking `onChange` so 
future prop changes to `TextAreaControl` update a single site instead of three 
copies drifting apart.
   </div>
   
   
   </div>
   
   
   
   
   <small><i>Code Review Run #3966de</i></small>
   </div>
   
   ---
   Should Bito avoid suggestions like this for future reviews? (<a 
href=https://alpha.bito.ai/home/ai-agents/review-rules>Manage Rules</a>)
   - [ ] Yes, avoid them



##########
superset-frontend/src/explore/components/controls/TextAreaControl.tsx:
##########
@@ -157,6 +175,27 @@ function TextAreaControl({
     [hotkeys],
   );
 
+  // Pick up an `initialValue` change that didn't originate from this
+  // editor's own typing (handleChange above would have already updated
+  // lastEditorValueRef for that case), without remounting the Ace instance
+  // or losing the user's current cursor position.
+  useEffect(() => {
+    const nextValue = initialValue ?? value;
+    const editor = editorRef.current;
+    if (
+      editor &&
+      nextValue !== undefined &&
+      nextValue !== lastEditorValueRef.current &&
+      nextValue !== editor.getValue()
+    ) {
+      const cursorPos = editor.getCursorPosition();
+      editor.setValue(nextValue, 1);
+      editor.clearSelection();
+      editor.moveCursorToPosition(cursorPos);
+      lastEditorValueRef.current = nextValue;
+    }
+  }, [initialValue, value]);

Review Comment:
   <div>
   
   
   <div id="suggestion">
   <div id="issue"><b>shared editorRef syncs wrong editor</b></div>
   <div id="fix">
   
   `onEditorLoad` (line 166) sets the single `editorRef.current`, but both the 
main editor (line 307) and the modal editor (line 298) pass it. antd `Modal` 
mounts children lazily, so after the user opens 'Edit in modal' once, the modal 
editor's `onLoad` overwrites `editorRef`. The new sync `useEffect` then calls 
`setValue` on the hidden modal editor, leaving the visible main editor stale. 
Track the two editors separately and sync the main one.
   </div>
   
   
   </div>
   
   
   
   
   <small><i>Code Review Run #6ca0d0</i></small>
   </div><div>
   
   
   <div id="suggestion">
   <div id="issue"><b>Magic sentinel in setValue</b></div>
   <div id="fix">
   
   `setValue(nextValue, 1)` passes Ace's cursor-placement sentinel as a bare 
literal; its meaning is only recoverable from Ace's API docs, while the 
surrounding comment describes the intent in prose. Extract it to a named 
constant so the call site reads without external documentation and cannot be 
casually edited.
   </div>
   
   
   </div>
   
   
   
   
   <small><i>Code Review Run #3966de</i></small>
   </div>
   
   ---
   Should Bito avoid suggestions like this for future reviews? (<a 
href=https://alpha.bito.ai/home/ai-agents/review-rules>Manage Rules</a>)
   - [ ] Yes, avoid them



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to