Skip to content
This repository has been archived by the owner on Feb 6, 2023. It is now read-only.

Conversation

mitermayer
Copy link
Contributor

Context

This PR is part of a series of PR's that will be exploring tree data block support in Draft.

Rendering tree data format

This PR adds support for react components to render the tree data structure


Note:
This is unstable and not part of the public API and should not be used by
production systems.

className,
getListItemClasses(blockType, depth, shouldResetCount, direction),
);
if (rootBlock && rootBlock instanceof ContentBlockNode) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

For now left the rendering logic for tree inside DraftEditorContents.react.js, but when we introduce a new feature flag to control this logic we can move this out to a tree aware variant of DraftEditorContents.react.js and use the feature flag to chose between both.

Decided to integrate in here for now to make it easier to review and get early end to end progression

@@ -0,0 +1,142 @@
/**
Copy link
Contributor Author

Choose a reason for hiding this comment

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

We have a file called DraftEditorContent.react-test.js however since I plan to move the logic for rendering tree outside of that file it made sense to create a separate test file as opposed to add to the existing one.

Copy link
Contributor

Choose a reason for hiding this comment

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

We talked about this and agreed that it might be nice to rename this - for now could we rename the other one to DraftEditorContents.react-test and call this one DraftEditorContentsExperimental.react-test or something like that?

tree: editorState.getBlockTree(blockKey),
};

const configForType =
Copy link
Contributor Author

@mitermayer mitermayer Nov 13, 2017

Choose a reason for hiding this comment

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

Due to DraftEditorBlockNode only handle wrapping for its own children had to still rely on the augment functionality provided by this file for wraping the rootBlock nodes (https://github.com/facebook/draft-js/pull/1508/files#diff-42e035f607d4b34ff0d0592ee0b49266R286)

Once we introduce featutre control flag move this logic out of this file we can then avoid this

@mitermayer mitermayer force-pushed the feature-tree/rendering-react-components-from-tree-data branch from d6b21b0 to e8b60b9 Compare November 14, 2017 03:58
Copy link
Contributor

@flarnie flarnie left a comment

Choose a reason for hiding this comment

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

still in progress reviewing

@@ -16,6 +16,7 @@
import type {BlockNodeRecord} from 'BlockNodeRecord';
Copy link
Contributor

Choose a reason for hiding this comment

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

Just confirming - looks like the only changes to this file were cosmetic, improving and modernizing the style and organization.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thats correct! only cosmetic chanes

* This is unstable and not part of the public API and should not be used by
* production systems. This file may be update/removed without notice.
*/

Copy link
Contributor

Choose a reason for hiding this comment

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

Can we leave a comment explaining that this is a fork of DraftEditorBlock? And we can remove the comment once we no longer have both versions.

We should do the same for BlockNode.js, saying that it's a fork of ContentBlock which adds nodes, parent, prevSibling, and nextSibling.


const {List} = Immutable;

type BlockRenderFn = (block: BlockNodeRecord) => ?Object;
Copy link
Contributor

Choose a reason for hiding this comment

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

Should the return type be something like React.Node?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I got this from the main file https://github.com/facebook/draft-js/blob/master/src/component/base/DraftEditorProps.js#L183

I suggest we update all of that later, could even raise this as a good first bug, updating the flow definitions

forceSelection: boolean,
selection: SelectionState,
startIndent?: boolean,
tree: List<any>,
Copy link
Contributor

Choose a reason for hiding this comment

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

This was already named tree in the previous one, just wondering if we can make the name more descriptive while we are here. Or open an issue to requests someone do so, here and in DraftEditorBlock.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I agree, do you have some suggestions ?

type BlockRenderFn = (block: BlockNodeRecord) => ?Object;
type BlockStyleFn = (block: BlockNodeRecord) => string;

