Forum Discussion
Sliding legend
Could you please provide code of visual, where this legend is implemented?
I have some difficulties with adding it by github guide.
Regards,
Yerkhan
I had a look at this, and the documentation of this library is quite out of date, and there's a number of quirks to getting it to work properly, which didn't help.
It took me about 3 hours to re-learn for the new v3 SDK and work out how to avoid breaking it, so I can understand why you're having trouble. I haven't looked at it in quite some time and it definitely used to work better. It's going to be fun to migrate some of my stuff to this version... :smileyfrustrated: I hear the team are working on improving the documentation... I really hope so.
To get this to work, I just created a new visual using pbiviz new and then removed all unnecessary code, but I'll paste my visual.ts below for you.
Things to bear in mind:
- Chartutils needs to be installed as a dependency:
npm i powerbi-visuals-utils-chartutils
- Critical references are the LegendData and LegendDataPoint interfaces, in terms of getting your data in there.
- Despire what the definition says, you need the following properties setting for each LegendDataPoint to ensure it renders without erroring:
- label
- color
- identity - this requires building a Selection ID and doesn't seem to work if you leave it null
- selected
- markerShape
- Invoking the drawLegend method will add a SVG element to the DOM. You will need to manage the sizing changes to your plot area after this is drawn.
- This can be done with the getMargins / getOrientation methods and adjusting your other elements accordingly.
- You can refer to the source of the violin plot for some examples of how this has been done, although it uses the older SDK so isn't subject to some of the stuff going on here, but I'd suggest keeping to the 1.2.0 code for reference; 1.3.0 gets a bit weird as it's manipulating the legend post-render.
- There's no error handling in the update method, so you'll need to ensure that you have a something in the Category field.
- I've added comments at the appropriare location in the imports to show where I've started to add stuff in.
Here's how it looks for a simple data set:
And, dragging the viewport across will cause the chart to scroll, e.g.:
Here's the code from my visual.ts:
"use strict";
import "core-js/stable";
import "./../style/visual.less";
import powerbi from "powerbi-visuals-api";
import VisualConstructorOptions = powerbi.extensibility.visual.VisualConstructorOptions;
import VisualUpdateOptions = powerbi.extensibility.visual.VisualUpdateOptions;
import IVisual = powerbi.extensibility.visual.IVisual;
import EnumerateVisualObjectInstancesOptions = powerbi.EnumerateVisualObjectInstancesOptions;
import VisualObjectInstance = powerbi.VisualObjectInstance;
import DataView = powerbi.DataView;
import VisualObjectInstanceEnumerationObject = powerbi.VisualObjectInstanceEnumerationObject;
import { VisualSettings } from "./settings";
/** Added to support legend addition */
import IVisualHost = powerbi.extensibility.visual.IVisualHost;
import { legendInterfaces, legend } from 'powerbi-visuals-utils-chartutils';
import createLegend = legend.createLegend;
import ILegend = legendInterfaces.ILegend;
import LegendDataPoint = legendInterfaces.LegendDataPoint;
import LegendPosition = legendInterfaces.LegendPosition;
import MarkerShape = legendInterfaces.MarkerShape;
export class Visual implements IVisual {
private target: HTMLElement;
private settings: VisualSettings;
private legend: ILegend;
private host: IVisualHost;
constructor(options: VisualConstructorOptions) {
console.log('Visual constructor', options);
this.target = options.element;
/** Assign visual host */
this.host = options.host;
/** Reserve element for legend */
this.legend = createLegend(
this.target,
false,
null,
true, /* scrollable */
LegendPosition.Top
);
}
public update(options: VisualUpdateOptions) {
this.settings = Visual.parseSettings(options && options.dataViews && options.dataViews[0]);
console.log('Visual update', options);
/** THIS ASSUMES THAT YOUR DATA VIEW WORKS, SO THERE'S NO ERROR HANDLING. YOU WILL AT LEAST
* NEED A VALUE IN THE CATEGORY DATA ROLE TO CONTINUE */
let categoryRole = options.dataViews[0].categorical.categories[0];
/** We're now going to create an array of LegendDataPoint objects to feed into our drawLegend
* method below.
* We're arbitrarily assigning a colour from the host's palette (theme) as it's required. You
* might want to sub this out with your own from your view model */
let myLegendDataPoints: LegendDataPoint[] = categoryRole.values.map(
(c, i) => (
{
label: <string>c,
color: this.host.colorPalette.getColor(<string>c).value,
identity: this.host.createSelectionIdBuilder()
.withCategory(categoryRole, i)
.createSelectionId(),
selected: false,
markerShape: MarkerShape.circle
}
)
);
/** This actually draws the legend; note that although font settings aren't required, it will look
* 'wrong' if they're not supplied */
this.legend.drawLegend(
{
title: 'Test Legend',
fontSize: 10,
dataPoints: myLegendDataPoints
},
options.viewport
);
}
private static parseSettings(dataView: DataView): VisualSettings {
return VisualSettings.parse(dataView) as VisualSettings;
}
/**
* This function gets called for each of the objects defined in the capabilities files and allows you to select which of the
* objects and properties you want to expose to the users in the property pane.
*
*/
public enumerateObjectInstances(options: EnumerateVisualObjectInstancesOptions): VisualObjectInstance[] | VisualObjectInstanceEnumerationObject {
return VisualSettings.enumerateObjectInstances(this.settings || VisualSettings.getDefault(), options);
}
}Hopefully this helps in some way.
Good luck,
Daniel