Skip to content
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

Editable: state as a cleaned up tree #3048

Closed
wants to merge 11 commits into from
Closed

Conversation

ellatrix
Copy link
Member

@ellatrix ellatrix commented Oct 18, 2017

Description

Tries to address #2750.

This branch was created as a proof of concept and alternative to #2786. Logging it as a PR, but it's still a work-in-progress.

The goal is to have the editable state as a true (simple arrays), a cleaned up version of the current state as a React tree.

@ellatrix ellatrix added [Status] In Progress Tracking issues with work in progress [Feature] Rich Text Related to the Rich Text component that allows developers to render a contenteditable labels Oct 18, 2017
@gziolo gziolo force-pushed the try/editable-state-tree branch 4 times, most recently from 6f18bbd to e6ffd52 Compare October 19, 2017 13:49
@ellatrix
Copy link
Member Author

While quickly putting this alternative together, I used dom-react to loop through the nodes. I wonder if we want to keep this though. The most important thing it does is mapping attributes to React canonical ones and parse the style attribute to JSON. If we don't mind attributes with dashes in the state... we could loop ourselves and drop this. With React 16, it seems still recommended to use the canonical attributes, so then we would need to map from state to React, instead of dom to React.

} );

it( 'should render null when false passed as value', () => {
const wrapper = shallow( <Editable.Value value={ [ false ] } /> );
Copy link
Member Author

@ellatrix ellatrix Oct 19, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this needed? Not sure how that could happen.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Same for the above.)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can drop it, I just wanted to track down why this fails in test env.

@gziolo
Copy link
Member

gziolo commented Oct 19, 2017

It turns out we are using React 15 and this break most of the code used. I will look into an upgrade to React 16 tomorrow to get rid of <span /> workaround.

@gziolo gziolo force-pushed the try/editable-state-tree branch from e6ffd52 to 14315b3 Compare October 19, 2017 14:25
return createElement( type, { ...props, key: i }, valueToReact( children ) );
} );

// We are still using React 15, so can't return array or string directly...
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only in test environment are we using React 15.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hopefully not that long: #3079 :)

@gziolo
Copy link
Member

gziolo commented Oct 20, 2017

