Skip to main content
cancel
Showing results for 
Search instead for 
Did you mean: 

Join us for an expert-led overview of the tools and concepts you'll need to become a Certified Power BI Data Analyst and pass exam PL-300. Register now.

Reply
SKYViz
Frequent Visitor

How to implement colorPicker in custom visual for updating color of Pie charts slices?

Hello Everyone,

I have developed the pie chart custom visual by taking the reference from the PowerBI-visuals-sampleBarChart.
Pie chart has been created and default color has been assigned to each slice using the colorPallete.

However, I am struggling with the implementation of functionality for updation of color of Piechart's slices from formatting Pane. Selected color from the formatting Pane is not updated in the visual.

SKYViz_0-1723638369392.png

 

 

Black is selected for the "West" region.  However, color is not updated.



Please find out the code:
visual.ts:

 

 

 

 

import FormattingSettingsColorPicker = formattingSettings.ColorPicker;
import * as d3 from "d3";
import { axisBottom, axisLeft, scaleLinear, scaleBand } from "d3";
import powerbi from "powerbi-visuals-api";
import { formattingSettings, FormattingSettingsService } from "powerbi-visuals-utils-formattingmodel";
import "./../style/visual.less";
import VisualConstructorOptions = powerbi.extensibility.visual.VisualConstructorOptions;
import VisualUpdateOptions = powerbi.extensibility.visual.VisualUpdateOptions;
import IVisual = powerbi.extensibility.visual.IVisual;
import DataView = powerbi.DataView;
import IVisualHost = powerbi.extensibility.visual.IVisualHost;
type Selection<T extends d3.BaseType, U = any, V extends d3.BaseType = any, W = any> = d3.Selection<T, U, V, W>;
import { Card, ColorPicker, Slice } from "powerbi-visuals-utils-formattingmodel/lib/FormattingSettingsComponents";
import { TextProperties } from "powerbi-visuals-utils-formattingutils/lib/src/interfaces";
import { valueFormatter } from "powerbi-visuals-utils-formattingutils";
import {createTooltipServiceWrapper, ITooltipServiceWrapper} from "powerbi-visuals-utils-tooltiputils";
import ISelectionManager = powerbi.extensibility.ISelectionManager;
import { dataViewObjects} from "powerbi-visuals-utils-dataviewutils";
import IColorPalette = powerbi.extensibility.IColorPalette;
import { VisualSettings } from "./settings";
import ISandboxExtendedColorPalette = powerbi.extensibility.ISandboxExtendedColorPalette;
import DataViewCategoryColumn = powerbi.DataViewCategoryColumn;
import DataViewObjectPropertyIdentifier = powerbi.DataViewObjectPropertyIdentifier;
import Fill = powerbi.Fill;
import { legend, legendInterfaces } from 'powerbi-visuals-utils-chartutils';
import ILegend = legendInterfaces.ILegend;
import LegendPosition = legendInterfaces.LegendPosition;
import positionChartArea = legend.positionChartArea;
import createLegend = legend.createLegend;
import ISelectionId = powerbi.visuals.ISelectionId;

export interface PieChartDataPoint {
    category: string;
    color: string;
    data:any;
    selectionId: ISelectionId;
    slice:string;
    size:number;

}

function getColumnColorByIndex(
    category: DataViewCategoryColumn,
    index: number,
    colorPalette: ISandboxExtendedColorPalette,
): string {
    if (colorPalette.isHighContrast) {
        return colorPalette.background.value;
    }

    const defaultColor: Fill = {
        solid: {
            color: colorPalette.getColor(`${category.values[index]}`).value,
        }
    };

    const prop: DataViewObjectPropertyIdentifier = {
        objectName: "colorSelector",
        propertyName: "fill"
    };

    let colorFromObjects: Fill;
    if(category.objects?.[index]){
        colorFromObjects = dataViewObjects.getValue(category?.objects[index], prop);
    }

    return colorFromObjects?.solid.color ?? defaultColor.solid.color;
}


function createSelectorDataPoints(options: VisualUpdateOptions, host: IVisualHost): PieChartDataPoint[] {
    console.log("createSelectorDataPoints function called");

    const pieChartDataPoints: PieChartDataPoint[] = [];

    let viewport = options.viewport;
    let dataView = options.dataViews;
    console.log("dataView", dataView);

    console.log("dataView length", dataView.length);

    let data = dataView[0];
    console.log("data[0]", data);
    let categorical = data.categorical;
    let categories = categorical.categories;
    let values = categorical.values;
    console.log("categorical", values);
    let slice = categories[0];
    let size = values[0];

    let xValues = slice.values;
    let sizeValues = size.values;

    console.log("xValues",xValues)

    const colorPalette: ISandboxExtendedColorPalette = host.colorPalette;

    for (let i = 0; i < xValues.length; i++) {
        let slice = xValues[i].toString();
        let size = +sizeValues[i];

        const color = colorPalette.getColor(slice).value;

        const selectionId: ISelectionId = host.createSelectionIdBuilder()
            .withCategory(categories[0], i)
            .createSelectionId();

        pieChartDataPoints.push({
            slice,
            size,
            color,
            selectionId,
            data, 
            category: slice 
        });  
    }

    console.log("pieChartDataPoints", pieChartDataPoints); 

    return pieChartDataPoints;
}

  
export class Visual implements IVisual {

    private textNode: Text;
    private formattingSettingsService: FormattingSettingsService;
    private selectionManager: ISelectionManager;
    private formattingSettings: VisualSettings;
    private svg: Selection<SVGGraphicsElement>
    private parentGroup: Selection<SVGElement>;
    private pieGroup: Selection<SVGElement>;
    private tooltipServiceWrapper: ITooltipServiceWrapper;
    private host: IVisualHost;
    private legend: ILegend;
    private colorPalette: IColorPalette;
    private pieDatapoints: PieChartDataPoint[];
    static margins = { top: 30, right: 30, bottom: 30, left: 30 };

    constructor(options: VisualConstructorOptions) {
        console.log('Visual constructor', options);
        this.formattingSettingsService = new FormattingSettingsService();
        this.svg = d3.select(options.element).append('svg').classed('pie', true);
        this.parentGroup = this.svg.append('g').attr('class', 'parent');
        this.pieGroup = this.parentGroup.append('g').attr('class', 'dots');
        this.host = options.host;
        this.colorPalette = options.host.colorPalette; 
        this.selectionManager = options.host.createSelectionManager();

     this.tooltipServiceWrapper = createTooltipServiceWrapper(this.host.tooltipService, options.element);

    }

    public update(options: VisualUpdateOptions) {
        this.formattingSettings = this.formattingSettingsService.populateFormattingSettingsModel(VisualSettings, options.dataViews);
        this.pieDatapoints = createSelectorDataPoints(options, this.host);
        this.formattingSettings.initColors(this.pieDatapoints);
       let viewport = options.viewport;
       this.svg.attr('height', viewport.height).attr('width', viewport.width);

        this.parentGroup.attr('transform', 'translate(' + viewport.width / 2 + ',' + viewport.height / 2 + ')');

        let radius = Math.min(viewport.width, viewport.height) / 4;
        let pie = d3.pie<PieChartDataPoint>().value(function (d) {
            console.log("arcd",d.size);
            return d.size;
        });

        let g = this.pieGroup.selectAll(".arc")
            .data(pie(this.pieDatapoints))
            .enter();

        let arc = g.selectAll("arc")
            .data(pie(this.pieDatapoints))
            .enter();
console.log("arc",arc);
        let path = d3.arc()
            .outerRadius(radius)
            .innerRadius(0);
        arc.append("path")
            .attr("d", function (d) { return path(d as any); })
            .attr("fill", function (d) {
                return d.data.color;
            })     
    }

