Power BI is turning 10! Tune in for a special live episode on July 24 with behind-the-scenes stories, product evolution highlights, and a sneak peek at what’s in store for the future.
Save the dateEnhance your career with this limited time 50% discount on Fabric and Power BI exams. Ends August 31st. Request your voucher.
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!