I have been doing a lot with Azure DevOps lately, from building pipelines to dashboard.
As I was building out a dashboard with various widget, I wondered if it was possible to create a custom dashboard widget!
What do you know? You can!
The widget are a combination of HTML, JavaScript and CSS.
So I decided to create a simple sample custom widget that would display two lists, a list of characters from Star Trek, and another list of characters from Star Wars.
This is a very academic sample, just something to get familiar with the widget creation/deployment process.
I started with VS Code to build the custom widget.
I followed the article at https://docs.microsoft.com/en-us/azure/devops/extend/develop/add-dashboard-widget?view=azure-devops, specifically Part 1: Hello World!, to create the bare bones structure.
While the jQuery library is imported by default, I would recommend, where you can, you stick with JavaScript, one less library you need to rely on, and the more I work with JavsScript the more I discover how far it has come with ease of DOM manipulation.
Next, I created two fake APIs using https://www.mocky.io to return my list of characters.
The first API returns a list of Star Trek characters in a JSON
format.
1 2 3 4 5 6 7 8 9 10 11 |
[ { "name":"Jean Luc Picard" }, { "name":"Benjamin Sisko" }, { "name":"Kathryn Janeway" } ] |
The second API returns a list of Star Wars characters in a JSON
format.
1 2 3 4 5 6 7 8 9 10 11 |
[ { "name":"Luke Skywalker" }, { "name":"Anakin Skywalker" }, { "name":"Obi-Wan Kenobi" } ] |
Next, add a widget.js
file to the scripts
directory. This file will contain the JavaScript function to add li
elements to the parent ul
element.
1 2 3 4 5 6 7 |
function createNode(element) { return document.createElement(element); } function append(parent, el) { return parent.appendChild(el); } |
Update your hello-world.html
with the following code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
<!DOCTYPE html> <html> <head> <script src="sdk/scripts/VSS.SDK.min.js"></script> <script src="scripts/widget.js"></script> <script type="text/javascript"> VSS.init({ explicitNotifyLoaded: true, usePlatformStyles: true }); VSS.require("TFS/Dashboards/WidgetHelpers", function(WidgetHelpers) { WidgetHelpers.IncludeWidgetStyles(); WidgetHelpers.IncludeWidgetConfigurationStyles(); VSS.register("HelloWorldWidget", function() { return { load: function(widgetSettings) { document.getElementById("mattruma_title").innerHTML = "Science Fiction Characters"; fetch("https://www.mocky.io/v2/5da5f217340000ef27632e26") .then(resp => resp.json()) .then(function(data) { let ul = document.getElementById("mattruma_links_a"); let items = data; return items.map(function(item) { let li = createNode("li"); li.innerHTML = item.name; append(ul, li); }); }) .then(function() { document.getElementById("mattruma_links_a_loading").remove(); }) .then(function() { fetch("https://www.mocky.io/v2/5da605ea340000d325632e85") .then(resp => resp.json()) .then(function(data) { let ul = document.getElementById("mattruma_links_b"); let items = data; return items.map(function(item) { let li = createNode("li"); li.innerHTML = item.name; append(ul, li); }); }) .then(function() { document .getElementById("mattruma_links_b_loading") .remove(); }); }); return WidgetHelpers.WidgetStatusHelper.Success(); } }; }); VSS.notifyLoadSucceeded(); }); </script> </head> <body> <div class="widget"> <h2 class="title" id="mattruma_title"></h2> <h3>Star Trek</h3> <ul id="mattruma_links_a"> <li id="mattruma_links_a_loading">Loading...</li> </ul> <h3>Star Wars</h3> <ul id="mattruma_links_b"> <li id="mattruma_links_b_loading">Loading...</li> </ul> </div> </body> </html> |
We will want to make sure that our JavaScript library, widget.js
, is pulled in, which is why we add <script src="scripts/widget.js"></script>
to the head
element.
You are now ready to publish your widget, but first you will need to visit the Visual Studio Marketplace Publishing Portal to creating a publisher profile, don’t worry, your widget will only be available to those organizations you share it with.
Package up your extension as explained at https://docs.microsoft.com/en-us/azure/devops/extend/develop/add-dashboard-widget?view=azure-devops#package-your-extension.
In the vss-extension.json
file replace the publisher name fabrikam
with the name of your publisher profile.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
{ "manifestVersion": 1, "id": "vsts-extensions-myExtensions", "version": "1.0.4", "name": "My First Set of Widgets", "description": "Samples containing different widgets extending dashboards", "publisher": "mattruma", "categories": ["Azure Boards"], "targets": [ { "id": "Microsoft.VisualStudio.Services" } ], "icons": { "default": "img/logo.png" }, "contributions": [ { "id": "HelloWorldWidget", "type": "ms.vss-dashboards-web.widget", "targets": ["ms.vss-dashboards-web.widget-catalog"], "properties": { "name": "Hello World Widget", "description": "My first widget", "catalogIconUrl": "img/CatalogIcon.png", "previewImageUrl": "img/preview.png", "uri": "hello-world.html", "supportedSizes": [ { "rowSpan": 2, "columnSpan": 2 } ], "supportedScopes": ["project_team"] } } ], "files": [ { "path": "hello-world.html", "addressable": true }, { "path": "scripts", "addressable": true }, { "path": "sdk/scripts", "addressable": true }, { "path": "img", "addressable": true } ] } |
Some things to be aware of:
As you rebuild your widget, you will want to increment the version number in the vss-extension.json
file.
To include an additional folder, like in my example, the scripts
folder, you will need to include it in the files
node of the vss-extension.json
.
To allow for different sizes for your widget, add them to the supportedSizes
node.
That’s it, pretty straight forward!
Related Links
- https://docs.microsoft.com/en-us/azure/devops/extend/develop/add-dashboard-widget?view=azure-devops
- https://docs.microsoft.com/en-us/azure/devops/extend/develop/manifest?view=azure-devops
- https://docs.microsoft.com/en-us/azure/devops/extend/publish/overview?view=azure-devops
- https://www.mocky.io/
- https://jsfiddle.net/ejno5hax/13/
Discover more from Matt Ruma
Subscribe to get the latest posts sent to your email.
1 Reply to “Adventures with Azure DevOps: Create a Custom Dashboard Widget”