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

Get Fabric Certified for FREE during Fabric Data Days. Don't miss your chance! Learn more

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
Fabric Data Days Carousel

Fabric Data Days

Advance your Data & AI career with 50 days of live learning, contests, hands-on challenges, study groups & certifications and more!

October Power BI Update Carousel

Power BI Monthly Update - October 2025

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

FabCon Atlanta 2026 carousel

FabCon Atlanta 2026

Join us at FabCon Atlanta, March 16-20, for the ultimate Fabric, Power BI, AI and SQL community-led event. Save $200 with code FABCOMM.

Top Solution Authors