-
Notifications
You must be signed in to change notification settings - Fork 4.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
RichText: Fix empty value delete behaviors #8735
Conversation
The failing end-to-end test is legitimate, and indicates that we rely on RichText to remove itself even when calling We seem to be quite inconsistent in how we expect this to work. It doesn't seem like we should want both a merge and remove to happen, but rather one or the other, depending on whether a merge is possible and if the current RichText is empty. Some ideas off the top of my head:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code here makes sense but I'm slightly too tired to offer much more input on
#8735 (comment)
I'll just leave my comments here. 🤷♂️
@@ -41,11 +41,11 @@ Render a rich [`contenteditable` input](https://developer.mozilla.org/en-US/docs | |||
|
|||
### `onMerge( forward: Boolean ): Function` | |||
|
|||
*Optional.* Called when blocks can be merged. `forward` is true when merging with the next block, false when merging with the previous block. | |||
*Optional.* Called when blocks can be merged. `forward` is true when merging with the next block, false when merging with the previous block. The callback should return `true` if a merge is possible, or `false` otherwise. This is used in determining whether an empty field should be deleted instead of merged. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a drive-by review nitpick: "true" should be backtick-wrapped (eg "true
") for improved readability (it's done that way elsewhere).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changes backed out in rebased d29d5d1.
|
||
### `onRemove( forward: Boolean ): Function` | ||
|
||
*Optional.* Called when the block can be removed. `forward` is true when the selection is expected to move to the next block, false to the previous block. | ||
*Optional.* Called when the block can be removed. `forward` is true when the selection is expected to move to the next block, false to the previous block. Called when pressing Backspace or Delete on an empty field and only when `onMerge` explicitly returns `false` to indicate non-merge. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here re: true
and false
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changes backed out in rebased d29d5d1.
@@ -426,41 +427,84 @@ export class RichText extends Component { | |||
} | |||
|
|||
/** | |||
* Handles a keydown event from TinyMCE. | |||
* Handles a delete key down event to handle merge or removal for a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be easier to visually scan for keyDown
over key down
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated in rebased d29d5d1.
@@ -426,41 +427,84 @@ export class RichText extends Component { | |||
} | |||
|
|||
/** | |||
* Handles a keydown event from TinyMCE. | |||
* Handles a delete key down event to handle merge or removal for a | |||
* collapsed selection where caret is at directional edge: forward for a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
An article (eg "the caret") here might improve readability.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added link in rebased d29d5d1.
if ( ! this.props.onMerge && ! this.props.onRemove ) { | ||
return; | ||
} | ||
// Only process delete if occurs at uncollapsed edge. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry to nitpick but this comment is a bit unclear... if what occurs? I guess this should be something like:
// Only process delete even if the deletion occurs at an uncollapsed edge.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated in rebased d29d5d1.
@@ -77,6 +77,34 @@ async function login() { | |||
] ); | |||
} | |||
|
|||
/** | |||
* Returns a promise which resolves once it's determined that the active DOM |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, this write-up is great 👍
Okay, the latest rebased iteration seems to handle all cases. The major change is only handling |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm unclear about:
with a maximum of one removal
in the testing instructions. Does that mean the forward delete key shouldn't trigger multiple empty paragraph deletions? It seems like it should conceptually, and it does:
This works great though, otherwise tested well locally, felt good to use, and tests passed locally for me. I made a tiny tweak (keydown
to keyDown
in a comment because I thought it made it more readable as an event).
🚢
Yep, for example, in the 3.4 release if you had multiple empty paragraphs, hitting (forward) Delete in the first of them would destroy two. In the following recording, I hit Delete once, but two paragraphs are deleted (and the caret randomly moves to the preceding paragraph): (Reiterating, this is recorded on 3.4, intended to be resolved with this pull request) |
Oh wait, okay, I understand the instruction now. I just misinterpreted what you meant. Aces! 👍 |
Fixes #8731
This pull request seeks to resolve two issues related to deletion behaviors on empty RichText (paragraph blocks). The first is described in #8731, the second I'm not sure has yet been surfaced (and exists in the production v3.4 release):
The latter of these is due to the fact that we did not distinguish between
onMerge
andonRemove
in the key handlers of RichText, thus causing both to fire, merging into the one block and then deleting that one block, resulting in the double-deletion. This has been accounted for here by updating the block'sonMerge
toreturn a boolean indicating whether it was successful in its merge. This is interpreted byonly handle removal on the Backspace key (see #8735 (comment)).RichText
to determine whether a removal is warranted, if possibleThe issue causing #8731 is a result of the fact that when focused in an empty RichText, the caret is placed adjacent (before) a "bogus"
br
node inserted by TinyMCE. The implementation of the general purposeisHorizontalEdge
interprets this to mean that the caret is not at the end of the field, thus it would never allow the removal.Implementation notes:
I adopted
waitForRichTextInitialization
from #8306 because I was having issues with failures locally without it. I'm not terribly happy with its inclusion, also because it only works for explicit inserts of the paragraph block, not a new paragraph inserted via Enter or other keyboard interactions.Testing instructions:
Repeat testing instructions from #8731 and the above second set of issue steps, verifying that paragraph merges as expected, with a maximum of one removal.
Ensure end-to-end tests pass: