Converting Split Pane omponents to be used in Dash

From https://github.com/tomkp/react-split-pane/tree/master/src,
I would like to convert the code below into a Dash component but unsure how to change the necessary functions for Dash component requirements. Is it possiblet to convert components like this into a single Dash component?

import React from ‘react’;
import PropTypes from ‘prop-types’;
import ReactDOM from ‘react-dom’;
import Prefixer from ‘inline-style-prefixer’;
import stylePropType from ‘react-style-proptype’;

import Pane from ‘./Pane’;
import Resizer, { RESIZER_DEFAULT_CLASSNAME } from ‘./Resizer’;

const DEFAULT_USER_AGENT = ‘Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.2 (KHTML, like Gecko) Safari/537.2’;
const USER_AGENT = typeof navigator !== ‘undefined’ ? navigator.userAgent : DEFAULT_USER_AGENT;

function unFocus(document, window) {
if (document.selection) {
document.selection.empty();
} else {
try {
window.getSelection().removeAllRanges();
// eslint-disable-next-line no-empty
} catch (e) {}
}
}

class SplitPane extends React.Component {
constructor() {
super();

    this.onMouseDown = this.onMouseDown.bind(this);
    this.onTouchStart = this.onTouchStart.bind(this);
    this.onMouseMove = this.onMouseMove.bind(this);
    this.onTouchMove = this.onTouchMove.bind(this);
    this.onMouseUp = this.onMouseUp.bind(this);

    this.state = {
        active: false,
        resized: false,
    };
}

componentDidMount() {
    this.setSize(this.props, this.state);
    document.addEventListener('mouseup', this.onMouseUp);
    document.addEventListener('mousemove', this.onMouseMove);
    document.addEventListener('touchmove', this.onTouchMove);
}

componentWillReceiveProps(props) {
    this.setSize(props, this.state);
}

componentWillUnmount() {
    document.removeEventListener('mouseup', this.onMouseUp);
    document.removeEventListener('mousemove', this.onMouseMove);
    document.removeEventListener('touchmove', this.onTouchMove);
}

onMouseDown(event) {
    const eventWithTouches = Object.assign(
        {},
        event,
        { touches: [{ clientX: event.clientX, clientY: event.clientY }] },
    );
    this.onTouchStart(eventWithTouches);
}

onTouchStart(event) {
    const { allowResize, onDragStarted, split } = this.props;
    if (allowResize) {
        unFocus(document, window);
        const position = split === 'vertical' ? event.touches[0].clientX : event.touches[0].clientY;

        if (typeof onDragStarted === 'function') {
            onDragStarted();
        }
        this.setState({
            active: true,
            position,
        });
    }
}

onMouseMove(event) {
    const eventWithTouches = Object.assign(
        {},
        event,
        { touches: [{ clientX: event.clientX, clientY: event.clientY }] },
    );
    this.onTouchMove(eventWithTouches);
}

onTouchMove(event) {
    const { allowResize, maxSize, minSize, onChange, split, step } = this.props;
    const { active, position } = this.state;
    if (allowResize && active) {
        unFocus(document, window);
        const isPrimaryFirst = this.props.primary === 'first';
        const ref = isPrimaryFirst ? this.pane1 : this.pane2;
        if (ref) {
            const node = ReactDOM.findDOMNode(ref);

            if (node.getBoundingClientRect) {
                const width = node.getBoundingClientRect().width;
                const height = node.getBoundingClientRect().height;
                const current = split === 'vertical' ? event.touches[0].clientX : event.touches[0].clientY;
                const size = split === 'vertical' ? width : height;
                let positionDelta = position - current;
                if (step) {
                    if (Math.abs(positionDelta) < step) {
                        return;
                    }
                    // Integer division
                    // eslint-disable-next-line no-bitwise
                    positionDelta = ~~(positionDelta / step) * step;
                }
                const sizeDelta = isPrimaryFirst ? positionDelta : -positionDelta;

                let newMaxSize = maxSize;
                if ((maxSize !== undefined) && (maxSize <= 0)) {
                    const splPane = this.splitPane;
                    if (split === 'vertical') {
                        newMaxSize = splPane.getBoundingClientRect().width + maxSize;
                    } else {
                        newMaxSize = splPane.getBoundingClientRect().height + maxSize;
                    }
                }

                let newSize = size - sizeDelta;
                const newPosition = position - positionDelta;

                if (newSize < minSize) {
                    newSize = minSize;
                } else if ((maxSize !== undefined) && (newSize > newMaxSize)) {
                    newSize = newMaxSize;
                } else {
                    this.setState({
                        position: newPosition,
                        resized: true,
                    });
                }

                if (onChange) onChange(newSize);
                this.setState({ draggedSize: newSize });
                ref.setState({ size: newSize });
            }
        }
    }
}

onMouseUp() {
    const { allowResize, onDragFinished } = this.props;
    const { active, draggedSize } = this.state;
    if (allowResize && active) {
        if (typeof onDragFinished === 'function') {
            onDragFinished(draggedSize);
        }
        this.setState({ active: false });
    }
}

setSize(props, state) {
    const { primary } = this.props;
    const ref = primary === 'first' ? this.pane1 : this.pane2;
    let newSize;
    if (ref) {
        newSize = props.size || (state && state.draggedSize) || props.defaultSize || props.minSize;
        ref.setState({
            size: newSize,
        });
        if (props.size !== state.draggedSize) {
            this.setState({
                draggedSize: newSize,
            });
        }
    }
}

render() {
    const { allowResize, children, className, defaultSize, minSize, onResizerClick, onResizerDoubleClick, paneStyle,
        pane1Style: pane1StyleProps, pane2Style: pane2StyleProps, primary, prefixer, resizerClassName,
        resizerStyle, size, split, style: styleProps } = this.props;
    const disabledClass = allowResize ? '' : 'disabled';
    const resizerClassNamesIncludingDefault = (resizerClassName ?
          `${resizerClassName} ${RESIZER_DEFAULT_CLASSNAME}` : resizerClassName);

    const style = Object.assign({},
        styleProps || {}, {
            display: 'flex',
            flex: 1,
            height: '100%',
            position: 'absolute',
            outline: 'none',
            overflow: 'hidden',
            MozUserSelect: 'text',
            WebkitUserSelect: 'text',
            msUserSelect: 'text',
            userSelect: 'text',
        });

    if (split === 'vertical') {
        Object.assign(style, {
            flexDirection: 'row',
            left: 0,
            right: 0,
        });
    } else {
        Object.assign(style, {
            bottom: 0,
            flexDirection: 'column',
            minHeight: '100%',
            top: 0,
            width: '100%',
        });
    }

    const classes = ['SplitPane', className, split, disabledClass];
    const pane1Style = prefixer.prefix(Object.assign({}, paneStyle || {}, pane1StyleProps || {}));
    const pane2Style = prefixer.prefix(Object.assign({}, paneStyle || {}, pane2StyleProps || {}));

    return (
        <div
            className={classes.join(' ')}
            ref={(node) => { this.splitPane = node; }}
            style={prefixer.prefix(style)}
        >
            <Pane
                className="Pane1"
                key="pane1"
                ref={(node) => { this.pane1 = node; }}
                size={primary === 'first' ? size || defaultSize || minSize : undefined}
                split={split}
                style={pane1Style}
            >
                {children[0]}
            </Pane>
            <Resizer
                className={disabledClass}
                onClick={onResizerClick}
                onDoubleClick={onResizerDoubleClick}
                onMouseDown={this.onMouseDown}
                onTouchStart={this.onTouchStart}
                onTouchEnd={this.onMouseUp}
                key="resizer"
                ref={(node) => { this.resizer = node; }}
                resizerClassName={resizerClassNamesIncludingDefault}
                split={split}
                style={resizerStyle || {}}
            />
            <Pane
                className="Pane2"
                key="pane2"
                ref={(node) => { this.pane2 = node; }}
                size={primary === 'second' ? size || defaultSize || minSize : undefined}
                split={split}
                style={pane2Style}
            >
                {children[1]}
            </Pane>
        </div>
    );
}

}

SplitPane.propTypes = {
allowResize: PropTypes.bool,
children: PropTypes.arrayOf(PropTypes.node).isRequired,
className: PropTypes.string,
primary: PropTypes.oneOf([‘first’, ‘second’]),
minSize: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
maxSize: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
// eslint-disable-next-line react/no-unused-prop-types
defaultSize: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
size: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
split: PropTypes.oneOf([‘vertical’, ‘horizontal’]),
onDragStarted: PropTypes.func,
onDragFinished: PropTypes.func,
onChange: PropTypes.func,
onResizerClick: PropTypes.func,
onResizerDoubleClick: PropTypes.func,
prefixer: PropTypes.instanceOf(Prefixer).isRequired,
style: stylePropType,
resizerStyle: stylePropType,
paneStyle: stylePropType,
pane1Style: stylePropType,
pane2Style: stylePropType,
resizerClassName: PropTypes.string,
step: PropTypes.number,
};

SplitPane.defaultProps = {
allowResize: true,
minSize: 50,
prefixer: new Prefixer({ userAgent: USER_AGENT }),
primary: ‘first’,
split: ‘vertical’,
};

export default SplitPane;

@jbrown15 - Have you checked out the tutorial here? https://plot.ly/dash/plugins. I recommend going through that tutorial first, and then adapting the ExampleComponent to the component that you’ve posted above. Once you’ve gone through that, I’m happy to answer any questions that you might have.