    public getFormattingModel(): powerbi.visuals.FormattingModel {
        return this.formattingSettingsService.buildFormattingModel(this.formattingSettings);
    }
}

 

 

 

 


Settings.ts:

 

 

 

 

"use strict";
import { formattingSettings } from "powerbi-visuals-utils-formattingmodel";
import FormattingSettingsCard = formattingSettings.Card;
import FormattingSettingsColorPicker = formattingSettings.ColorPicker;
import FormattingSettingsModel = formattingSettings.Model;
import FormattingSettingsSlice = formattingSettings.Slice;
import { ColorHelper } from "powerbi-visuals-utils-colorutils";

import {PieChartDataPoint} from "./visual";
import { Visual } from "./visual";
import { Card, ColorPicker, Slice } from "powerbi-visuals-utils-formattingmodel/lib/FormattingSettingsComponents";


export class ColorSelectorCardSettings extends Card {
    name: string = "colorSelector";
    displayName: string = "Data Colors";
    slices: Slice[] = [];
}

export class VisualSettings extends FormattingSettingsModel {
    colorSelector = new ColorSelectorCardSettings();
    cards: Card[] = [this.colorSelector];

    public initColors(dataPoints:PieChartDataPoint[]) {

        const slices:Slice[]=this.colorSelector.slices;
        if (dataPoints) {
            dataPoints.forEach(dataPoint => {
                slices.push(new ColorPicker({
                    name: "fill",
                    displayName: dataPoint.slice,
                    value: { value: dataPoint.color },
                    selector: dataPoint.selectionId.getSelector(),
                }));
            });
        }

        console.log("slices",slices)
    }

}

 

 

 

 


Capabilities.json:

 

 

 

 

{
    "dataRoles": [
        {
            "displayName": "Slice",
            "name": "slice",
            "kind": "Grouping"
        },
        {
            "displayName": "Value",
            "name": "size",
            "kind": "Measure"
        }
    ],
    "dataViewMappings": [
        {
            "categorical": {
                "categories": {
                    "select": [
                        {
                            "for": { "in": "slice" }
                        }
                    ]
                },
                "values": {
                    "select": [
                        {
                            "bind": {
                                "to": "size"
                            }
                        }
                    ]
                }
            }
        }
    ],

    "objects": {
        "colorSelector": {
            "properties": {
                "fill": {
                    "type": { "fill": { "solid": { "color": true } } }
                }
            }
        }
    },
    "privileges": []
}

 

 

 

 



Basically, I am having difficulties in implementing logic for updating the color after selection from the color Picker. However, still I am unaware if I miss something more.

 

 

 

 

        arc.append("path")
            .attr("d", function (d) { return path(d as any); })
            .attr("fill", function (d) {
                return ordScale(d.data.slice).toString();
            })

 

 

 

 


Thank You!

1 ACCEPTED SOLUTION
SKYViz
Frequent Visitor

I have managed to fix the issue:

const color = colorPalette.getColor(slice).value;
Replace the above line of code with:
const color: string = getColumnColorByIndex(categories[0], i, colorPalette);

View solution in original post

1 REPLY 1
SKYViz
Frequent Visitor

I have managed to fix the issue:

const color = colorPalette.getColor(slice).value;
Replace the above line of code with:
const color: string = getColumnColorByIndex(categories[0], i, colorPalette);

Helpful resources

Announcements
Join our Fabric User Panel

Join our Fabric User Panel

This is your chance to engage directly with the engineering team behind Fabric and Power BI. Share your experiences and shape the future.

June 2025 Power BI Update Carousel

Power BI Monthly Update - June 2025

Check out the June 2025 Power BI update to learn about new features.

June 2025 community update carousel

Fabric Community Update - June 2025

Find out what's new and trending in the Fabric community.

Top Solution Authors
Top Kudoed Authors