dropdown
2 TopicsDropdown options list is positined and scaled relatively to browser window instead of the object
Dear Community, I'm experiencin a very annoying issue where the list of options (that appears when clicking the dropdown) is poisitioned and scaled relatively to the browser window but not to the dialogue box of the dropdown (see images). In the image on left I window is 100% scaled and positioned in the middle. On the right I have increased window scaling to ~150% and moved it to the left. As a result of these manipulations the dropdown dialogue box moves and scales as expected, but the list of options stays unoved and unscaled. Here is my visual.ts file: import powerbi from "powerbi-visuals-api"; import "./../style/visual.less"; import { IconType, SVGIcons } from "./../style/svg_icons" import VisualConstructorOptions = powerbi.extensibility.visual.VisualConstructorOptions; import VisualUpdateOptions = powerbi.extensibility.visual.VisualUpdateOptions; import IVisual = powerbi.extensibility.visual.IVisual; import DataView = powerbi.DataView; import * as d3 from "d3"; import { BasicFilter, IFilterColumnTarget } from "powerbi-models"; import IFilter = powerbi.IFilter; import FilterAction = powerbi.FilterAction; export class Visual implements IVisual { // Visual elements private svgRoot: d3.Selection<SVGElement, {}, HTMLElement, any>; private dropdowns: d3.Selection<HTMLSelectElement, {}, HTMLElement, any>[] = []; private clearButtons: d3.Selection<HTMLButtonElement, {}, HTMLElement, any>[] = []; // Parameters private visualHost: powerbi.extensibility.visual.IVisualHost; private currentDataView: DataView | null = null; private postalCode: string = ""; private lob: string = ""; constructor(options: VisualConstructorOptions) { this.visualHost = options.host; this.svgRoot = d3.select(options.element).append("svg") .style("background", "rgb(0, 82, 164)") .style("cursor", "default"); // Dropdowns const dropdownNames = ["Postal Code", "LOB"]; dropdownNames.forEach((name, index) => { this.svgRoot.append("text") .text(name + ":") .attr("x", 10) .attr("y", 90 + index * 40) .style("font-size", "16px") .style("fill", "white"); const dropdown = d3.select(options.element) .append("select") .attr("class", "dropdown-box") .style("position", "absolute") .style("top", `${70 + index * 40}px`) .style("left", "150px") .style("width", "138px") .style("padding", "8px") .style("border", "1px solid rgb(0, 122, 197)") .style("border-radius", "6px") .style("background", "white") .style("color", "black") .style("cursor", "pointer") .on("change", (event) => this.handleDropdownChange(index, event)) .on("click", () => this.clearDropdown(index)); this.dropdowns.push(dropdown); const defaultColor = "rgb(215,215,215)"; const highlightColor = "white" const clearButton = d3.select(options.element) .append("button") .html(SVGIcons.Get_SVG_Icon(IconType.Eraser, defaultColor)) .style("width", "15px") .style("height", "15px") .style("position", "absolute") .style("top", `${80 + index * 40}px`) .style("left", "290px") .style("padding", "0px 0px") .style("border", "none") .style("background", "transparent") .style("cursor", "pointer") .on("mouseover", function () { d3.select(this).select("svg path").attr("fill", highlightColor); }) .on("mouseout", function () { d3.select(this).select("svg path").attr("fill", defaultColor); }) .on("click", () => this.clearDropdown(index)) this.clearButtons.push(clearButton); }); } private handleDropdownChange(index: number, event: any): void { const value = event.target.value; if (index === 0) { this.postalCode = value; } else if (index === 1) { this.lob = value; } if (this.currentDataView) { this.applyFilter(this.currentDataView); } } public update(options: VisualUpdateOptions) { this.currentDataView = options.dataViews[0]; // Save the DataView to a class property this.svgRoot .attr("width", options.viewport.width) .attr("height", options.viewport.height); let dataView: DataView = this.currentDataView; let table = dataView.table; let postalCodeIndex = table.columns.findIndex(col => col.roles["PostalCode"]); let lobIndex = table.columns.findIndex(col => col.roles["LOB"]); if (postalCodeIndex !== -1) { let postalCodes = Array.from(new Set(table.rows.map(row => String(row[postalCodeIndex])))); this.populateDropdown(this.dropdowns[0], postalCodes, this.postalCode); } if (lobIndex !== -1) { let lobs = Array.from(new Set(table.rows.map(row => String(row[lobIndex])))); this.populateDropdown(this.dropdowns[1], lobs, this.lob); } } private populateDropdown(dropdown: d3.Selection<HTMLSelectElement, {}, HTMLElement, any>, values: string[], selectedValue: string): void { dropdown.selectAll("option").remove(); // Clear existing options // Preserve existing selection let isSelectedValueInList = values.includes(selectedValue); // Add options dynamically values.forEach(value => { dropdown.append("option") .text(value) .attr("value", value) .attr("selected", value === selectedValue ? "selected" : null); // Keep previous selection }); if (!isSelectedValueInList) { dropdown.property("value", ""); // Reset only if the selection is invalid } } private applyFilter(dataView: DataView): void { if (!this.visualHost) { console.error("Visual Host is not initialized properly."); return; } const filterValues: string[] = []; let targets: IFilterColumnTarget[] = []; if (this.postalCode) filterValues.push(this.postalCode); if (this.lob) filterValues.push(this.lob); if (dataView?.metadata?.columns) { dataView.metadata.columns.forEach(column => { console.log("Checking Column:", column.displayName, "Query Name:", column.queryName); if (column.displayName === "PostalCode" && this.postalCode) { targets.push({ table: column.queryName.split('.')[0], column: column.displayName }); } if (column.displayName === "LOB" && this.lob) { targets.push({ table: column.queryName.split('.')[0], column: column.displayName }); } }); } console.log("Targets:", targets); console.log("Filter Values:", filterValues); if (targets.length > 0 && filterValues.length > 0) { const filters: IFilter[] = targets.map((target, index) => new BasicFilter(target, "In", [filterValues[index]]) // Use index instead of shift() ); filters.forEach(filter => { this.visualHost.applyJsonFilter(filter, "general", "filter", FilterAction.merge); }); console.log("Filters Applied: ", filters); } else { this.visualHost.applyJsonFilter(null, "general", "filter", FilterAction.merge); console.log("Filters Cleared."); } } private clearDropdown(index: number): void { if (index === 0) { this.postalCode = ""; } else if (index === 1) { this.lob = ""; } this.dropdowns[index].property("value", ""); this.visualHost.applyJsonFilter(null, "general", "filter", FilterAction.merge); console.log(`Dropdown ${index} cleared.`); } } Thank you very much in advance! Kind regards, ArtemCreating excel like cell-based dropdown in Power BI Table
Hi, I am new to Power BI and was looking for ways to create a status update table in Power BI to track progress of certain tasks. The table in excel would look like this I want to create a similar table where I would be able to update the status in the visuals itself. Is that possible to do in Power BI? Thanks in advance for any help!