I went through the tutorial and was able to start up a functioning ExampleComponent as instructed. I then modified the component to have the functionality of my split pane and I am having a hard time trouble shooting why I get errors when performing npm prepublish.

import React from 'react';
import PropTypes from 'prop-types';
import ReactDOM from 'react-dom';
import Prefixer from 'inline-style-prefixer';
import stylePropType from 'react-style-proptype';

import Pane from './Pane';
import Resizer, { RESIZER_DEFAULT_CLASSNAME } from './Resizer';

const DEFAULT_USER_AGENT = 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.2 (KHTML, like Gecko) Safari/537.2';
const USER_AGENT = typeof navigator !== 'undefined' ? navigator.userAgent : DEFAULT_USER_AGENT;

function unFocus(document, window) {
    if (document.selection) {
        document.selection.empty();
    } else {
        try {
            window.getSelection().removeAllRanges();
            // eslint-disable-next-line no-empty
        } catch (e) {}
    }
}

class SplitPane extends React.Component {
    constructor() {
        super();

        this.onMouseDown = this.onMouseDown.bind(this);
        this.onTouchStart = this.onTouchStart.bind(this);
        this.onMouseMove = this.onMouseMove.bind(this);
        this.onTouchMove = this.onTouchMove.bind(this);
        this.onMouseUp = this.onMouseUp.bind(this);

        this.state = {
            active: false,
            resized: false,
        };
    }

    componentDidMount() {
        this.setSize(this.props, this.state);
        document.addEventListener('mouseup', this.onMouseUp);
        document.addEventListener('mousemove', this.onMouseMove);
        document.addEventListener('touchmove', this.onTouchMove);
    }

    componentWillReceiveProps(props) {
        this.setSize(props, this.state);
    }

    componentWillUnmount() {
        document.removeEventListener('mouseup', this.onMouseUp);
        document.removeEventListener('mousemove', this.onMouseMove);
        document.removeEventListener('touchmove', this.onTouchMove);
    }

    onMouseDown(event) {
        const eventWithTouches = Object.assign(
            {},
            event,
            { touches: [{ clientX: event.clientX, clientY: event.clientY }] },
        );
        this.onTouchStart(eventWithTouches);
         if (fireEvent) fireEvent({event: 'change'});
    }

    onTouchStart(event) {
        const { allowResize, onDragStarted, split } = this.props;
        if (allowResize) {
            unFocus(document, window);
            const position = split === 'vertical' ? event.touches[0].clientX : event.touches[0].clientY;

            if (typeof onDragStarted === 'function') {
                onDragStarted();
            }
            this.setState({
                active: true,
                position,
            });
             if (fireEvent) fireEvent({event: 'change'});
        }
    }

    onMouseMove(event) {
        const eventWithTouches = Object.assign(
            {},
            event,
            { touches: [{ clientX: event.clientX, clientY: event.clientY }] },
        );
        this.onTouchMove(eventWithTouches);
         if (fireEvent) fireEvent({event: 'change'});
    }

    onTouchMove(event) {
        const { allowResize, maxSize, minSize, onChange, split, step } = this.props;
        const { active, position } = this.state;
        if (allowResize && active) {
            unFocus(document, window);
            const isPrimaryFirst = this.props.primary === 'first';
            const ref = isPrimaryFirst ? this.pane1 : this.pane2;
            if (ref) {
                const node = ReactDOM.findDOMNode(ref);

                if (node.getBoundingClientRect) {
                    const width = node.getBoundingClientRect().width;
                    const height = node.getBoundingClientRect().height;
                    const current = split === 'vertical' ? event.touches[0].clientX : event.touches[0].clientY;
                    const size = split === 'vertical' ? width : height;
                    let positionDelta = position - current;
                    if (step) {
                        if (Math.abs(positionDelta) < step) {
                            return;
                        }
                        // Integer division
                        // eslint-disable-next-line no-bitwise
                        positionDelta = ~~(positionDelta / step) * step;
                    }
                    const sizeDelta = isPrimaryFirst ? positionDelta : -positionDelta;

                    let newMaxSize = maxSize;
                    if ((maxSize !== undefined) && (maxSize <= 0)) {
                        const splPane = this.splitPane;
                        if (split === 'vertical') {
                            newMaxSize = splPane.getBoundingClientRect().width + maxSize;
                        } else {
                            newMaxSize = splPane.getBoundingClientRect().height + maxSize;
                        }
                    }

                    let newSize = size - sizeDelta;
                    const newPosition = position - positionDelta;

                    if (newSize < minSize) {
                        newSize = minSize;
                    } else if ((maxSize !== undefined) && (newSize > newMaxSize)) {
                        newSize = newMaxSize;
                    } else {
                        this.setState({ position: newPosition, resized: true, });
                    }

                    if (onChange) onChange(newSize);
                    this.setState({ draggedSize: newSize });
                    ref.setState({ size: newSize });
                    if (setProps) setProps({draggedSize: newSize});
                   /* if (setProps) setProps({size: newSize}); unsure if this belongs here */
                    if (fireEvent) fireEvent({event: 'change'});
                }
            }
        }
    }

    onMouseUp() {
        const { allowResize, onDragFinished } = this.props;
        const { active, draggedSize } = this.state;
        if (allowResize && active) {
            if (typeof onDragFinished === 'function') {
                onDragFinished(draggedSize);
            }
            this.setState({ active: false });
             if (fireEvent) fireEvent({event: 'change'});
        }
    }

    setSize(props, state) {
        const { primary } = this.props;
        const ref = primary === 'first' ? this.pane1 : this.pane2;
        let newSize;
        if (ref) {
            newSize = props.size || (state && state.draggedSize) || props.defaultSize || props.minSize;
            ref.setState({
                size: newSize,
            });
            if (props.size !== state.draggedSize) {
                this.setState({
                    draggedSize: newSize,
                });
                 if (setProps) setProps({draggedSize: newSize});
                 if (fireEvent) fireEvent({event: 'change'});
            }
        }
    }

