draft-js icon indicating copy to clipboard operation
draft-js copied to clipboard

OS X "Add period with double-space" feature breaks Editor if invoked after a decorator

Open thinhngo opened this issue 4 years ago • 10 comments

Reproducible here: https://jsfiddle.net/3o2jnf5k/

Steps to reproduce: 1 - Create a decorated block of text, e.g.: type #tweet in editor 2 - Trigger "Add period with double-space" functionality by double tapping spacebar

Result: Editor is left in a funky state

thinhngo avatar May 11 '20 15:05 thinhngo

I'm seeing a similar issue when I render a custom link component. I've also noticed the issue in the demo example on https://draftjs.org/

Steps to reproduce:

  1. add some text into the editor
  2. add an inline style
  3. Trigger "Add period with double-space" functionality by double tapping spacebar

Result: Editor is left in a funky state

If you then try to edit the content it throws :

Error: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node.

edleeks87 avatar Jun 24 '20 10:06 edleeks87

Currently, I fighting with the same issue. I found that this is related to the OSX keyboard setting. When I turned off "Add full stop with double-space", the issue disappeared.

Anyway, this is not a solution, it is just a clue. I am still looking for a solution.

Screenshot 2020-08-03 at 14 53 06

FilipMessa avatar Aug 03 '20 13:08 FilipMessa

I was able to disable it programmatically in the end by using something similar to the following:

function replaceText(
  editorState,
  text,
  inlineStyle,
  forceSelection,
) {
  const contentState = Modifier.replaceText(
    editorState.getCurrentContent(),
    editorState.getSelection(),
    text,
    inlineStyle,
  );

  return EditorState.push(
    editorState,
    contentState,
    'insert-characters',
    forceSelection,
  );
}

function isASCIIChar(str) {
  return /^\S+$/.test(str);
}

const handleBeforeInput = (str, newState) => {
    if (lastKeyPressed === ' ' && !isASCIIChar(str)) {
      lastKeyPressed = null;
      onChange(replaceText(newState, ' ', newState.getCurrentInlineStyle(), true));

      return 'handled';
    }

    if (str === ' ') {
      lastKeyPressed = str;

      return 'not-handled';
    }
...
  };

edleeks87 avatar Aug 03 '20 13:08 edleeks87

Ah yes, unfortunately I tracked the fix to this error to something that has to be changed in React too, so it'll take a while. It's on the radar though.

mrkev avatar Aug 03 '20 18:08 mrkev

@mrkev Do you know why this happens? Can you explain that? If you don't have the time it's OK, I am just curious. Have a nice day

FilipMessa avatar Aug 04 '20 07:08 FilipMessa

@FilipMessa I think it's probably related to this, since it's a text replacement and it happens when there's decorators, which add DOM nodes (leafs): https://github.com/facebook/draft-js/issues/2171#issuecomment-631749909

mrkev avatar Aug 08 '20 05:08 mrkev

I just want to share my findings that is not yet complete.

I was looking into the component/handlers/edit/editOnInput.js and found that adding a space after double whitespaces has a weird behavior in the underlying editorState's content. In normal case, the DOM text will match with the model text (https://github.com/facebook/draft-js/blob/fb728fde4f041270f2234263a8572a8fe4d50258/src/component/handlers/edit/editOnInput.js#L128) But, the model text with the period after the two whitespaces have mismatched text Attaching screenshot for debug details: My console code: console.log('here', e.nativeEvent, '|' + block.getText() + '|', '|' + domText + '|', '|' + modelText + '|'); Screen Shot 2020-08-28 at 8 42 40 PM You can see the logs for the second space I inserted produced an expected content in the DOM text, but the model text has an extra space after the text.

It looks like during the given second whitespace, the editorState given to editOnInput passes both the first whitespace + the result of the second space (a period and a leading whitespace) I'm not sure why there is an "apply-entity" change type after the second space Then, I'm also not sure why adding another character makes the model text return to the correct one.

Edit: we may be able to check if the input event is inserting a . and replace the last white space with . .

kelvien avatar Aug 29 '20 00:08 kelvien

Adding more details on my findings at my prior post, I found that the content is also handled by the handleBeforeInput. So, to "disable" OS's behavior in replacing 2 whitespaces ( ) with a period and a whitespace (. ), we handle the input prior to handling the input by checking if the event is trying to insert a special character (. ) and replace it with a whitespace instead.

handleBeforeInput={(chars, editorState, eventTimestamp) => {
            if (chars === '. ') {
              const currentSelection = editorState.getSelection();
              this.setState({
                editorState: EditorState.set(
                  editorState,
                  { 
                    currentContent: Modifier.replaceText(
                      editorState.getCurrentContent(),
                      currentSelection,
                      // or
                      // currentSelection.merge({
                      //   anchorOffset: currentSelection.getAnchorOffset() - 1
                      // }),
                      // '. '
                      ' '
                    )
                  }
                )
              })
              return 'handled';
            }
            return 'not-handled';
}}

I hope it helps

Edit: The above is a workaround. A more permanent fix should be done internally. This is my attempt to fix it - https://github.com/facebook/draft-js/pull/2588/files

kelvien avatar Aug 29 '20 02:08 kelvien

Does anyone knows how to fix the spacing/double spacing issue on draftjs ? I've spend couple of days trying to figure how to fix it but no luck. I saw couple suggestions here but this still did not fix my problem.

Scenario one and steps to reproduce.

  1. Go to https://codesandbox.io/s/elegant-chatelet-1ozdw?file=/src/LinkAndUnlinkText/index.js.
  2. You will see default populated link on styled editor (looks like text field) and click/focus on the editor at end of the text try hit space keyboard one or try to do double space it removes the link then crashes the app. Visual test case see below

double-space

Scenario two and steps to reproduce.

  1. Go to https://codesandbox.io/s/elegant-chatelet-1ozdw?file=/src/LinkAndUnlinkText/index.js. and comment out line 393 function called handleBeforeInput and uncomment line 394 function called handleBeforeInput={(string, editorFromParam) => handleSpaceIssue(string, editorFromParam)}.
  2. Then click on editor/text field and try to type space at the end of the text. See below for visual test case.

manual-space-handler

Second scenario works when handling manually but for some reason the UI can not detect the manual changes even after editorState update (see screenshot or open the dev console in sandbox). I would appreciate if someone advice anything I am running out of options. Thanks

Solijons avatar Feb 13 '22 22:02 Solijons

Any update on this ?

Brad-tavus avatar Jun 26 '22 03:06 Brad-tavus