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

Find everything you need to get certified on Fabric—skills challenges, live sessions, exam prep, role guidance, and more. Get started

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
Sept PBI Carousel

Power BI Monthly Update - September 2024

Check out the September 2024 Power BI update to learn about new features.

September Hackathon Carousel

Microsoft Fabric & AI Learning Hackathon

Learn from experts, get hands-on experience, and win awesome prizes.

Sept NL Carousel

Fabric Community Update - September 2024

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

Top Kudoed Authors