type Props = {
Copy link
Contributor

Choose a reason for hiding this comment

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

For my own notes - this adds the following props which are not passed into DraftEditorBlock:

  • blockRenderMap
  • blockRenderFn
  • contentState
  • editorKey
  • editorState

const getDraftRenderConfig = (
block: BlockNodeRecord,
blockRenderMap: DraftBlockRenderMap,
): * => {
Copy link
Contributor

Choose a reason for hiding this comment

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

We could at least say it returns type Object and leave a TODO to add a specific type. Same below with getCustomRenderConfig.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sounds good to me, I was leaning towards adding typing now but the PR was already too big, happy to do this on follow up PR's

editorKey: string,
offsetKey: string,
blockStyleFn: BlockStyleFn,
customConfig: *,
Copy link
Contributor

Choose a reason for hiding this comment

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

if this customConfig param is the same as the return value of getCustomRenderConfig we could add a specific type and reuse it. Could be done in a follow-up commit.

const {block, direction, tree} = this.props;

return (
block.getChildKeys() !== List() ||
Copy link
Contributor

Choose a reason for hiding this comment

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

Just noting that we talked about updating this conditional.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's correct this conditional logic as it is is wrong, will update it

);
const childProps = {
...this.props,
tree: editorState.getBlockTree(key),
Copy link
Contributor

Choose a reason for hiding this comment

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

I hate to pass the editorState through in order to use it just once. Not sure how to extract what we need here though.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I could pass just the editorState.getBlockTree method reference, what do you reckon ?

Copy link
Contributor

Choose a reason for hiding this comment

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

Sounds good - passing a bound function like that is a bit unconventional, but I can't think of any strong reason not to.


return acc;
}, []);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

This might be nice as a helper function, like this._renderChildren.

for rendering tree data structure

- This PR adds support for rendering tree data structure
Copy link

@SirensOfTitan SirensOfTitan left a comment

Choose a reason for hiding this comment

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

First pass: I haven't quite wrapped my head around the larger idea yet, but I'm getting there.

block: BlockNodeRecord,
blockProps?: Object,

Choose a reason for hiding this comment

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

Can we type this a bit better? Perhaps as something like: {+[key: string]: *} to force covariance. * may not work either so any would work too.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is already existent draft editor block, we definitely should improve typing but this should probably come up on a different PR

Copy link
Contributor

Choose a reason for hiding this comment

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

I agree that we could improve the types - seems like a good thing to work on as a follow up. We can make a list and either bootcamp or share with @juliankrispel .

We have run into some type definitions that pass CI on Github but break internally, so we are also being careful so we don't get blocked by that.


// we check back until we find a sibbling that does not have same wrapper
for (const sibling: any of nodes.reverse()) {
if (sibling.type !== Element) {

Choose a reason for hiding this comment

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

Why are we tagging this block as any? React.Node could be null or a number, amongst other things, so we should insulate against that?

+nit, but since this is so simple why don't we avoid a break?

for (const sibling of nodes.reverse()) {
  if (sibling && sibling.type === Element) {
    wrappedSiblings.push(sibling);
  }
}
// or this could be reduced down to a simple filter:
const wrappedSiblings = nodes.reverse().filter(node ==> node && node.type === Element);

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We want to avoid unnecessary iterations on larger documents

blockStyleFn: BlockStyleFn,
customConfig: *,
): Object => {
let elementProps: Object = {

Choose a reason for hiding this comment

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

Again, I don't know what flow internals are around props, but using a CovariantObject is safer, so perhaps getElementPropsConfig should return the type {+[key: string]: any};

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Been trying to play safe on this PR since I don't want to get blocked by flow types, a lot of times flow internal is behaving different than here so I think we can create a follow up PR / issue to harden up draft.js typings in general

const {block, direction, tree} = this.props;

return (
block.getChildKeys() !== List() ||

Choose a reason for hiding this comment

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

!block.getChildKeys.isEmpty() as perhaps something more readable?

block !== nextProps.block ||
tree !== nextProps.tree ||
direction !== nextProps.direction ||
(isBlockOnSelectionEdge(nextProps.selection, nextProps.block.getKey()) &&

Choose a reason for hiding this comment

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

This line perhaps deserves a comment. So why would we update if we're on a selection edge and want to forceSelection?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is legacy from the port of the other BlockNode

} = this.props;

const blockKey = block.getKey();
const leavesForLeafSet = leafSet.get('leaves');

Choose a reason for hiding this comment

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

Perhaps I'm missing something here, but leafSet is a List, which means get takes numbers. Is leafSet typed correctly?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will double check, could be that it's typed incorrectly

Copy link
Contributor

@flarnie flarnie left a comment

Choose a reason for hiding this comment

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

I think this is a good start. We talked through it a bit and this is my impression of the high level overview:

  • The logic for rendering the set of DraftEditorBlocks used to mainly live in DraftEditorContents.
  • A block can now be either a normal block or a container that holds other blocks, since we are adding support for ntested blocks. For that reason we can't just do the block rendering in DraftEditorContents.
  • A block containing other blocks must render those blocks within itself, so that is what DraftEditorBlockNode does.
  • We are breaking out DraftEditorNode and DraftEditorDecoratedLeaves in order to keep things modular and organized. Those contain some of the logic for rendering the DraftEditorLeaf within a DraftEditorBlockNode.
  • We still retain some logic for rendering the 'root' blocks inside DraftEditorContents, because the DraftEditorBlockNode cannot render itself. In follow up diffs we will fork DraftEditorCOntents and organize/improve the logic for rendering those root blocks.

Glad to see so many tests!!!

wins_everywhere

* This is unstable and not part of the public API and should not be used by
* production systems. This file may be update/removed without notice.
*/

Copy link
Contributor

Choose a reason for hiding this comment

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

Nice - looks like this is pulled out from DraftEditorBlock to keep things modular.

const decoratorKey = leafSet.get('decoratorKey');
const leavesForLeafSet = leafSet.get('leaves');
const lastLeaf = leavesForLeafSet.size - 1;
const Leaves = leavesForLeafSet
Copy link
Contributor

Choose a reason for hiding this comment

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

nit - Leaves to leaves


const blockKey = block.getKey();
const offsetKey = DraftOffsetKey.encode(blockKey, 0, 0);

Copy link
Contributor

Choose a reason for hiding this comment

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

So the legacy approach was to render DraftEditorLeaf inside DraftEditorBlock, like so:

DraftEditorBlock
|
|-> DraftEditorLeaf

Now we have added more layers, with DraftEditorBlockNode rendering DraftEditorNode which renders DraftEditorLeaf wrapped optionally in DraftEditorDecoratedLeaves:

DraftEditorBlockNode
|
|-> DraftEditorNode
    |
    |-> DraftEditorDecoratedLeaves
        |
        |-> DraftEditorLeaf

Is the point of this new structure to make the code more clean and modular? Since we may have many blocks on the page, I wonder if it will affect performance.

*/

'use strict';

Copy link
Contributor

Choose a reason for hiding this comment

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

Just mentioning - if we can merge this it might be helpful to get the tests working - #1464

});
});

test('renders block with nested child that if of different block type', () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit - if of different -> is of different

@@ -0,0 +1,142 @@
/**
Copy link
Contributor

Choose a reason for hiding this comment

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

We talked about this and agreed that it might be nice to rename this - for now could we rename the other one to DraftEditorContents.react-test and call this one DraftEditorContentsExperimental.react-test or something like that?

@mitermayer mitermayer force-pushed the feature-tree/rendering-react-components-from-tree-data branch from e8b60b9 to 8de180a Compare November 15, 2017 22:10
Copy link

@facebook-github-bot facebook-github-bot left a comment

Choose a reason for hiding this comment

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

@mitermayer is landing this pull request. If you are a Facebook employee, you can view this diff on Phabricator.

midas19910709 added a commit to midas19910709/draft-js that referenced this pull request Mar 30, 2022
Summary:
This PR is part of a series of PR's that will be exploring **tree data block support** in Draft.

**Rendering tree data format**

This PR adds support for react components to render the tree data structure

***

**Note:**
This is unstable and not part of the public API and should not be used by
production systems.
Closes facebookarchive/draft-js#1508

Differential Revision: D6341091

fbshipit-source-id: e285bb04ca51eb55b75c6912a2f5d283a4145d2d
alicayan008 pushed a commit to alicayan008/draft-js that referenced this pull request Jul 4, 2023
Summary:
This PR is part of a series of PR's that will be exploring **tree data block support** in Draft.

**Rendering tree data format**

This PR adds support for react components to render the tree data structure

***

**Note:**
This is unstable and not part of the public API and should not be used by
production systems.
Closes facebookarchive/draft-js#1508

Differential Revision: D6341091

fbshipit-source-id: e285bb04ca51eb55b75c6912a2f5d283a4145d2d
aforismesen added a commit to aforismesen/draft-js that referenced this pull request Jul 12, 2024
Summary:
This PR is part of a series of PR's that will be exploring **tree data block support** in Draft.

**Rendering tree data format**

This PR adds support for react components to render the tree data structure

***

**Note:**
This is unstable and not part of the public API and should not be used by
production systems.
Closes facebookarchive/draft-js#1508

Differential Revision: D6341091

fbshipit-source-id: e285bb04ca51eb55b75c6912a2f5d283a4145d2d
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants