-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Fixing major Android editing issues #2035
Fixing major Android editing issues #2035
Conversation
…tempt_android_fix
…tempt_android_fix
…will never work with the data that comes from the event and will be working on a new approach next
There is still some cleanup to be done on this PR before anything. |
const documentSelection = getDraftEditorSelection( | ||
editorState, | ||
editorNode.firstChild, | ||
getContentEditableContainer(editor), |
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 refactor to use the newly created getContentEditableContainer
package.json
Outdated
"description": "A React framework for building text editors.", | ||
"version": "0.10.5", | ||
"version": "0.10.6-beta.0", |
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.
This will be reverted before merging
|
||
// TODO, check if Facebook still needs this flag or if it could be removed. | ||
// Since there can be multiple mutations providing a `composedChars` doesn't | ||
// apply well on this new model. |
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.
@claudiopro would you know if we can remove this code or do we need to keep it somehow working?
If we keep it working it will be buggy, because we are now applying multiple mutations (which leads to multiple composedChars
) on compositionEnd, which is not compatible with the arguments that we provide to handleBeforeInput
here.
If draft_handlebeforeinput_composed_text
is still being used at FB, I'd suggest to provide the composedChars
from the first mutation, completely ignoring the other mutations, which should be fine for the common case. My main suggestion is still to stop depending on this code/gkflag.
editor.update(newEditorState); | ||
return; | ||
} | ||
} |
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.
This change is necessary because keyDown
on draft-js edit
mode on Android doesn't come with the correct keyCode
, so, for example, when users press backspace
we are not actually executing keyCommandPlainBackspace
, which leads to an incorrect content state.
The input event comes with this inputType
which lets us execute the correct action.
I still need to make sure older versions of Android also support this property (will do soon).
…tring for characterData mutation events
…e, including contentState and selectionState
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.
Please address these linter issues, otherwise looking good 👍
test('Can handle a single mutation', () => { | ||
withGlobalGetSelectionAs({}, () => { | ||
editor._latestEditorState = getEditorState({blockkey0: ''}); | ||
const mutations = Map({'blockkey0-0-0': '私'}); |
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.
Please replace with the unicode literal escape sequence '\u79c1'
compositionHandler.onCompositionEnd(editor); | ||
jest.runAllTimers(); | ||
|
||
expect(editorTextContent()).toBe('私'); |
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
const USE_CHAR_DATA = UserAgent.isBrowser('IE <= 11'); | ||
|
||
class DOMObserver { | ||
observer: MutationObserver; |
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.
Please initialize this property or make it nullable changing the type to ?MutationObserver
.
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.
oh, good call.
observer: MutationObserver; | ||
container: HTMLElement; | ||
mutations: Map<string, string>; | ||
onCharData: ({target: EventTarget, type: string}) => void; |
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.
This should also be initialized, or make it nullable.
* https://w3c.github.io/input-events/#dom-inputevent-inputtype */ | ||
const {inputType} = e.nativeEvent; | ||
if (inputType) { | ||
let newEditorState = onInputType(inputType, editorState); |
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.
This is never reassigned, please declare it as const
.
…tempt_android_fix_with_dom_diff
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.
@claudiopro has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.
Thank you so much for contributing this improvement @fabiomcosta! This is now merged in master 🎉 |
@claudiopro merged this pull request in 634bd29. |
Hello, please create a new release with the tag #next for testing. |
I'd love to see a new release w/this fix too! We're using some draft-js plugins and I haven't figured out how to make them work when installing from git (they can't find draft-js). |
Summary: This PR is a new attempt to address facebookarchive#1895 On facebookarchive#2031 I was trying to make compositions work using the data provided by that event, and even though that works well for most operational system, that doesn't work well for Android, where you can make composition updates in multiple places on the same composition transaction. This PR is an improvement over that PR, in that it uses a DOM mutation observer during the `compositionStart` -> `compositionEnd` interval to detect the changes the user made, and then re-apply those to the ContentState on `compositionEnd`. This approach is the one used by [Prosemirror](http://prosemirror.net/) (see https://github.com/ProseMirror/prosemirror-view/blob/master/src/domobserver.js), which is the only Rich Text Editor I've tried that works well on Android. Like previously mentioned, it allows multiple mutations on multiple places in the same composition transaction, which was impossible with the previous approach, and would cause DOM<->state inconsistencies in multiple use cases. The intent of this PR is not to fix every single Android bug, but to have a consistent editing experience on Android without introducing bugs (ideally). **TODO, known issues:** - [x] Removing empty line breaks with <backspace> doesn’t remove blocks. - [x] Mutations on the same block (different leaf nodes) are not being properly applied. - [x] Selection is not properly updated during composition events - [ ] Keep `inlineStyleOverride` working with a consistent behavior **TODO, others:** - [x] Test on Android Pie v9 API 28 - [x] Test on Android Oreo v8.1 API 27 - [x] Test on Android Oreo v8.0 API 26 - [x] Test on iPhone 12.1 (with Japanese Kana keyboard) - [x] Test composition events on Chrome Desktop v73 - [x] Test composition on IE11 (I didn't know how to test composition events though) - [x] Unit tests There are 3 ways to try out this PR. Codesandbox: https://3ymzzlj9n5.codesandbox.io/ (uses `draft-js-android-fix-beta.3`) Use the published [draft-js-android-fix](https://www.npmjs.com/package/draft-js-android-fix) package: `yarn install draft-js-android-fix` Note that this package might not be up-to-date, it's hard for me to guarantee I'll always remember to update the package, but I'll do my best. The other way is guaranteed to be up-to-date, but has a longer setup: * run `git clone https://github.com/facebook/draft-js.git` * run `git remote add fabiomcosta https://github.com/fabiomcosta/draft-js.git` * run `git fetch fabiomcosta` * run `git checkout -b attempt_android_fix_with_dom_diff fabiomcosta/attempt_android_fix_with_dom_diff` * run `yarn install` (or use `npm`) * run `open examples/draft-0-10-0/rich/rich.html`, or any other example you'd like to test Pull Request resolved: facebookarchive#2035 Reviewed By: kedromelon Differential Revision: D14568528 Pulled By: claudiopro fbshipit-source-id: 16861de52eca41cd98f884b0aecf034213fc1bd0
Hi @fabiomcosta , this was released as a npm version? I've upgraded to 0.11.4 but the space error on android is still present. |
Summary: This PR is a new attempt to address facebookarchive#1895 On facebookarchive#2031 I was trying to make compositions work using the data provided by that event, and even though that works well for most operational system, that doesn't work well for Android, where you can make composition updates in multiple places on the same composition transaction. This PR is an improvement over that PR, in that it uses a DOM mutation observer during the `compositionStart` -> `compositionEnd` interval to detect the changes the user made, and then re-apply those to the ContentState on `compositionEnd`. This approach is the one used by [Prosemirror](http://prosemirror.net/) (see https://github.com/ProseMirror/prosemirror-view/blob/master/src/domobserver.js), which is the only Rich Text Editor I've tried that works well on Android. Like previously mentioned, it allows multiple mutations on multiple places in the same composition transaction, which was impossible with the previous approach, and would cause DOM<->state inconsistencies in multiple use cases. The intent of this PR is not to fix every single Android bug, but to have a consistent editing experience on Android without introducing bugs (ideally). **TODO, known issues:** - [x] Removing empty line breaks with <backspace> doesn’t remove blocks. - [x] Mutations on the same block (different leaf nodes) are not being properly applied. - [x] Selection is not properly updated during composition events - [ ] Keep `inlineStyleOverride` working with a consistent behavior **TODO, others:** - [x] Test on Android Pie v9 API 28 - [x] Test on Android Oreo v8.1 API 27 - [x] Test on Android Oreo v8.0 API 26 - [x] Test on iPhone 12.1 (with Japanese Kana keyboard) - [x] Test composition events on Chrome Desktop v73 - [x] Test composition on IE11 (I didn't know how to test composition events though) - [x] Unit tests There are 3 ways to try out this PR. Codesandbox: https://3ymzzlj9n5.codesandbox.io/ (uses `draft-js-android-fix-beta.3`) Use the published [draft-js-android-fix](https://www.npmjs.com/package/draft-js-android-fix) package: `yarn install draft-js-android-fix` Note that this package might not be up-to-date, it's hard for me to guarantee I'll always remember to update the package, but I'll do my best. The other way is guaranteed to be up-to-date, but has a longer setup: * run `git clone https://github.com/facebook/draft-js.git` * run `git remote add fabiomcosta https://github.com/fabiomcosta/draft-js.git` * run `git fetch fabiomcosta` * run `git checkout -b attempt_android_fix_with_dom_diff fabiomcosta/attempt_android_fix_with_dom_diff` * run `yarn install` (or use `npm`) * run `open examples/draft-0-10-0/rich/rich.html`, or any other example you'd like to test Pull Request resolved: facebookarchive#2035 Reviewed By: kedromelon Differential Revision: D14568528 Pulled By: claudiopro fbshipit-source-id: 16861de52eca41cd98f884b0aecf034213fc1bd0
I'm using draft-js version: 0.11.6 and still having lot of Android issues, what am I missing? |
Hey! I've been experiencing some weirds issues with my custom components. It started on version From what I can tell this issue seems to be directly tied to the alterations made here to the |
@claudiopro @fabiomcosta When typing Chinese, the problem reappears |
Summary: This PR is a new attempt to address facebookarchive/draft-js#1895 On facebookarchive/draft-js#2031 I was trying to make compositions work using the data provided by that event, and even though that works well for most operational system, that doesn't work well for Android, where you can make composition updates in multiple places on the same composition transaction. This PR is an improvement over that PR, in that it uses a DOM mutation observer during the `compositionStart` -> `compositionEnd` interval to detect the changes the user made, and then re-apply those to the ContentState on `compositionEnd`. This approach is the one used by [Prosemirror](http://prosemirror.net/) (see https://github.com/ProseMirror/prosemirror-view/blob/master/src/domobserver.js), which is the only Rich Text Editor I've tried that works well on Android. Like previously mentioned, it allows multiple mutations on multiple places in the same composition transaction, which was impossible with the previous approach, and would cause DOM<->state inconsistencies in multiple use cases. The intent of this PR is not to fix every single Android bug, but to have a consistent editing experience on Android without introducing bugs (ideally). **TODO, known issues:** - [x] Removing empty line breaks with <backspace> doesn’t remove blocks. - [x] Mutations on the same block (different leaf nodes) are not being properly applied. - [x] Selection is not properly updated during composition events - [ ] Keep `inlineStyleOverride` working with a consistent behavior **TODO, others:** - [x] Test on Android Pie v9 API 28 - [x] Test on Android Oreo v8.1 API 27 - [x] Test on Android Oreo v8.0 API 26 - [x] Test on iPhone 12.1 (with Japanese Kana keyboard) - [x] Test composition events on Chrome Desktop v73 - [x] Test composition on IE11 (I didn't know how to test composition events though) - [x] Unit tests There are 3 ways to try out this PR. Codesandbox: https://3ymzzlj9n5.codesandbox.io/ (uses `draft-js-android-fix-beta.3`) Use the published [draft-js-android-fix](https://www.npmjs.com/package/draft-js-android-fix) package: `yarn install draft-js-android-fix` Note that this package might not be up-to-date, it's hard for me to guarantee I'll always remember to update the package, but I'll do my best. The other way is guaranteed to be up-to-date, but has a longer setup: * run `git clone https://github.com/facebook/draft-js.git` * run `git remote add fabiomcosta https://github.com/fabiomcosta/draft-js.git` * run `git fetch fabiomcosta` * run `git checkout -b attempt_android_fix_with_dom_diff fabiomcosta/attempt_android_fix_with_dom_diff` * run `yarn install` (or use `npm`) * run `open examples/draft-0-10-0/rich/rich.html`, or any other example you'd like to test Pull Request resolved: facebookarchive/draft-js#2035 Reviewed By: kedromelon Differential Revision: D14568528 Pulled By: claudiopro fbshipit-source-id: 16861de52eca41cd98f884b0aecf034213fc1bd0
This PR is a new attempt to address #1895
On #2031 I was trying to make compositions work using the data provided by that event, and even though that works well for most operational system, that doesn't work well for Android, where you can make composition updates in multiple places on the same composition transaction.
This PR is an improvement over that PR, in that it uses a DOM mutation observer during the
compositionStart
->compositionEnd
interval to detect the changes the user made, and then re-apply those to the ContentState oncompositionEnd
.This approach is the one used by Prosemirror (see https://github.com/ProseMirror/prosemirror-view/blob/master/src/domobserver.js), which is the only Rich Text Editor I've tried that works well on Android.
Like previously mentioned, it allows multiple mutations on multiple places in the same composition transaction, which was impossible with the previous approach, and would cause DOM<->state inconsistencies in multiple use cases.
The intent of this PR is not to fix every single Android bug, but to have a consistent editing experience on Android without introducing bugs (ideally).
TODO, known issues:
inlineStyleOverride
working with a consistent behaviorTODO, others:
Help test this PR
There are 3 ways to try out this PR.
Codesandbox: https://3ymzzlj9n5.codesandbox.io/ (uses
draft-js-android-fix-beta.3
)Use the published draft-js-android-fix package:
yarn install draft-js-android-fix
Note that this package might not be up-to-date, it's hard for me to guarantee I'll always remember to update the package, but I'll do my best.
The other way is guaranteed to be up-to-date, but has a longer setup:
git clone https://github.com/facebook/draft-js.git
git remote add fabiomcosta https://github.com/fabiomcosta/draft-js.git
git fetch fabiomcosta
git checkout -b attempt_android_fix_with_dom_diff fabiomcosta/attempt_android_fix_with_dom_diff
yarn install
(or usenpm
)open examples/draft-0-10-0/rich/rich.html
, or any other example you'd like to testTest Plan
On Android (Pie v9 API 28, Oreo v8.1 API 27, Oreo v8.0 API 26)
Type "d[space]"
![](https://user-images.githubusercontent.com/28530/54044671-57859d00-41ae-11e9-9b65-c30cd7ef0462.gif)
Renders the letter "d" followed by a space.
Type "da[space]"
![](https://user-images.githubusercontent.com/28530/54044675-59e7f700-41ae-11e9-9953-6cff17d1a1ce.gif)
Renders the expected highlighted autocomplete option, "day" in this case.
Type "day[space][backspace][backspace][backspace][backspace]"
![](https://user-images.githubusercontent.com/28530/54045982-c87a8400-41b1-11e9-9c8e-81abfe238e42.gif)
Properly removes the text without re-adding the previous text.
Type "d[space][enter][backspace][enter]"
![](https://user-images.githubusercontent.com/28530/54395744-78a72b80-468f-11e9-9a8d-28577dc42380.gif)
Should render "d[space][enter]".
Known issues, that might not be fixed on this PR
enter
, but the expected behavior is to commit that composition and add a line break.Acknowledgments
Based on ideas from:
#1672
#1774
ianstormtaylor/slate#2565
http://prosemirror.net/
Useful links
Latest draft-js 0.10.5 sandbox (useful to compare with previous behavior): https://ko4zmx633v.codesandbox.io/
Plain contenteditable sandbox: https://q77yqk1nww.codesandbox.io/