    render() {
        const { allowResize, children, className, defaultSize, minSize, onResizerClick, onResizerDoubleClick, paneStyle,
            pane1Style: pane1StyleProps, pane2Style: pane2StyleProps, primary, prefixer, resizerClassName,
            resizerStyle, size, split, style: styleProps, setProps, fireEvent } = this.props;
        const disabledClass = allowResize ? '' : 'disabled';
        const resizerClassNamesIncludingDefault = (resizerClassName ?
              `${resizerClassName} ${RESIZER_DEFAULT_CLASSNAME}` : resizerClassName);

        const style = Object.assign({},
            styleProps || {}, {
                display: 'flex',
                flex: 1,
                height: '100%',
                position: 'absolute',
                outline: 'none',
                overflow: 'hidden',
                MozUserSelect: 'text',
                WebkitUserSelect: 'text',
                msUserSelect: 'text',
                userSelect: 'text',
            });

        if (split === 'vertical') {
            Object.assign(style, {
                flexDirection: 'row',
                left: 0,
                right: 0,
            });
        } else {
            Object.assign(style, {
                bottom: 0,
                flexDirection: 'column',
                minHeight: '100%',
                top: 0,
                width: '100%',
            });
        }

        const classes = ['SplitPane', className, split, disabledClass];
        const pane1Style = prefixer.prefix(Object.assign({}, paneStyle || {}, pane1StyleProps || {}));
        const pane2Style = prefixer.prefix(Object.assign({}, paneStyle || {}, pane2StyleProps || {}));

        return (
            <div
                className={classes.join(' ')}
                ref={(node) => { this.splitPane = node; }}
                style={prefixer.prefix(style)}
            >
                <Pane
                    className="Pane1"
                    key="pane1"
                    ref={(node) => { this.pane1 = node; }}
                    size={primary === 'first' ? size || defaultSize || minSize : undefined}
                    split={split}
                    style={pane1Style}
                >
                    {children[0]}
                </Pane>
                <Resizer
                    className={disabledClass}
                    onClick={onResizerClick}
                    onDoubleClick={onResizerDoubleClick}
                    onMouseDown={this.onMouseDown}
                    onTouchStart={this.onTouchStart}
                    onTouchEnd={this.onMouseUp}
                    key="resizer"
                    ref={(node) => { this.resizer = node; }}
                    resizerClassName={resizerClassNamesIncludingDefault}
                    split={split}
                    style={resizerStyle || {}}
                />
                <Pane
                    className="Pane2"
                    key="pane2"
                    ref={(node) => { this.pane2 = node; }}
                    size={primary === 'second' ? size || defaultSize || minSize : undefined}
                    split={split}
                    style={pane2Style}
                >
                    {children[1]}
                </Pane>
            </div>
        );
    }
}

SplitPane.propTypes = {

    /**
    * Resizing can be disabled by passing the allowResize
    * prop as false (allowResize={false}). Resizing is enabled by default.
    */
    allowResize: PropTypes.bool,


      /**
    * Children
    */
    children: PropTypes.arrayOf(PropTypes.node).isRequired,

     /**
    * Name of Class
    */
    className: PropTypes.string,

     /**
    * Selection of one of the two panes
    */
    primary: PropTypes.oneOf(['first', 'second']),

     /**
    * Min allowable size of Pane
    */
    minSize: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),


     /**
    * You can limit the maximal size of the 'fixed' pane using the maxSize parameter with 
    *a positive value (measured in pixels but state just a number). If you wrap the SplitPane into 
    *a container component (yes you can, just remember the container has to have the relative or 
    *absolute positioning), then you'll need to limit the movement of the splitter (resizer) at the end 
    *of the SplitPane (otherwise it can be dragged outside the SplitPane and you don't catch it never more).
    * For this purpose use the maxSize parameter with value 0. When dragged the splitter/resizer will stop at
    * the border of the SplitPane component and think this you'll be able to pick it again and drag it back then. 
    *And more: if you set the maxSize to negative value (e.g. -200), then the splitter stops 200px before the border
    * (in other words it sets the minimal size of the 'resizable' pane in this case). This can be useful also in the full-screen case of use.
    */
    maxSize: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
    
     /**
     * eslint-disable-next-line react/no-unused-prop-types
    * Children
    */
    defaultSize: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),

   /**
    * You can also set the size of the pane using the size prop. Note that a size set through props ignores
    * the defaultSize and minSize properties.
    */
    size: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),

    /**
    * By default it is the left pane for 'vertical' SplitPane and the top pane for 'horizontal' SplitPane. If you want to keep size of the 
    *second pane and let the first pane to shrink or grow by browser window dimensions, set SplitPane prop primary to second. 
    *In case of 'horizontal' SplitPane the height of bottom pane remains the same.
    */
    split: PropTypes.oneOf(['vertical', 'horizontal']),


     /**
    * If you need more control over resizing, SplitPane can notify you about when resizing started and when it 
    *ended through two callbacks: onDragStarted and onDragFinished.
    */
    onDragStarted: PropTypes.func,

     /**
    * See onDragStarted
    */
    onDragFinished: PropTypes.func,

        /**
    * Function that allows change of state
    */
    onChange: PropTypes.func,


        /**
    * Function that allows option of resize in one click
    */
    onResizerClick: PropTypes.func,


    /**
    * Function that allows option of resize one click
    */
    onResizerDoubleClick: PropTypes.func,

     /**
    * Prefixer
    */
    prefixer: PropTypes.instanceOf(Prefixer).isRequired,

    /**
    *  A Style props of pane
    * You can also pass inline styles to the components via props. 
    */
    style: stylePropType,
    /**
    *  resizerStyle - Styling to be applied to the resizer bar
    */
    resizerStyle: stylePropType,
    /**
    *  paneStyle - Styling to be applied to both panes
    */
    paneStyle: stylePropType,
     /**
    *  pane1Style - Styling to be applied to the first pane, with precedence over paneStyle
    */
    pane1Style: stylePropType,
    /**
    *  pane2Style - Styling to be applied to the second pane, with precedence over paneStyle
    */
    pane2Style: stylePropType,
         /**
    *  Class name of resizer component
    */
    resizerClassName: PropTypes.string,
    /**
    *  You can use the step prop to only allow resizing in fixed increments.
    */
    step: PropTypes.number,
};

SplitPane.defaultProps = {
    allowResize: true,
    minSize: 50,
    prefixer: new Prefixer({ userAgent: USER_AGENT }),
    primary: 'first',
    split: 'vertical',
};

export default SplitPane;

All of those errors look like eslint issues. Here is the full list of eslint rules. The eslint website includes dos and don’ts some examples for each rule.

I also noticed that these imports might be incorrect. I believe that you should npm install react-split-pane --save and then import those files. So:

npm install react-split-pane  --save

and then change your imports to something like:

import {Pane, Resizer} from 'react-split-pane; 

However, after looking at GitHub - tomkp/react-split-pane: React split-pane component, I’m not sure if Pane and Resizer are accessible. I’m not sure what the logic in your example is doing, but on first glance would something simpler like this work? (I haven’t tested or run this code, this is just a sketch)

import ReactSplitPane from 'react-split-pane'
import React, {PropTypes, Component} from 'react'

class SplitPane extends Component {
    constructor(props) {
        super(props)
    }
    
    render {
        return (
            <SplitPane split={this.props.split" defaultSize={this.props.default_size} primary={this.props.primary}>
                {this.props.children}
            </SplitPane>
        );
    }
}

SplitPane.defaultProps = {
    default_size: 200,
    primary: 'second',
    split: 'vertical'
}

SplitPane.propTypes = {
    default_size: PropTypes.number,
    primary: PropTypes.string,
    split: PropTypes.string,
    children: PropTypes.node,
}

This does seem like what I intended. I just want to utilize a split pane component. I made your adjustments with some tweaks to get errors to go away.

import React, {PropTypes, Component} from "react";

class SplitPane extends Component {
	constructor(props) {
		super(props);
	}
    
