Draft-js: Custom block wrappers not working as in docs

Created on 10 Jan 2017  路  6Comments  路  Source: facebook/draft-js

Do you want to request a feature or report a bug?
bug

What is the current behavior?
I am trying to add a custom id to headers (for a table of contents).
I am following the example, and I get an error about this not being defined. I using the exact code as per docs.

What is the expected behavior?
props should be passed through to custom wrapper

Which versions of Draft.js, and which browser / OS are affected by this issue? Did this work in previous versions of Draft.js?
Chrome Latest (macOS)
draft-js 0.9.1

pasted graphic copy

Most helpful comment

@aight8 I was able to achieve what I was going for using a custom element:

const HEADERS = {
    'header-one': 'h1',
    'header-two': 'h2',
    'header-three': 'h3',
    'header-four': 'h4',
    'header-five': 'h5',
};

const blockRenderMapGenerator = editorState => {

    let contentState = editorState.getCurrentContent();

    const Header = props => {
        let {key} = props.children;
        let block = contentState.getBlockForKey(key);
        let id = block.getData().get('id');
        let type = block.getType();
        const Tag = HEADERS[type] || 'span';

        return (
            <Tag id={id}>
                {props.children}
            </Tag>
        );
    }

    const blockRenderMap = Immutable.Map({
        'header-one': {
            element: Header
        },

thankyou for you help!

All 6 comments

props not exists in this context, your this references to the global scope.

Anyway, you don't have to pass this props to your custom block.
https://github.com/facebook/draft-js/blob/master/src/component/contents/DraftEditorContents.react.js#L218
The element you pass will be cloned and receives the props already.
It must be a mistake in the documentation.

Thanks for the comment @aight8.
Removing the ...this.props does pass through some props.

Two issues:

  1. These props dont have any information about the block. only the key....

  2. The wrapper does not wrap each block individually, it groups them:
    screen shot 2017-01-11 at 10 27 17 am

I am trying to add a id to each of my h1 so that I can internally link to headers (using a table of contents), that unique id is stored in the data key on the block.

Okei, yeah you cant achieve this with blockMap (the block component is just a native element name and you can't use wrapper neither). So the only way is with the blockRendererFn editor prop.
You can try something like:

if (contentBlock.getType() === 'header-one') {
  const id = this.fetchIdFromBlock(contentBlock); // your function which determinate the id by analyze the content state, also check possibilities for: cache/pre-calculation, when it gives benefits
  const H1 = props => <h1 id={id} {...props}></h1>;
  return {
    component: H1,
    editable: true,
  };
}

Maybe you can save the per-block id calculation in the content block data (when it makes it cleaner/easier), but at the end, you must implement blockRendererFn anyway. And the calculation of the id's is still the same.

@aight8 I was able to achieve what I was going for using a custom element:

const HEADERS = {
    'header-one': 'h1',
    'header-two': 'h2',
    'header-three': 'h3',
    'header-four': 'h4',
    'header-five': 'h5',
};

const blockRenderMapGenerator = editorState => {

    let contentState = editorState.getCurrentContent();

    const Header = props => {
        let {key} = props.children;
        let block = contentState.getBlockForKey(key);
        let id = block.getData().get('id');
        let type = block.getType();
        const Tag = HEADERS[type] || 'span';

        return (
            <Tag id={id}>
                {props.children}
            </Tag>
        );
    }

    const blockRenderMap = Immutable.Map({
        'header-one': {
            element: Header
        },

thankyou for you help!

oh I didn't know that this works. thank you!

Was this page helpful?
0 / 5 - 0 ratings