[lexical-react] Bug Fix: the draggable block handle follows setEditable - #9012
Closed
LeSingh1 wants to merge 1 commit into
Closed
[lexical-react] Bug Fix: the draggable block handle follows setEditable#9012LeSingh1 wants to merge 1 commit into
LeSingh1 wants to merge 1 commit into
Conversation
## Description
`DraggableBlockPlugin_EXPERIMENTAL` takes the editability it renders on straight off the editor's private field, during render, with no subscription:
```tsx
const [editor] = useLexicalComposerContext();
return useDraggableBlockMenu(
editor,
anchorElem,
menuRef,
targetLineRef,
editor._editable, // <- plain field read
...
```
`editor.setEditable(...)` notifies editable listeners; it does not schedule a React render for a component that merely read the field. So the value the plugin renders on is whatever it happened to be at the last render:
- Switching an editor to read-only leaves the drag handle rendered — `{isEditable && menuComponent}` is still true — and fully functional. Dragging still runs `$onDrop`, which calls `targetNode.insertAfter(...)` / `insertBefore(...)`, so a read-only document can be reordered by dragging.
- The reverse also breaks: an editor mounted with `editable: false` and later made editable never grows a handle.
- The Firefox blur guard inside the hook is gated on the same stale value, so it registers against the wrong state.
It is intermittent, which is the usual signature: any unrelated re-render of the plugin's parent silently corrects it until the next toggle.
`@lexical/react` already ships the hook for this, and its doc comment is explicit:
> Get the current value for `LexicalEditor.isEditable` using `useLexicalSubscription`. You should prefer this over manually observing the value with `LexicalEditor.registerEditableListener`, which is a bit tricky to do correctly, particularly when using React StrictMode (the default for development) or concurrency.
Every other editability consumer in the package uses it — `LexicalRichTextPlugin`, `ContentEditableElement`, `LexicalContentEditable` — and `editor._editable` is the only `_editable` reference in `packages/lexical-react/src`. This swaps it for `useLexicalEditable()`. The hook's signature is unchanged (it already takes `isEditable` as a parameter), so this is internal to the exported component; no API or serialization change.
This is reachable in the shipped playground, which renders `DraggableBlockPlugin` unconditionally and has a read-only toggle calling `editor.setEditable(!editor.isEditable())`.
## Test plan
New unit test `packages/lexical-react/src/__tests__/unit/LexicalDraggableBlockPlugin.test.tsx` renders the plugin and toggles editability in both directions.
### Before
```
❯ packages/lexical-react/src/__tests__/unit/LexicalDraggableBlockPlugin.test.tsx (2 tests | 2 failed)
× follows setEditable
AssertionError: expected true to be false // Object.is equality
× renders no drag handle for an editor that mounts read-only
AssertionError: expected false to be true // Object.is equality
Test Files 1 failed (1)
Tests 2 failed (2)
```
### After
```
Test Files 1 passed (1)
Tests 2 passed (2)
```
Package suite is unchanged:
```
$ npx vitest run packages/lexical-react
Test Files 32 passed (32)
Tests 184 passed (184)
```
LeSingh1
requested review from
acywatson,
etrepum,
fantactuka,
ivailop7,
potatowagon and
zurfyx
as code owners
August 9, 2026 05:18
|
@LeSingh1 is attempting to deploy a commit to the Meta Open Source Team on Vercel. A member of the Team first needs to authorize it. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
DraggableBlockPlugin_EXPERIMENTALtakes the editability it renders on straight off the editor's private field, during render, with no subscription:editor.setEditable(...)notifies editable listeners; it does not schedule a React render for a component that merely read the field. So the value the plugin renders on is whatever it happened to be at the last render:{isEditable && menuComponent}is still true — and fully functional. Dragging still runs$onDrop, which callstargetNode.insertAfter(...)/insertBefore(...), so a read-only document can be reordered by dragging.editable: falseand later made editable never grows a handle.It is intermittent, which is the usual signature: any unrelated re-render of the plugin's parent silently corrects it until the next toggle.
@lexical/reactalready ships the hook for this, and its doc comment is explicit:Every other editability consumer in the package uses it —
LexicalRichTextPlugin,ContentEditableElement,LexicalContentEditable— andeditor._editableis the only_editablereference inpackages/lexical-react/src. This swaps it foruseLexicalEditable(). The hook's signature is unchanged (it already takesisEditableas a parameter), so this is internal to the exported component; no API or serialization change.This is reachable in the shipped playground, which renders
DraggableBlockPluginunconditionally and has a read-only toggle callingeditor.setEditable(!editor.isEditable()).Test plan
New unit test
packages/lexical-react/src/__tests__/unit/LexicalDraggableBlockPlugin.test.tsxrenders the plugin and toggles editability in both directions.Before
After
Package suite is unchanged: