com.atlassian.confluence.content.render.xhtml.migration.exceptions.UnknownMacroMigrationException: The macro 'next_previous_link3' is unknown.

Creating Custom Widgets

In addition to creating a widget using widget generation wizard, you can implement your own custom widgets and use them within the Dashboard portal.

If you want to refer the source of a sample widget to develop your custom widget, you can find the available sample widgets in the WSO2 Stackblitz account.

For instructions to create your custom widget by editing a sample widget, see Using Sample Widgets to Create Custom Widgets.

 This section explains:

  • How to write a custom widget for the Dashboard portal 
  • The features that are provided for widget developers in the Dashboard portal

A widget in Dashboard portal is a simple ReactJS component that can be used to visualize information. This widget functions as a chart, table, set of components to interact with, etc.

In order to identify a particular ReactJS component  as a widget by the dashboard portal, it needs to register itself as a widget in the portal.

Let’s have a look at a simple widget, Hello World.

Creating HelloWorld widget

The directory structure of your HelloWorld widget source s as follows

HelloWorld (referred as <WIDGET_ROOT>)
├── package.json
├── src
│   ├── HelloWorld.jsx
│   └── resources
│       └── widgetConf.json
└── webpack.config.js


  1. Copy following content to package.json and webpack.config.js respectively. Webpack is required for building a widget.

    package.json
    {
        "name": "hello-world-widget",
        "version": "1.0.0",
        "private": true,
        "dependencies": {
            "react": "^16.1.1",
            "react-dom": "^16.1.1"
        },
        "scripts": {
            "build": "webpack -p"
        },
        "devDependencies": {
            "webpack": "^3.5.6",
            "ajv": "^5.2.2",
            "babel-core": "^6.25.0",
            "babel-loader": "^7.1.1",
            "babel-preset-es2015": "^6.24.1",
            "babel-preset-react": "^6.24.1",
            "babel-register": "^6.26.0",
            "copy-webpack-plugin": "^4.2.0",
            "node-sass": "^4.5.3",
            "sass-loader": "^6.0.6"
        }
    }
    webpack.config.js
    const path = require('path');
    const webpack = require('webpack');
    const CopyWebpackPlugin = require('copy-webpack-plugin');
    
    module.exports = {
        context: path.resolve(__dirname, './src'),
        entry: {
            index: './HelloWorld.jsx'
        },
        output: {
            path: path.resolve(__dirname, './dist/HelloWorld/'),
            filename: 'HelloWorld.js'
        },
        module: {
            loaders: [
                {
                    test: /\.html$/,
                    use: [{loader: 'html-loader'}]
                },
                {
                    test: /\.js$/,
                    exclude: /node_modules/,
                    use: [
                        {
                            loader: 'babel-loader',
                            query: {
                                presets: ['es2015', 'react']
                            }
                        }
                    ]
                },
                {
                    test: /\.(png|jpg|svg|cur|gif|eot|svg|ttf|woff|woff2)$/,
                    use: ['url-loader']
                },
                {
                    test: /\.jsx?$/,
                    exclude: /(node_modules)/,
                    loader: 'babel-loader',
                    query: {
                        presets: ['es2015', 'react']
                    }
                },
                {
                    test: /\.css$/,
                    use: ['style-loader', 'css-loader']
                },
                {
                    test: /\.scss$/,
                    use: [{loader: 'style-loader'}, {loader: 'css-loader'}, {loader: 'sass-loader'}]
                }
    
            ]
        },
        plugins: [
            new CopyWebpackPlugin([
                {from: path.resolve(__dirname, './src/resources/')}
            ])
        ],
        resolve: {
            extensions: ['.js', '.json', '.jsx', '.scss']
        }
    };
  2. Now copy the following content into HelloWorld.jsx under src directory. This contains all the logic for our widget.

    HelloWorld.jsx
    import React, { Component } from 'react';
    
    class HelloWorld extends Component {
        render() {
            return (
                <h1>Hello, World!</h1>
            );
        }
    }
    
    global.dashboard.registerWidget('HelloWorld', HelloWorld);

    Note that we have registered the react component as a widget in the Dashboard portal by calling the registerWidget function where the first argument is the ID you want to register your widget, and the second argument is the react component.

    global.dashboard.registerWidget('HelloWorld', HelloWorld);
  3. Dashboard portal requires few meta information regarding the widget in order to identify it. <WIDGET_ROOT>/src/resources/widgetConf.json file contains this meta information. When building the widget this configuration file gets copied into the final widget directory. Add the following content into the widgetConf.json file.

    widgetConf.json
    {
        "name": "HelloWorld",
        "id": "HelloWorld",
        "thumbnailURL": "",
        "configs": {}
    }
  4. Now the source of the widget is complete. To install the dependencies required to to build your widget, navigate to the <WIDGET_ROOT> directory and issue the following command.

    npm install
  5. Go to the <WIDGET_ROOT> directory and issue the following command to build the widget.

    npm run build
  6. Once the build is successful the final widget directory is created in the <WIDGET_ROOT>/dist directory. Copy the <WIDGET_ROOT>/dist/HelloWorld directory into the <SP_HOME>/wso2/dashboards/deployment/web-ui-apps/portal/extensions/widgets directory.
  7. Restart WSO2 Stream Processor.
  8. Now log in to the Dashboard portal and create a new dashboard .

    For instructions to access the Dashboard Portal, see Visualizing Data.

    For instructions to create a new dashboard, see Creating New Dashboards.


    You can view the newly created HelloWorld widget in the widget listing panel of the Dashboard Designer.

Now you understand how to develop a widget and get it rendered in the Dashboard portal.

Let's improve this widget to add more interactive elements to it.

  1. Clone the WSO2 carbon-dashboards repository to your machine.
  2. Copy the HelloWorld widget you created to the carbon-dashboards/samples/widgets directory.
  3. To import the channel manager to access the database, you need to use the WidgetChannelManager. To do this, create a separate file named ExtendedWidget as shown below.

    import React from 'react';
    import Widget from '@wso2-dashboards/widget';
    import WidgetChannelManager from '../../../../components/dashboards-web-component/src/utils/dashboard-channel/WidgetChannelManager';
    
    let widgetChannelManager = null;
    
    export default class ExtendedWidget extends Widget {
    
        constructor(props) {
            super(props);
        }
    
        getWidgetChannelManager() {
            if(!widgetChannelManager) {
                widgetChannelManager=new WidgetChannelManager();
            }
    
            return widgetChannelManager;
        }
    
    }
  4. Update the  HelloWorld.jsx file as shown below. 

    • The react-vizgrammar module is a library that is used to create the charts in the widget named ExtendedWidget that you previously created. To add the config, metadata and data fields of the table, you can find the required instructions in react-vizgrammar - Table Chart Samples.
    • Here, console.info is the action of the row click. You can use the required function that is related to the second widget as the handleData function in your implementation.
    import React, { Component } from 'react';
    import ExtendedWidget from './ExtendedWidget';
    import VizG from 'react-vizgrammar';
    
    class HelloWorld extends ExtendedWidget {
        constructor(props) {
             super(props);
    
    
             this.tableConfig = {
                 //add table configues 
             };
    
             this.metadata = {
                 //add meta data
             };
    
             this.data = [
                 //add table data here
             ];
    
             this.handleData = this.handleData.bind(this);
        }
    
        render() {
            return (
                <VizG config={this.tableConfig} data={this.data} metadata={this.metadata} onClick={this.handleData} />
            );
        }
    
        handleData(row) {
            console.info(row);
        }
    }
    
    global.dashboard.registerWidget('HelloWorld', HelloWorld); //(widgetId,reactComponent)
    
    
  5. If you consider the publisher-subscriber concept, the widget with the table is a publisher and the widget that triggers the table action with a row click is a subscriber. To define this:
    • Open the widgetConf.json file of the publisher widget and change the pubsub type to publisher as shown below.

      {
        "name": "HelloWorld",
        "id": "HelloWorld",
        "thumbnailURL": "",
        "configs": {
          "pubsub": {
            "types": ["publisher"]
          }
        }
      }
    • Similarly open the widgetConf.json file of the subscriber widget and change the pubsub type to subscriber.
  6.  Navigate to the <WIDGET_ROOT> directory and issue the following command to build the widget.
    npm run build

    Once the build is successful, the widget is created in the <WIDGET_ROOT>/dist directory.
  7. Copy the <WIDGET_ROOT>/dist/HelloWorld directory and place it in the <SP_HOME>/wso2/dashboards/deployment/web-ui-apps/portal/extensions/widgets directory.

The Dashboard portal also provides additional capabilities to widget developers by providing a set of APIs via a base widget component. Please follow the step below to extend your widget from the base widget component.

Extending from base widget component

To extend from the base widget component, follow the steps below:

The base widget version compatible with WSO2 SP 4.2.0 is @wso2-dashboards/widget: 1.2.2.

  1. Add base widget as a dependency in your package.json file.

    'dependencies': {
    	...
    	'@wso2-dashboards/widget': '1.1.1.',
    	...
    },
  2. Import base widget in your widget source file.

    import Widget from '@wso2-dashboards/widget';
  3. Extend the base widget.

    class MyWidget extends Widget {
    	...
    }


Base widget component provides following capabilities for a widget author. In order to consume these capabilities from your custom widget, extend your widget from the above Widget class. 

Resolving CSS style conflicts

When creating custom widgets there is a possibility of getting styles conflicts since all the widgets are rendered within a single page. There are certain practices a widget developer can follow to overcome this issue. To understand these practices follow "Resolving CSS style conflicts".

Retrieving the information to be displayed

To retrieve information to be displayed in this widget via an RDBMS data provider, follow the steps below:

  1. Access the data provider configurations in <SP_HOME>/deploment/web-ui-apps/portal/extensions/widgets/<WIDGET_NAME>/widgetConf.json file as specified in Accessing Widget Configuration.
  2.  Use the base widget API to create the WebSocket connection. For this, you can use the following APIs:
    • Subscribe to the endpoint

      API Syntax super.getWidgetChannelManager( ).subscribeWidget(<Widget id>, <Call back method to handle data>, <data provider configs>)
      Example

      super.getWidgetChannelManager()
      .subscribeWidget(this.props.id , this.handleDataReceived , dataProviderConfigs));

    • Unsubscribe from the endpoint

      API Syntax super. getWidgetChannelManager(). unsubscribeWidget(<Widget ID>);
      Example
                        super.getWidgetChannelManager().unsubscribeWidget(this.props.id);
                      
com.atlassian.confluence.content.render.xhtml.migration.exceptions.UnknownMacroMigrationException: The macro 'next_previous_links2' is unknown.