-
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
Implement pasting external content with plain and styled text #13841
Implement pasting external content with plain and styled text #13841
Conversation
4b0b0bf
to
304978b
Compare
@mkevins - I'll be testing these changes through One suggestion I wanted to make for future PRs, is to avoid moving large blocks of code between files, unless the PR is specifically about that. The reason behind this suggestion is that during the review the diff becomes a mix of code-changes and code-movements that becomes hard to differentiate, and as such, hard to validate. |
Thank you for testing it out!
Thanks for the suggestion. I was torn on this, as it wasn't clear which would be more noisy, splitting on PRs with multiple related PRs across projects, or reducing the number of PRs and clarifying the changes in the commit message. I refactored (the big code movements) in the first commit and isolated those changes by commit. I'll keep this in mind in future PRs, and try to keep them smaller. If you have any questions, or want clarification on any part of the code, I'll be happy to help. |
I added a commit to refactor |
👋 @iseulde , can you make a pass on this PR? It touches some files used from the web too so, just want to make sure we don't break anything there. Thanks! |
@@ -4,7 +4,9 @@ | |||
import { wrap, replaceTag } from '@wordpress/dom'; | |||
|
|||
export default function( node, doc ) { | |||
if ( node.nodeName === 'SPAN' ) { | |||
// In jsdom-jscore, 'node.style' can be null. |
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.
Kinda a nit but, perhaps it's not too clear what jsdom-jscore
means in this context. Of course, jsdom-jscore it's the DOM parser we're using in the native mobile (React Native) version of Gutenberg, but I think I'd suggest add that bit to the comment to, to help out web-side friends read the code more easily.
So, my suggestion is to refer with In native mobile (jsdom-jscore), ...
instead of the current. What do you think? Same for a couple other similar references in this same PR.
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.
On a less of a nit thing, I wonder about the (emphasis mine on the "can"):
'node.style' *can* be null.
Does that mean that sometimes node.style is undefined and some other it's not?
In the cases where it's undefined, is it OK to just ignore it like we're doing currently in the PR? I wonder if being undefined denotes an unrecoverable error and we should let the user know about it, in some way.
Should we at least log an error/warning?
There are a few places in the PR to ask the same question, but let's discuss it here and then we apply accordingly.
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.
Yes, good suggestion! I'll edit those, and keep that in mind for future comments.
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.
Does that mean that sometimes node.style is undefined and some other it's not?
With the change of wording in the comment to: In native mobile (jsdom-jscore), ...
, it's probably more accurate to say that node.style
is undefined.
Should we at least log an error/warning?
In the codepath where this is used, node
is actually an HTMLElement
, as the condition is satisfied that the element's nodeName
is SPAN
. HTMLElement nodes are supposed to have a style
property which returns a read-only CSSStyleDeclaration
object, which can be used to retrieve the inline styles applied to the element via the style attribute. This is then used to wrap the node in appropriate tag(s) (strong
, em
, etc.). In the case where the DOM implementation does not support the style
property on a node, I believe it is safe to ignore, but that we may lose style information that was associated with the content via inline styles on a span's attribute.
The property seems to have had wide support for a long time, but I wonder if there are any browsers that don't support this property, or that leave it undefined or null when no style attribute was present on the element. My reason for wording the comment somewhat "vaguely" is that I tend to assume that there could be such a case in the wild, and thus the safety check could be useful even in the browser implementation. I checked the browser compatibility for it on MDN, and it lists a few question marks, but that just means unconfirmed AFAIK.
TL;DR:
The check will not lead to an unrecoverable error, but will gracefully degrade the feature that normalizes some inline styles on span tags. I don’t have a strong opinion on whether that warrants logging a warning; I suppose it depends on the verbosity desired (e.g. web: “please use a better browser :)”, mobile: "mobile currently lacks support for this feature").
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.
mobile: "mobile currently lacks support for this feature"
I think that's a good case to actually log since, these things are useful while offering support to users.
While at it, since this is a known limitation, let's add a "Known Limitations" section to wordpress-mobile/gutenberg-mobile#553 and mention this there. WDYT?
@@ -92,35 +98,32 @@ export class RichText extends Component { | |||
* handler. | |||
* | |||
*/ | |||
splitContent( htmlText, start, end ) { | |||
splitContent( currentRecord, blocks = [], isPasted = false ) { | |||
const { onSplit } = this.props; | |||
|
|||
if ( ! onSplit ) { |
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.
Not strictly related to this PR but, it seems that the onSplit
prop is already marked as deprecated on the web side so, since we're touching the splitContent
method, what do you think about mirroring this (https://github.com/WordPress/gutenberg/blob/master/packages/editor/src/components/rich-text/index.js#L84-L93) here too?
I'd suggest a follow up PR with that since it's not a blocker for this PR and adding more diff to this is not desirable :)
@@ -92,35 +98,32 @@ export class RichText extends Component { | |||
* handler. | |||
* | |||
*/ | |||
splitContent( htmlText, start, end ) { | |||
splitContent( currentRecord, blocks = [], isPasted = 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.
isPasted = false
I understand that we just need this flag here but, may I suggest copying what the web side is providing the corresponding method with? The idea is to stay as close as possible to the web code, wishing we can refactor and re-use the same code eventually.
I this case, let's pass a context
object with a paste
(boolean) field. WDYT?
(By the way, only reason I'm not just recommending extracting this method into a common file with the web is that there's this lastEventCount
trigger in this one, and extraction would make the code complex.)
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.
👋 @mkevins , great work here, works really nicely!
I left a few comments which I think could use some minor work. Good thing is, I don't see any blockers for this PR in any of those! I'd suggest we tackle them all in follow up PR(s) and go ahead and merge this one. Let me know if that's OK with you too.
If OK with you, then please open a ticket with a checklist of all the known issues and additional work needed so we can more easily work against them.
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.
Looks good to me! Nice work @mkevins, thanks!
I'll wait for the restarted Travis job to get green and I will merge this PR.
When you get the chance, please open a ticket in the Gutenberg-mobile repo with a checklist of all the known issues and additional work needed so we can more easily work against them.
I've added this issue to track the related work: |
* Refactor raw-handler and paste-handler into their own files * Add undefined and null checks for jsdom-jscore node methods/properties * Implement createRecord method for converting native content to RichText * Refactor splitContent and onEnter for common interface with onPaste * Begin implementing onPaste for plain and styled text * Fix some lint errors and some invalid references. * Add undefined check for jsdom-jscore node in phrasing-content-reducer * Add onReplace method to ParagraphEdit * Refactor raw-handler back into index.js * Remove unused parameters from RichText create method
* Refactor raw-handler and paste-handler into their own files * Add undefined and null checks for jsdom-jscore node methods/properties * Implement createRecord method for converting native content to RichText * Refactor splitContent and onEnter for common interface with onPaste * Begin implementing onPaste for plain and styled text * Fix some lint errors and some invalid references. * Add undefined check for jsdom-jscore node in phrasing-content-reducer * Add onReplace method to ParagraphEdit * Refactor raw-handler back into index.js * Remove unused parameters from RichText create method
Description
This PR aims to implement paste handling (targets 1 and 2) for the Mobile Gutenberg project. It is part of the work to address #238. The goal is to respond to a paste event from ReactNative code and transform the data for use with Gutenberg's existing paste handling implementations.
How has this been tested?
This PR is a work in progress, and so far testing has been manual (some manual tests done for Android and known issues are described here). When the implementation is more complete / stable, mock events will be created, as well as tests for the implementations.
A local test environment has been set up using the
./bin/setup-local-env.sh
script.DOM implementation on mobile
In order to implement these changes, it was necessary to modify several areas of the codebase. This mostly involved changes to
*.native.js
(Mobile Gutenberg only) files, but some shared code as well. In particular, a few extra safety checks have been added for situations where properties or methods are null or undefined in the ReactNative environment due to an incomplete implementation of DOM classes.The codepaths utilizing some DOM methods can be hard to follow at times. As an example, when the
pasteHandler
function callsdeepFilterHTML
, it uses thespecialCommentConverter
filter. Theraw-handling/special-comment-converter.js
file has a dependency on@wordpress/dom
, and makes use of theremove
andreplace
functions from there.There is a comment in the
rawHandler
function describing the use of thespecialCommentConverter
filter there:// Needed to create more and nextpage blocks.
AspasteHandler
uses the same filter and calls through to the same function (deepFilterHTML
) I believe this comment's description applies to the filter's use inpasteHandler
as well.Deeper into this codepath, where
deepFilterNodeList
makes use of the filters (the forEach loop), is where the need arises for thecontains
method (which had to be patched intojsdom-jscore
). In the forEach loop,item
is actually the filter function.Screenshots
Pasting styled text:
Pasting styled text with image:
Types of changes
pasteHandler
fromraw-handling/index.js
into its own file.jsdom
methods/properties.splitContent
for use withonEnter
andonPaste
, and implementedcreateRecord
for converting native content to the RichText format used in several other methods.onPaste
for plain and styled text.onReplace
fromedit.js
inedit.native.js
, and pasted image blocks are now functional in the demo app.Notes
Originally, I had planned on approaching this task in stages as described in the original issue. However, I realized upon implementing plain text paste, that styled text was implemented with very little additional effort.
Edit: Similarly, images are now working with the above approach, after implementing
onReplace
.Related PRs