As soon as we land React upgrade for tests (#3079) I will update PR to stop using hacky <span /> wrapper.

@gziolo gziolo force-pushed the try/editable-state-tree branch from 14315b3 to eebaeca Compare October 20, 2017 17:32
@gziolo
Copy link
Member

gziolo commented Oct 20, 2017

I rebased with master branch which now has React 16 and Enzyme 3.

We have still 14 failing tests, but 12 of them are directly related to the different JSON representation of components. There are 2 tests failing that we still need to investigate:

FAIL blocks/api/test/source.js
sources
✓ should generate sources which apply internal flag (7ms)
children()
✓ should return a source function (1ms)
✕ should return HTML equivalent WPElement of matched element (10ms)
node()
✓ should return a source function (1ms)
✕ should return HTML equivalent WPElement of matched element (2ms)

● sources › children() › should return HTML equivalent WPElement of matched element

Invariant Violation: Objects are not valid as a React child (found: object with keys {}). If you meant to render a collection of children, use an array instead.null
  
  at invariant (node_modules/react/node_modules/fbjs/lib/invariant.js:42:15)
  at traverseAllChildrenImpl (node_modules/react/cjs/react.development.js:830:7)
  at traverseAllChildrenImpl (node_modules/react/cjs/react.development.js:803:23)
  at traverseAllChildrenImpl (node_modules/react/cjs/react.development.js:803:23)
  at traverseAllChildren (node_modules/react/cjs/react.development.js:858:10)
  at mapIntoWithKeyPrefixInternal (node_modules/react/cjs/react.development.js:934:3)
  at toArray (node_modules/react/cjs/react.development.js:981:3)
  at new ReactDOMServerRenderer (node_modules/react-dom/cjs/react-dom-server.node.development.js:2677:64)
  at renderToStaticMarkup (node_modules/react-dom/cjs/react-dom-server.node.development.js:2990:18)
  at Object.<anonymous> (blocks/api/test/source.js:36:39)
  at process._tickCallback (internal/process/next_tick.js:109:7)

● sources › node() › should return HTML equivalent WPElement of matched element

Invariant Violation: Objects are not valid as a React child (found: object with keys {}). If you meant to render a collection of children, use an array instead.null
  
  at invariant (node_modules/react/node_modules/fbjs/lib/invariant.js:42:15)
  at traverseAllChildrenImpl (node_modules/react/cjs/react.development.js:830:7)
  at traverseAllChildrenImpl (node_modules/react/cjs/react.development.js:803:23)
  at traverseAllChildren (node_modules/react/cjs/react.development.js:858:10)
  at mapIntoWithKeyPrefixInternal (node_modules/react/cjs/react.development.js:934:3)
  at toArray (node_modules/react/cjs/react.development.js:981:3)
  at new ReactDOMServerRenderer (node_modules/react-dom/cjs/react-dom-server.node.development.js:2677:64)
  at renderToStaticMarkup (node_modules/react-dom/cjs/react-dom-server.node.development.js:2990:18)
  at Object.<anonymous> (blocks/api/test/source.js:53:39)
  at process._tickCallback (internal/process/next_tick.js:109:7)

@gziolo gziolo force-pushed the try/editable-state-tree branch from eebaeca to 0b5428f Compare October 23, 2017 11:31
const match = parse( html, sources.node() );

expect( renderToString( match ) ).toBe( `<body>${ html }</body>` );
expect( renderToString( valueToElement( [ match ] ) ) ).toBe( `<body>${ html }</body>` );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@iseulde this test is failing. I didn't find a way to fix it. There is some issue with the sources.node parser. It inlines string nodes and nodes represented as an array. Once this is sorted out we should be good to do.

It also fails for this HTML fixture.

<blockquote class="wp-block-pullquote alignnone">
    <p>Paragraph <strong>one</strong></p>
    <p>Paragraph two</p>
    <footer>by whomever</footer>
</blockquote>

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I fixed it :)

