positioning
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, ArtemIs there a stated roadmap for Report Server that compares its intended feature set with PBI Service?
We are evaluating Power BI Report Server as a potential internal service for sharing/collaborating with Power BI Desktop-authored dashboards and visuals. Is it Microsoft's intent to replicate all the features of the Power BI Service into Power BI Report Server? (I assume not.) Do you have any guidance to help us assess what Report Server is going to be, and how it is going to evolve? Thanks, Tom LJ2.3KViews0likes1Comment