Loading…

Creating Universal Dashboard Components – Part 1

Now that Universal Dashboard is open source, you can add your own components to the ecosystem. In this blog post, we’ll go over the steps needed to develop a new component for Universal Dashboard.

Setting Up Your Development Environment

You’ll need to install the .NET Core SDK and Node JS to build Unviersal Dashboard. I would suggest installing VS Code for development. You’ll want to download Git to get the source code.

Building for the first time

First, you’ll need to clone the repository.

git clone https://github.com/ironmansoftware/universal-dashboard.git

Next, you should be able to run the build script to build and scaffold the module. The build script takes care of downloading NuGet and NPM packages, building markdown into MAML help, building the .NET Core and JavaScript components and then finally laying out the module into an output directory found in the src folder.

You can run the build script in PowerShell.

.\src\build.ps1

As long as everything goes smoothly, you’ve now built and packaged your own verson of Universal Dashboard!

Build a new control

Universal Dashboard controls are three parts: PowerShell cmdlet, a C# model class and a React JavaScript component. All the plumbing to hook up those controls to the browser from a PowerShell script is already done by the UD framework.

In this example, we are going to add a Treeview control to the platform.

You can approach this from either developing the client JavaScript side or the cmdlet side first. Usually, I try and define how the user experience will be in PowerShell and then develop the JavaScript component. That’s what we will do now.

Server Side Code

A tree view is easy to represent in PowerShell. We can achieve this by implement two cmdlets.

First, we’ll make the tree node class. This will be serialized and sent to the client during requests.

public class TreeNode
{   
    [JsonProperty("name")]
    public string Name { get; set; }
    [JsonProperty("children")]
    public IEnumerable<TreeNode> Children { get; set; }
}

Next, we’ll make a tree node cmdlet. We can use a ScriptBlock for the child nodes. This allows us to create a domain-specific language in PowerShell.

[Cmdlet(VerbsCommon.New, "UDTreeNode")]
public class NewTreeNodeCommand : PSCmdlet {
    [Parameter(Mandatory = true)]
    public string Name { get; set; }
    [Parameter]
    public ScriptBlock Children { get; set; }

    protected override void EndProcess() {
        WriteObject(new TreeNode {
            Name = Name,
            Children = Children?.Invoke().Select(m => m.BaseObject).Cast<TreeNode>()
        })
    }
}

The result of this cmdlet design allows us to use the cmdlet like this.

New-UDTreeNode -Name "Root" -Children {
    New-UDTreeNode -Name "Child 1" 
    New-UDTreeNode -Name "Child 2" -Children {
        New-UDTreeNode -Name "Nested"
    }
    New-UDTreeNode -Name "Child 3" 
}

Next, we need to define our TreeView component class. It needs to extend from Component and set an appropriate type name.

public class TreeView : Component
{
    [JsonProperty("type")]
    public override string Type => "treeview";

    [JsonProperty("name")]
    public TreeNode Node { get; set; }
}

Now we can define our TreeView cmdlet. All components in UD are expected to have a type property so that the UI knows which control to render.

[Cmdlet(VerbsCommon.New, "UDTreeNode")]
public class NewTreeNodeCommand : PSCmdlet {
    [Parameter(Mandatory = true)]
    public TreeNode Node { get; set; }

    protected override void EndProcess() {
        WriteObject(new TreeView {
            Node = Node
        })
    }
}

Finally, you need to add the cmdlets to the modul manifest. This can be done in the New-UDModuleManifest.ps1 file in .\src\UniversalDashboard.

Now that we have the server side components created, we can create the client side code.

Client Side Code

The first step is to install any NPM packages that we need. Instead of developing an entire TreeView control ourselves, let’s just use one that’s available. We can install the react-treebeard package by using the following command line.

cd .\src\client
npm install --save react-treebeard

The NPM command installs the package locally and saves the package registration into the package.json folder so that other machines will pull the package as well.

To create a new client side component, we need to create a JSX file within the UD app. These files can be found in .\src\client\app. We’ll just call our component ud-treeview.jsx. React components extend from React.Component and must define a render function to return the component to render to the screen. A very simple implementation of the tree view would be to import the tree view component and return it from the render function.

The props for the React control will have all the properties that we defined on our TreeView and TreeNode classes. We can use them to set the data for the Treebeard control.

import React from 'react';
import {Treebeard} from 'react-treebeard'

export default class UDTreeView extends React.Component {
    render() {
        return (
            <Treebeard data={this.props.node} />
        )
    }
}

To make sure our control is rendered by UD, we need to add it to the render service. The render service is in .\src\client\app\services\render-service.jsx.

First, we need to import our component.

import TreeView from './../ud-treeview.jsx';

Next, we need to check the type property and create our component.

case "treeview":   
    return <TreeView {...component} key={component.id} />;

We should now be able to create a dashboard with our new tree view component. To test our component, we first need to build the debug version of UD. You can run dotnet build from the .\src directory to do this.

cd .\src
dotnet build

Next, you’ll want to start the Webpack Dev Server. This tool compiles the JSX files into JS and then serves them via a built in webserver. Everytime the JSX files are modified on disc, it automatically rebuilds the JS and reloads the page. There is no need to manually compile the JS manually over and over again. This is started via an npm task.

cd .\src\client
npm run dev

The webpack dev server runs on port 10000. You’ll want to start your test dashboard on port 10001. From there, we are up and running.

$Dashboard = New-UDDashboard -Title "TreeView" -Content {
    New-UDTreeNode -Text "Root" -Children {
        New-UDTreeNode -Text "Child 1" 
        New-UDTreeNode -Text "Child 2" -Children {
            New-UDTreeNode -Text "Nested"
        }
        New-UDTreeNode -Text "Child 3" 
    }
}
Start-UDDashboard -Port 10001 -Dashboard $Dashboard

You can now visit your page on port 10000. The tree view will appear in the page!

The tree view won’t actually work yet so we need to add a bit more logic to the React control.

constructor(props){
    super(props);
    this.state = {};
    this.onToggle = this.onToggle.bind(this);
}
onToggle(node, toggled){
    if(this.state.cursor){this.state.cursor.active = false;}
    node.active = true;
    if(node.children){ node.toggled = toggled; }
    this.setState({ cursor: node });
}
render(){
    return (
        <Treebeard
            data={this.props.node}
            onToggle={this.onToggle}
        />
    );
}

Now the tree control has a toggle function. The page should have automatically reloaded with your changes and you should be able to click the tree view to expand the nodes.

The code for this example has been checked into the Universal Dashboard project via this commit. You can see all the source that was used to develop this example.

In part 2 of this blog post, we will look at how to make the control dynamic and provide auto reload functionality as well as interact with other components inside UD.

Leave a Reply