	render ( ){
		return (
			<SplitPane split={this.props.split} defaultSize={this.props.default_size} primary={this.props.primary}>{this.props.children}</SplitPane>
		);
	}
}

SplitPane.defaultProps = {
	default_size: 200,
	primary: "second",
	split: "vertical"
};

SplitPane.propTypes = {
	default_size: PropTypes.number,
	primary: PropTypes.string,
	split: PropTypes.string,
	children: PropTypes.node,
};

export default  SplitPane;

ran the command ‘npm run prepublish’

change the usage.py file to just show me the raw split pane component.

import acme_component
import dash
import dash_html_components as html

app = dash.Dash('')

app.scripts.config.serve_locally = True
app.config.supress_callback_exception=True 

app.layout = html.Div([
    acme_component.ExampleComponent( )
   
])


if __name__ == '__main__':
    app.run_server(debug=True)

Now getting this error

Thoughts?

It looks like there is an infinite recursion error because the render method is referring to SplitPane but the component itself is called SplitPane. I’d change the name of the class class SplitPane to something like class DashSplitPane

Sorry for all this back and forth. I thought a simple split pane would have been an easy conversion…

Changing the class name

import SplitPane from "react-split-pane";
import React, {PropTypes, Component} from "react";

class DashSplitPane extends Component {
	constructor(props) {
		super(props);
	}
    
	render ( ){
		return (
			<SplitPane split={this.props.split} defaultSize={this.props.default_size} primary={this.props.primary}>{this.props.children}</SplitPane>
		);
	}
}

SplitPane.defaultProps = {
	default_size: 200,
	primary: "second",
	split: "vertical"
};

SplitPane.propTypes = {
	default_size: PropTypes.number,
	primary: PropTypes.string,
	split: PropTypes.string,
	children: PropTypes.node,
};

export default  DashSplitPane;

and passing a property …_

import acme_component
import dash
import dash_html_components as html

app = dash.Dash('')

app.scripts.config.serve_locally = True
app.config.supress_callback_exception=True 

app.layout = html.Div([
    acme_component.ExampleComponent( "DashSplitPane",
        html.H1("Dash Split pane"))
   
])


if __name__ == '__main__':
    app.run_server(debug=True)

now im getting an error when trying to load the page

image

It looks like this part is missing a list around the first argument. It should be:

    acme_component.ExampleComponent([
        "DashSplitPane",
        html.H1("Dash Split pane")
    ])

I’m not sure if that is the underlying issue though.

From the exception, I suspect that the metadata.json is malformed somehow. Did you runnpm run prepublish for running this command? Could you share the metadata.json file (it should be in the acme_components folder)

this is the only thing in there. I did re run the npm run prepublish as well

{
“src/components/ExampleComponent.react.js”: {
“description”: “”,
“methods”: []
}
}

Ah I see - You have to change these as well to DashSplitPane

Do you mean to replace

“description”: “”,
“methods”: []

in the json file with

SplitPane.defaultProps = {
“default_size” : “200”,

}

to the metadata.json file?

Rather that the component file should look like this:

import SplitPane from "react-split-pane";
import React, {PropTypes, Component} from "react";

class DashSplitPane extends Component {
	constructor(props) {
		super(props);
	}
    
	render ( ){
		return (
			<SplitPane split={this.props.split} defaultSize={this.props.default_size} primary={this.props.primary}>{this.props.children}</SplitPane>
		);
	}
}

DashSplitPane.defaultProps = {
	default_size: 200,
	primary: "second",
	split: "vertical"
};

DashSplitPane.propTypes = {
	default_size: PropTypes.number,
	primary: PropTypes.string,
	split: PropTypes.string,
	children: PropTypes.node,
};

export default  DashSplitPane;

This now gives the error which is weird because we defined all the props needed.

image

image

That is weird. I’m not sure why the test is failing. I think you need to modify the test file in src/components/__tests__/ExampleComponent.js.

It’s a little silly to modify the test before trying it out, so I’d just run the other two parts of the prepublish script with:

builder run build-dist
npm run copy-lib

That command doesnt exist. just to make sure, i re-installed it before running the command and it resulted in an error

Screenshot 2017-08-11 19.10.39

You can either install builder globally (npm install -g builder) or you can access the local instance of builder with ./node_modules/.bin/builder, so

./node_modules/.bin/builder run build-dist

this is the output from that…What kind of information do we expect from running the two commands builder run buil-dist and npm run copy-lib?

Screenshot 2017-08-17 18.04.18

@jbrown15 What was your resolution? Were you able to export the module to your Python environment??