Showing results for 
Search instead for 
Did you mean: 

Part 2 - Building a Custom Power BI Visual from scratch (KPI Card)


This article is a follow-up to the first article where we created a visual from scratch. The approach that we used was very straightforward, we directly used the data interfaces without using any typing. In this article, we will add typed data interface. Instead of reading you can also watch the video. Btw, this article is a re-post from Medium, I am the original author. 

The commit for the above video can be found here and here.


The below text is a slightly structured / trimmed-down version of the transcription of the audio of the above video. The video has a lot of console.log verification steps, while the text below shows the final code changes made.


Adding explicit interfaces and add typing

We’re going to add a couple of additional fields so we not only have the KPI name but also the description and the tooltip.

We’re going to add these fields to the data roles (in capabilities.json).



"dataRoles": [
      "displayName": "KPI",
      "name": "kpi",
      "kind": "Grouping"
      "displayName": "Description",
      "name": "description",
      "kind": "Grouping"
      "displayName": "Tooltip",
      "name": "tooltip",
      "kind": "Grouping"
      "displayName": "Measure Data",
      "name": "measure",
      "kind": "Measure"



What we can now do is get the index position of each of those individual Fields, so for this, we’ll need the data role helper that will help us identify the index position of a specific field. We will import this function.

import { dataRoleHelper } from "powerbi-visuals-utils-dataviewutils";

And we’ll add another array with the categories:

categorical = options.dataViews[0].categorical;

We’re looking to find the index position of a certain field and for this, we can use this data role helper. We’ll add this piece of code where we specify the column that we’re looking for:

let kpiId = dataRoleHelper.getCategoryIndexOfRole(categorical.categories, "kpi")

Let’s see what happens if we drag in the same field, now we have the description in both the tooltip and the description field. We see the tooltip is referring to index position 1 as well, while we might expect this to be number 2.

We can verify because basically, it’s looking into roles so whenever it’s true it returns that position and description is actually like the second data field that we are receiving so, therefore, it’s still accurate what we’re getting. More info is available here.

Role index positionRole index position

Now that we have these explicit interfaces we’re going to apply some typing. We’re going to define a data point interface where we are specifically going to specify each column in this array, it’s going to be a key-value pair.



interface kpiDataPoint {
    index: number,
    kpivalue: string,
    selectionId: ISelectionId,
    colour: string,
    selector: any,
    highlight: string



We can now add a ’For Loop’ whereby we’ll iterate over the KPI names and then populate the array which has been typed and we’ll push in the values that we’re receiving, so first of all uh pick up the KPI values;

let kpiVals = categorical.categories[kpiId]['values'];

So we’ve defined the KPI values, and now we can iterate over them in a for Loop;



for (var i = 0; i < kpiVals.length; i++) 
            let selectionId: ISelectionId =
                .withCategory(categorical.categories[kpiId], i)

                kpivalue : <string>kpiVals[i],
                colour : (category.objects) ? category.objects[i] ? String(<Fill>(category.objects[i].colorSelector.fill['solid']['color'])) : "#FF0000" : "#FF0000", //objects is initially not present
                highlight: (measurevals.highlights) ? (measurevals.highlights[i]) ? "Y" : "N" : "N",
                index: i,
                selectionId: selectionId,
                selector : selectionId.getSelector()



From here, our reference in D3 is then not based on a column index but actually the name of the column, f.e. like here:

.style("fill", (d: kpiDataPoint) => d.colour)


This concludes this article whereby we added typed data interfaces. Stay tuned for more articles on visual development, web-scraping, office automation, etc.

This article is a re-post from Medium, I am the original author. 


What is your favorite Power BI feature release for September 2023?