Power BI is turning 10, and we’re marking the occasion with a special community challenge. Use your creativity to tell a story, uncover trends, or highlight something unexpected.
Get startedJoin 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.
Hello,
I am working on creating a custom visual in Power BI using React. I have been following a tutorial provided by the Microsoft team, and I've successfully implemented the process of sending data from Power BI to my application. Now, I am looking to understand how to send signals from the custom visuals to other visuals. In my exploration, I came across documentation related to 'Host,' 'createSelectionIdBuilder,' and 'createSelectionManager.
While I have already written some code, I am struggling with how to effectively integrate these concepts into my React-based custom visual. If you have any insights or guidance on this matter, I would greatly appreciate it.
capabilities.json file
{
"dataRoles": [
{
"displayName": "Target Values",
"name": "CheckMarksDropDown",
"kind": "Grouping"
}
],
"dataViewMappings": [
{
"conditions": [
{
"CheckMarksDropDown": {
"min": 1,
"max": 1
}
}
],
"categorical": {
"categories": {
"for": {
"in": "CheckMarksDropDown"
}
}
}
}
],
"privileges": []
}
visual.ts
"use strict";
import powerbi from "powerbi-visuals-api";
import DataView = powerbi.DataView;
import VisualConstructorOptions = powerbi.extensibility.visual.VisualConstructorOptions;
import VisualUpdateOptions = powerbi.extensibility.visual.VisualUpdateOptions;
import IVisual = powerbi.extensibility.visual.IVisual;
import * as React from "react";
import * as ReactDOM from "react-dom";
import {
initialState,
CheckMarksDropDown,
} from "./components/CheckMarksDropDown";
import "./../style/visual.less";
export class Visual implements IVisual {
private target: HTMLElement;
private host: powerbi.extensibility.visual.IVisualHost;
private reactRootElement: React.ComponentElement<any, any>;
private selectionManager: powerbi.extensibility.ISelectionManager;
constructor(options: VisualConstructorOptions) {
this.target = options.element;
this.reactRootElement = React.createElement(CheckMarksDropDown, {
state: initialState,
onSelect: this.onSelect.bind(this),
});
this.selectionManager = options.host.createSelectionManager();
this.selectionManager.registerOnSelectCallback(this.onSelect.bind(this));
ReactDOM.render(this.reactRootElement, this.target);
}
public update(options: VisualUpdateOptions) {
console.log({ "Update options": options });
const dataView: DataView = options?.dataViews[0];
if (dataView) {
const targetValues = dataView.categorical.categories[0].values;
CheckMarksDropDown.update({
title: "Zero After Zero",
target: targetValues,
});
} else {
this.clear();
}
}
private onSelect(selectionId: powerbi.extensibility.ISelectionId) {
console.log("Selected DP", selectionId);
}
private clear() {
CheckMarksDropDown.update(initialState);
}
}
CheckMarksDropDown.tsx
import { Box, Select, MenuItem, Checkbox, ListItemText } from "@mui/material";
import * as React from "react";
export interface CheckMarksDropDownProps<T> {
target?: T[];
title?: string;
selectedValues?: T[];
onSelect?: (selectionId: powerbi.extensibility.ISelectionId) => void;
}
export const initialState = {
title: "",
target: [],
selectedValues: [],
};
export class CheckMarksDropDown extends React.Component<
CheckMarksDropDownProps<any>,
CheckMarksDropDownProps<any>
> {
constructor(props) {
super(props);
this.state = initialState;
}
private static updateCallback: (data: object) => void = null;
public static update = (newState: CheckMarksDropDownProps<any>) => {
if (typeof CheckMarksDropDown.updateCallback === "function") {
CheckMarksDropDown.updateCallback(newState);
}
};
public componentDidMount(): void {
CheckMarksDropDown.updateCallback = (
newState: CheckMarksDropDownProps<any>
) => {
this.setState(newState);
};
}
componentWillUnmount(): void {
CheckMarksDropDown.updateCallback = null;
}
render() {
const { title, target, selectedValues } = this.state;
const handleChange = (event) => {
const {
target: { value },
} = event;
this.props.onSelect(value);
this.setState((prevState) => ({
...prevState,
selectedValues: value,
}));
};
return (
<div>
<Box sx={{ my: 2, fontSize: "2vw" }}>{title}</Box>
<Box sx={{ minWidth: 200 }}>
<Select
multiple
fullWidth
value={selectedValues}
onChange={handleChange}
renderValue={(selected) => selected.join(", ")}
MenuProps={{
PaperProps: {
style: {
left: "0 !important",
},
},
}}
>
{target &&
target.map((value) => (
<MenuItem divider key={value} value={value}>
<Checkbox checked={selectedValues.indexOf(value) > -1} />
<ListItemText primary={value} />
</MenuItem>
))}
</Select>
</Box>
</div>
);
}
}
Thank you!
This is your chance to engage directly with the engineering team behind Fabric and Power BI. Share your experiences and shape the future.
Check out the June 2025 Power BI update to learn about new features.