Sample ReactJS Custom Card

Required reading card(s)

If you are on our support site you can see the customization here. It is not visible from our public documentation. It consists of 3 main components. We will only be showing the sample code for the first here as an example. 

The first component is the Required reading banner. When added to the template of a page all users will be notified to confirm reading of the page when they visit it. Once confirmed, the will no longer see the banner.

The second part of this customization is a report that is visible in the left hand column. It shows who has confirmed reading, and who has visited the page without confirming. It also allows for a reset of the page confirmation. If you are on our support site, feel free to play around with the customizaton to see how the two components interact with each other client side. Normally, the report card would be limited to users with edit permissions to the page. This sample has been modified for demo purposes.

The third part is a custom handler the was created as a single file and placed in the Extensions directory of the application. This type of server side file is dynamically compiled and persists upgrades. It is yet another mechanism available to extend your ThoughtFarmer intranet. Contact if you require further information.

Required Reading JSX

The JSX code for the top required reading banner card can be seen below. In line comments have been added to demonstrate some key concepts. Detailed documentation on the overall structure and creation of React components can be found at 
var TFC_RequiredReading = React.createClass({ 
    customActionId: '',
    getDefaultProps : function(){
        var page = ctx.customPortlets.getPage();
        return {
            customPortletId: 2142016, // We hard code the
            key: 'userHasReadContent_{0}_{1}'.format(ctx.session.userId, page.contentId)
    getInitialState: function() {
        return {
            hasConfirmedView : false,
            firstLoad : true
    handleReset: function(payload){
        // As part of the flux pattern the ThoughtFarmer dispatcher will execute 
        // this callback we registered in componentDidMount.
        // We always need to check this is the action we want
        if(payload.action === 'tfcRequiredReadingReset'){
    loadData: function(){

    // the following getData call is documented on our <a href="" title="JavaScript API">JavaScript API</a> page
    this.props.ctx.customPortlets.getData(this.props.customPortletId, this.props.key, function(key, value){
        if(self.isMounted()) {
            var hasConfirmed = value !== null &amp;&amp; value.toLowerCase() !== 'false';
            this.setState({hasConfirmedView: hasConfirmed, firstLoad: true});
confirmView: function() {
    var self = this;
    this.props.ctx.api.setCustomPortletData(this.props.customPortletId, this.props.key, 'true', function(response){

        // Here we are setting up a custom action to dispatch. 
        // Another React component can be registered to listen to this action.
        // You can see in componentDidMount and handleReset the listening pattern
                        action: 'tfcRequiredReadingConfirm'            
            self.setState({hasConfirmedView : true, firstLoad : false});
componentDidMount: function() {        
    this.customActionId = this.props.ctx.appDispatcher.register(this.handleReset);
componentWillUnmount: function() {
render: function(){
    if(this.state.firstLoad &amp;&amp; this.state.hasConfirmedView) {
        return null;
    } else {
        // The following component is defined below
        return &lt;TFC_ConfirmReadingMessage hasConfirmedView={this.state.hasConfirmedView} confirmView={this.confirmView} /&gt;;


var TFC_ConfirmReadingMessage = React.createClass({
render: function(){ if(!this.props.hasConfirmedView) { return <div className="tfc-confirm-reading error"> <span className="tfc-confirm-label">Please confirm reading this page </span><br /> <Button onClick={this.props.confirmView}>I confirm</Button> </div>; } else { return <div className="tfc-thankyou success"> Thank you for confirming. </div>; } } });

// This call replaces the view for the custom card. It is required in order to see the components. replaceView(<TFC_RequiredReading ctx={ctx} />);