@gziolo gziolo force-pushed the try/editable-state-tree branch 3 times, most recently from 4cf568b to 03c9bc0 Compare October 24, 2017 10:11
@WordPress WordPress deleted a comment from codecov bot Oct 24, 2017
@gziolo gziolo requested a review from youknowriad October 24, 2017 10:13
@gziolo gziolo added Framework Issues related to broader framework topics, especially as it relates to javascript and removed [Status] In Progress Tracking issues with work in progress labels Oct 24, 2017
@@ -27,10 +28,29 @@ registerBlockType( 'core/table', {
type: 'array',
source: children( 'table' ),
default: [
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might want to keep what we had before and provide transformation layer instead. I wanted to make it work.

@gziolo
Copy link
Member

gziolo commented Oct 24, 2017

@iseulde @aduth @youknowriad - I manage to make it work with both the existing unit tests and the browser. There are two is one known issue:

  • Transforming from the quote or pullquote to anything else breaks the block.
  • It's not possible to use toggle on anchor until it's saved. (I can see it also in the master branch)

@gziolo gziolo requested a review from mtias October 24, 2017 11:16
@@ -44,7 +43,7 @@ export const children = withKnownSourceFlag( ( selector ) => {
}

if ( match ) {
return nodeListToReact( match.childNodes || [], createElement );
return nodeListToReact( match.childNodes || [], toArray );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess dom-react should be something like dom-traverse?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should I read it as we should introduce a facade for dom-react? I think it would make a lot of sense.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess dom-react should be something like dom-traverse?

Yeah, this confused me as well, and I thought we were creating instances of React elements and then converting those into arrays. Understand better now, but the naming is a bit misleading. I also wonder how much of the React-isms we really need to be accounting for (e.g. key) or if it's enough to pick attributes from node.attributes (see 6521f06).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Honestly I just did this to spin up a fast POC. Would prefer if we get rid of this too.

@@ -36,7 +36,7 @@ import { EVENTS } from './constants';

const { BACKSPACE, DELETE, ENTER } = keycodes;

function createTinyMCEElement( type, props, ...children ) {
function toArray( type, props, ...children ) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we name it valueFromElement (to be consistent with valueToElement)?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea, I like your proposal :)

@@ -58,6 +57,6 @@ export const node = withKnownSourceFlag( ( selector ) => {
match = domNode.querySelector( selector );
}

return nodeToReact( match, createElement );
return flatten( nodeToReact( match, toArray ) );
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this need to flatten?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this one is tricky. I had to flatten it to mirror what is happening in the editor. Somehow it wraps with one additional array when you compare with what is passed to <Editable /> or <Editable.value />. It was quite hard to find a setup where all moving parts are satisfied: <Editable />, <Editable.value />, pasting from other documents. It might be a better idea to wrap value with another array in Editable. I'm not sure what's better here.

@gziolo gziolo force-pushed the try/editable-state-tree branch 3 times, most recently from 525b9c4 to 107a6d9 Compare December 13, 2017 09:53
@gziolo gziolo force-pushed the try/editable-state-tree branch from 107a6d9 to e9b7111 Compare December 13, 2017 09:57
const [ type, props, ...children ] = element;
const reactProps = Object.keys( props ).reduce( ( accumulator, key ) => {
const mapping = {
class: 'className',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like adding those 2 mappings is enough to satisfy React.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okey, style needs its own handling, because I see this: https://reactjs.org/docs/error-decoder.html?invariant=62&args[]=

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All keys must go camel case, too.

};
}, { key: i } );

return createElement( type, { ...reactProps }, ...valueToElement( children ) );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It can be simply reactProps :)

@@ -850,7 +882,7 @@ export default class Editable extends Component {
getSettings={ this.getSettings }
onSetup={ this.onSetup }
style={ style }
defaultValue={ value }
defaultValue={ valueToElement( value ) }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use applySimpleNodeList in here, too?

@aduth
Copy link
Member

aduth commented Dec 13, 2017

@gziolo @iseulde How reasonable do you think (in parallel to the work here) to implement our own save serialization and skip React's altogether? Maybe similar to the ideas in #2463 with interoperability renderers. That way, we might not need Editable.Value or otherwise coercing the value back to a React element, if we had built-in understanding of this tree shape.

I'm finding we're repeatedly bumping up against issues with React's serialization, most recently summarized at #3353 (comment).

@ellatrix
Copy link
Member Author

@aduth But then we can't use React elements inside the save function? Or would it be mixed?

@aduth
Copy link
Member

aduth commented Dec 14, 2017

@aduth But then we can't use React elements inside the save function? Or would it be mixed?

Right, I would think still supporting React elements, but "turning off" some of the sanitization behaviors, and supporting other object shapes.

@ellatrix
Copy link
Member Author

Should we consider this now as succeeded by #4049?

@gziolo
Copy link
Member

gziolo commented Jan 10, 2018

Should we consider this now as succeeded by #4049?

Yes, probably it makes the most sense. This one was very helpful in the context of exploring what we really need. We should focus on one approach and finally get it merged :D

@gziolo gziolo removed their assignment Jan 10, 2018
@ellatrix
Copy link
Member Author

Closing for now in favour of #4049.

@ellatrix ellatrix closed this Jan 11, 2018
@ellatrix ellatrix deleted the try/editable-state-tree branch January 11, 2018 09:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Feature] Rich Text Related to the Rich Text component that allows developers to render a contenteditable Framework Issues related to broader framework topics, especially as it relates to javascript [Status] In Progress Tracking issues with work in progress
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants