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

Custom Components

matt-manywho edited this page Oct 5, 2016 · 6 revisions

A UI component consists of at least one React component and potentially some optional CSS.

Components can be written in either normal vanilla ES5 javascript or Typescript.

Basic Container

var myContainer = React.createClass({
 
    render: function () {

        // Get any child containers & components that need to be displayed in this container
        var children = manywho.model.getChildren(this.props.id, this.props.flowKey);
  
        // The UI framework will handle displaying the label, outcomes & the collapse / expand toggle button, custom containers only need to be concerned about
        // rendering their children. this.props.children will be populated when being rendered at design time in the page layout editor
        return React.DOM.div({ className: "clearfix" }, (this.props.children || manywho.component.getChildComponents(children, this.props.id, this.props.flowKey)) });
 
    }
 
});

// (Required) Register this container with the framework so it can be found later in the rendering cycle
manywho.component.registerContainer('container_name', myContainer);
 
// (Optional) Register this container in the styling framework so that child items can have classes applied based on the parent container
manywho.styling.registerContainer('container_name', function (item, container) {      
    return [];
});

Basic Component

/// <reference path="../../typings/index.d.ts" />
/// <reference path="../interfaces/IComponentProps.ts" />

declare var manywho: any;

interface IMyComponentProps extends IComponentProps {
}

interface IComponentState {
}

class MyComponent extends React.Component<IMyComponentProps, IComponentState> {

    constructor(props: IMyComponentProps){
        super(props);

        this.handleChange = this.handleChange.bind(this);
        this.handleEvent = this.handleEvent.bind(this);
    }

    handleChange(e) {
        // Sync the state of this component into the state store, this will then be sent back to ManyWho
        manywho.state.setComponent(this.props.id, { contentValue: e.target.value }, this.props.flowKey, true);          
        
        // Re-sync with ManyWho if this component hasEvents = true, also re-render the component
        manywho.component.handleEvent(this, manywho.model.getComponent(this.props.id, this.props.flowKey), this.props.flowKey); 
    }

    handleEvent(e) {
        manywho.component.handleEvent(this, manywho.model.getComponent(this.props.id, this.props.flowKey), this.props.flowKey);
    }

    render() {
        // Get the basic info about this component e.g. id, name, etc
        const model = manywho.model.getComponent(this.props.id, this.props.flowKey);

        manywho.log.info(`Rendering My Component: ${this.props.id}, ${model.developerName}`);

        // Get the local state for this component. Local state will be synced with ManyWho, this.state will not be synced.
        const state = manywho.state.getComponent(this.props.id, this.props.flowKey) || {};

        // Get any outcomes that are associated with the component, then transform them into Outcome components 
        const outcomes: any = manywho.model.getOutcomes(this.props.id, this.props.flowKey);
        const outcomeElements: Array<JSX.Element> = outcomes && outcomes
            .map((outcome) => React.createElement(manywho.component.getByName('outcome'), { id: outcome.id, flowKey: this.props.flowKey }));

        // Get the classes that should be applied to this component e.g. mw-component-name
        let className = (manywho.styling.getClasses(this.props.parentId, this.props.id, 'toggle', this.props.flowKey)).join(' ');

        // If the component is invalid then add a has-error class
        if (model.isValid === false || state.isValid === false)
            className += ' has-error';

        // Hide the component if isVisible is false
        if (model.isVisible === false)
            className += ' hidden';

        // Setup the props for the <input> we will be rendering
        const props: any = {
            type: 'text',
            value: state.contentValue || model.contentValue;
            readOnly: !model.isEditable,
            required: model.isRequired,
            disabled: !model.isEnabled,
        }

        // Add event handlers at runtime only
        if (!this.props.isDesignTime) {
            props.onChange = this.handleChange;
            props.onBlur = this.handleEvent;
        }

        return <div className={className} id={this.props.id}>
            <label>{model.label}</label>
            <input {...props} />
            <span className="help-block">{model.validationMessage || state.validationMessage}</span>
            <span className="help-block">{model.helpInfo}</span>
            {outcomeElements}
        </div>
    }
}

// (Required) Register this component with the framework so it can be found later in the rendering cycle. This component has been
// registered with an alias of 'input', this component will be used when a component with the name of 'input' is requested
manywho.component.register('component_name', MyComponent, ['input']);

Component that displays multiple items

If you need to create a component to display multiple items of the same type e.g. table, list, radio, etc Then you should create an items component. The UI framework includes a higher order component that handles selection, pagination & searching that you can register a custom component into.

Take a look at the Table component as an example, or the Select component for a more advanced example.

Overriding a default component

The default containers / components can be overridden by registering your custom component with the same name as the default component. This is only currently supported in custom players, the call to manywho.component.register will need to go at the top of the initialize function in the player HTML.

Loading Custom Components Resources

  1. Upload your resources to a publicly available url (this url will need to support CORS).
  2. Create a custom player using the Player tool in the Admin area.
  3. Alter the manywho object by adding a "customResources" property as shown below.
var manywho = {
    cdnUrl: 'https://assets.manywho.com',
    customResources: [
        "https://mydomain.com/js/test.js"
    ],
    initialize: function () {
        // Your usual initialize code here...
    }
}

Any custom resource file loaded using the customResources property will be included into the player after the manywho resources have finished loading. If you need your resources to load first then add them as script or link tags to the player directly.

Clone this wiki locally