-
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
Manage Editable value as React element #466
Conversation
blocks/components/editable/index.js
Outdated
this.editor.selection.moveToBookmark( bookmark ); | ||
} | ||
|
||
setContent( content ) { | ||
if ( ! content ) { | ||
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.
Should we setContent( '' )
instead? What if it's an update?
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.
Should we
setContent( '' )
instead?
Oh, yes, I think I meant for this line to be content = '';
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.
Fixed in rebased d4f5a57c4854c11d405691b37824e63cefb3eab1.
blocks/library/text/index.js
Outdated
} ) ); | ||
} } | ||
/> | ||
); | ||
}, | ||
|
||
save( { attributes } ) { | ||
const { align, content } = attributes; | ||
let { content } = attributes; | ||
if ( ! Array.isArray( content ) ) { |
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.
Could this be something different than an array of undefined ?
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.
Could this be something different than an array of undefined ?
Yes, I've added some clarifying comments and slightly adjusted the behavior to return early on an undefined
content in the rebased d4f5a57c4854c11d405691b37824e63cefb3eab1.
Specifically:
// Content can be in the following shapes, so we normalize to an array:
// - Single paragraph: Object
// - Multiple paragraph: Array of objects
blocks/library/text/index.js
Outdated
} | ||
|
||
return content.map( ( paragraph ) => { | ||
if ( 'string' === typeof paragraph ) { |
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.
When do we have a string here?
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.
When do we have a string here?
When splitting a paragraph into two, the default TinyMCE getContent
was including a newline between the paragraphs which when parsed by html-to-react
resulted in an [ Element, '\n', Element ]
. In the rebased 075237ef5d3947a9f784c27580bf6ba9a9193a4e, I changed this to get content as raw formatted with the assumption that clean-up will occur as a result of html-to-react
and renderToString
(see also extended commit description).
blocks/components/editable/index.js
Outdated
return ''; | ||
} | ||
|
||
content = wp.element.renderToString( content ); |
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.
Should we be concerned about performance here? renderToString
on each render and parse
on each onChange
?
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.
Should we be concerned about performance here?
renderToString
on each render andparse
on eachonChange
?
Yes 😄
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 the intent of the change, but a trade-off that could help performance slightly is change to retrieve content from editor instance as raw formatted in 075237ef5d3947a9f784c27580bf6ba9a9193a4e.
e5d1d1d
to
075237e
Compare
blocks/library/text/index.js
Outdated
// - Single paragraph: Object | ||
// - Multiple paragraph: Array of objects | ||
if ( ! Array.isArray( content ) ) { | ||
content = [ content ]; |
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.
could we use castArray
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 thing that bothers me with this, is that Plugin authors would need to understand well React Elements.
The fact that it could be sometimes a single element and sometimes an array is a bit confusing I think. Shouldn't we always castArray
in the parser and in the Editable
?
Anyway, this solves the dangerouslySetInnerHTML
issue and is worth a try
* @param {?Object} props Props to apply to cloned element | ||
* @return {WPElement} Cloned element | ||
*/ | ||
export { cloneElement }; |
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.
Honestly, I don't see much value in abstracting "React"
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.
Honestly, I don't see much value in abstracting "React"
What are you suggesting here?
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.
we just drop wp.element
:)
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 could probably be convinced. Needs to be a decision made with future compatibility in mind, related to some of the "reason to be" outlined in the docs:
https://github.com/WordPress/gutenberg/tree/master/element#element
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.
To clarify my point, I don't see much value in abstracting React because we're using a lot of React features: Element, Component (refs, state, lifecycle), cloneElement, renderToString, render. Which is close to be the whole React.
Also, and this is probably the main reason for me. If we want to use a library like react-textarea-autosize
or react-tooltip
or any library dependent on React, we can do so (we're already doing it) technically and it'll work because we're using React but what if we switch our wp.element
implementation to a custom React alternative, nothing guarantees that the libraries we're using are only using the React features we whitelist on our implementation. And this IMO should force us to avoid using any React third-party library because "logically" they're not dependent on our abstraction but they're dependent on React (which is a larger library).
I don't anticipate that they'd need to do any manipulation of the object, and I'm fine with it being slightly opaque in that regard. You're correct there would need to be some awareness of what the value is generally. The goal here is to eliminate plugin author consideration of markup strings. We could simply pass the So the question becomes: if we want to represent the caption, citation, or paragraphs nested elements as data, what shape does it take? I think there are some less complex options if we optimize for the value's shape to be inspected, but I don't think we care. A React tree seemed a natural fit into what we're already building. Aside: I'm not claiming you disagree with me on any of these points. Just trying to clarify (for myself and others) the rationale for this change. |
I'm not totally clear on the ramifications of this change, but I think it could make sense, sure. |
075237e
to
0924fe6
Compare
Rebased again to resolve conflicts, and account for newly added Embed block. @youknowriad 0924fe6 is an improved approach to handling differing shapes of the parsed content, leveraging the fact that
https://facebook.github.io/react/docs/react-api.html#react.children And yes, I can totally appreciate that including this adds fuel to your argument that we should drop the abstraction 😆 Let's do that... but not here. |
0924fe6
to
f67320a
Compare
Default formatting is slower because it cleans the content. We don’t particularly care for this behavior since the React parser and subsequent renderToString will achieve the desired output
Designed for this purpose of handling opaque structure of children https://facebook.github.io/react/docs/react-api.html#react.children
f67320a
to
82feb24
Compare
Closes #421
This pull request seeks to address issues with treating the Editable value as a string. Specifically, the need to manage a string value counteracts the promise of avoiding manual manipulation of HTML and adds complexity to serialization.
Related discussion at #413 (comment)
Implementation details:
children
hpq matcher to extract content as a React element, using html-to-reactrenderToString
to handle more incoming value types (falsey, string, array)It's admittedly concerning to apply even more parsing in the form of HTML → React transformation, especially in juggling the incoming and outgoing value on the Editable (TinyMCE) component. I think we'll need to address this sooner than later, but the changes introduced here are aimed at shaping expectations of managing attributes of a block which contain elements.
Testing instructions:
Verify that there are no regressions of existing behavior:
Ensure included tests pass: