Forum Discussion

saviourofdp's avatar
saviourofdp
Advocate I
8 years ago
Solved

Font Size Property Unit Issue in Custom Visual

Hello 

 

I have an issue with creating a custom visual in which I need to be able to set 2 different font sizes from the property pane. The issue is that the first font size control is a slider which allows values from 8-40 whereas subsequent controls allow 0-100 percent.

 

I can show this using the basic new visualisation

 

 

pbiviz new fontVisual
npm install

Then edit capabilities.json to include a second font size property (in this case "fontSize1")

 

},
                "fontSize": {
                    "displayName": "Text Size",
                    "type": {
                        "formatting": {
                            "fontSize": true
                        }
                    }
                },
                "fontSize1": {
                    "displayName": "Text Size 1",
                    "type": {
                        "formatting": {
                            "fontSize": true
                        }
                    }
                }

 

 Update settings.ts to include the new property

 

     // Text Size
      public fontSize: number = 12;
      public fontSize1: number = 12;

Then build and run

 

pbiviz start

This is the result

 

2 different font size controls 

Is this the expected behaviour? Or is the second font size meant to be a percentage of the first? If not, Is there any way I can make them the same control?

 

Thanks 

  • Hello saviourofdp

     

    The fontSize is predefined Power BI capabilities name.

    You might try to rename the second fontSize property to textSize, titleFontSize, secFontSize, secTitleFontSize.

     

    Ignat Vilesov,

    Software Engineer

     

    Microsoft Power BI Custom Visuals

    [email protected]

12 Replies

  • v-viig's avatar
    v-viig
    Community Champion

    Hello saviourofdp

     

    The fontSize is predefined Power BI capabilities name.

    You might try to rename the second fontSize property to textSize, titleFontSize, secFontSize, secTitleFontSize.

     

    Ignat Vilesov,

    Software Engineer

     

    Microsoft Power BI Custom Visuals

    [email protected]

    • saviourofdp's avatar
      saviourofdp
      Advocate I

       

       

       

      ok, thanks Ignat that works. To conclude, I have to use one of

       

      • fontSize
      • textSize
      • titleFontSize
      • secFontSize
      • secTitleFontSize

      to get the absolute value slider. Otherwise, I get a percentage slider.

       

      This then begs further questions:

       

      1. Are there any other predefined capabilities names? For example there might be one to get at the Font Family enumeration. Documentation for these would be great and very useful.
      2. Why not have a configuration property that allows us to specify which control to use without relying on undocumented hardcoded names?
      3. Why does creating a capabilities property without using a name from the aforementioned list create a percentage slider? Of what value is the percentage meant to be?

      Thanks for the help

       

       

      • v-viig's avatar
        v-viig
        Community Champion

        Regarding your questions:

        1. Power BI API doesn't suppor this. This is in our backlog
        2. This is related to historical reasons
        3. Percentage slicer is a fallback for all of slider if the predefined name isn't used

         

        Ignat Vilesov,

        Software Engineer

         

        Microsoft Power BI Custom Visuals

        [email protected]

      • v-viig's avatar
        v-viig
        Community Champion

        Can you share a whole capabilities.json file to figure out a root of this issue?

         

        Ignat Vilesov,

        Software Engineer

         

        Microsoft Power BI Custom Visuals

        [email protected]

    • tcd1nc's avatar
      tcd1nc
      Frequent Visitor

      Is there an up to date list of the reserved names?

      • v-viig's avatar
        v-viig
        Community Champion

        This is a full list of properties that I know about:

        • weight

        • angle

        • textSize

        • fontSize

        • titleFontSize

        • secFontSize

        • secTitleFontSize

        • outlineWeight

        • gridVerticalWeight

        • gridHorizontalWeight

        • barWeight

        • rowPadding

        • cardPadding

        • imageHeight

        • steppedLayoutIndentation

        • borderThickness

        • preferredCategoryWidth

        • strokeWidth

        • gridlineThickness

        • markerSize

         

        Ignat Vilesov,

        Software Engineer

         

        Microsoft Power BI Custom Visuals

        [email protected]

  • tcd1nc's avatar
    tcd1nc
    Frequent Visitor

    Why does the font size property in the properry pane get rendered as a numeric(integer %) up/down control under its label and not a slider on the same line as the label, when not using the settings.ts file? 

    • v-viig's avatar
      v-viig
      Community Champion

      Not sure. Please share your code sample.

       

      Ignat Vilesov,

      Software Engineer

       

      Microsoft Power BI Custom Visuals

      [email protected]

      • tcd1nc's avatar
        tcd1nc
        Frequent Visitor
        {
            "dataRoles": [
                {
                    "displayName": "Values",
                    "name": "values",
                    "kind": "Grouping"
                }
            ],
            "dataViewMappings": [
                {
                    "conditions": [
                        {
                            "values": {
                                "max": 1
                            }
                        }
                    ],
                    "categorical": {
                        "categories": {
                            "for": {
                                "in": "values"
                            },
                            "dataReductionAlgorithm": {
                                "top": {
                                    "count": 100000
                                }
                            }
                        }
                    }
                }
            ],
            "objects": {
                "defaults": {
                    "displayName": "Defaults",
                    "properties": {                
                        "defaultId": {
                            "type": {
                                "integer": true
                            },
                            "displayName": "Default Item Index",
                            "description": "Select default item index"
                        }
                    }
                },
                "labels": {
                    "displayName": "Labels",
                    "properties": {
                        "vertical": {
                            "displayName": "Vertical",
                            "description": "If true, the radio buttons will be displayed vertically.",
                            "type": {
                                "bool": true
                            }
                        },   
                        "backgroundColor": {
                            "type": {
                                "fill": {
                                    "solid": {
                                        "color": true
                                    }
                                }
                            },
                            "displayName": "Background Color",
                            "description": "Select background color"
                        },
                        "fontSize": {
                            "type": {
                                "formatting": {
                                    "fontSize": true
                                }
                            },
                            "displayName": "Font Size",
                            "description": "Select font size"
                        },
                        "fontColor": {
                            "type": {
                                "fill": {
                                    "solid": {
                                        "color": true
                                    }
                                }
                            },
                            "displayName": "Font Color",
                            "description": "Select font color"
                        },
                        "fontFamily": {
                            "displayName": "Font family",
                            "description": "Select font family",
                            "type": {
                                "enumeration": [
                                    {
                                        "displayName": "Default",
                                        "description": "helvetica, arial, sans-serif",
                                        "value": "helvetica, arial, sans-serif"
                                    },
                                    {
                                        "displayName": "Arial",
                                        "value": "Arial"
                                    },
                                    {
                                        "displayName": "Arial Black",
                                        "value": "\"Arial Black\""
                                    },
                                    {
                                        "displayName": "Arial Unicode MS",
                                        "value": "\"Arial Unicode MS\""
                                    },
                                    {
                                        "displayName": "Calibri",
                                        "value": "Calibri"
                                    },
                                    {
                                        "displayName": "Cambria",
                                        "value": "Cambria"
                                    },
                                    {
                                        "displayName": "Cambria Math",
                                        "value": "\"Cambria Math\""
                                    },
                                    {
                                        "displayName": "Candara",
                                        "value": "Candara"
                                    },
                                    {
                                        "displayName": "Comic Sans MS",
                                        "value": "\"Comic Sans MS\""
                                    },
                                    {
                                        "displayName": "Consolas",
                                        "value": "Consolas"
                                    },
                                    {
                                        "displayName": "Constantia",
                                        "value": "Constantia"
                                    },
                                    {
                                        "displayName": "Corbel",
                                        "value": "Corbel"
                                    },
                                    {
                                        "displayName": "Courier New",
                                        "value": "\"Courier New\""
                                    },
                                    {
                                        "displayName": "Georgia",
                                        "value": "Georgia"
                                    },
                                    {
                                        "displayName": "Lucida Sans Unicode",
                                        "value": "\"Lucida Sans Unicode\""
                                    },
                                    {
                                        "displayName": "Segoe (Bold)",
                                        "value": "\"Segoe UI Bold\", wf_segoe-ui_bold, helvetica, arial, sans-serif"
                                    },
                                    {
                                        "displayName": "Segoe UI",
                                        "value": "\"Segoe UI\", wf_segoe-ui_normal, helvetica, arial, sans-serif"
                                    },
                                    {
                                        "displayName": "Segoe UI Light",
                                        "value": "\"Segoe UI Light\", wf_segoe-ui_bold, helvetica, arial, sans-serif"
                                    },
                                    {
                                        "displayName": "Symbol",
                                        "value": "Symbol"
                                    },
                                    {
                                        "displayName": "Tahoma",
                                        "value": "Tahoma"
                                    },
                                    {
                                        "displayName": "Times New Roman",
                                        "value": "\"Times New Roman\""
                                    },
                                    {
                                        "displayName": "Trebuchet MS",
                                        "value": "\"Trebuchet MS\""
                                    },
                                    {
                                        "displayName": "Verdana",
                                        "value": "Verdana"
                                    },
                                    {
                                        "displayName": "Wingdings",
                                        "value": "Wingdings"
                                    }
                                ]
                            }
                        },
                        "btnSize": {
                            "type": {
                                "integer": true                        
                            },
                            "displayName": "Button Size",
                            "description": "Select button size"
                        }
                    }
                }
            }
        }

        visual.ts:

        /*
         *  Power BI Visual CLI
         *
         *  Copyright (c) Microsoft Corporation
         *  All rights reserved.
         *  MIT License
         *
         *  Permission is hereby granted, free of charge, to any person obtaining a copy
         *  of this software and associated documentation files (the ""Software""), to deal
         *  in the Software without restriction, including without limitation the rights
         *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
         *  copies of the Software, and to permit persons to whom the Software is
         *  furnished to do so, subject to the following conditions:
         *
         *  The above copyright notice and this permission notice shall be included in
         *  all copies or substantial portions of the Software.
         *
         *  THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
         *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
         *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
         *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
         *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
         *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
         *  THE SOFTWARE.
         */
        
        module powerbi.extensibility.visual {
            "use strict";
            interface FlexiSlicerViewModel {
                vertical: boolean;
                defaultId: number;
                numberCats: number;
                fontSize: number;
                fontFamily: string;
                fontColor: string;
                backgroundColor: string;
                radioBtnSize: number;
            };
        
            function visualTransform(options: VisualUpdateOptions, host: IVisualHost): FlexiSlicerViewModel {
                let dataViews = options.dataViews;
                let viewModel: FlexiSlicerViewModel = {
                   vertical: true,
                   defaultId: 0,
                   numberCats: 0,
                   fontSize: 12,
                   fontFamily: "Arial",
                   fontColor: "blue",
                   backgroundColor: "white",
                   radioBtnSize: 12
                };
               
                if (!dataViews
                    || !dataViews[0]
                    || !dataViews[0].categorical
                    || !dataViews[0].categorical.categories
                    || !dataViews[0].categorical.categories[0]
                )
                    return viewModel;        
                
                let category = options.dataViews[0].categorical.categories[0];
                var numCats = category.values.length;
        
                let dvobjs = dataViews[0].metadata.objects;
                try{
                    let style: FlexiSlicerViewModel = {
                            vertical: getValue<boolean>(dvobjs, 'labels', 'vertical', true),
                            defaultId: getValue<number>(dvobjs, 'defaults', 'defaultId', 0),
                            numberCats: numCats,
                            fontSize: getValue<number>(dvobjs, 'labels', 'fontSize', 12),
                            fontFamily: getValue<string>(dvobjs, 'labels', 'fontFamily', "Arial"),
                            fontColor: getFill(dataViews[0], 'labels', 'fontColor', "blue"),
                            backgroundColor: getFill(dataViews[0], 'labels', 'backgroundColor', "white"),
                            radioBtnSize: getValue<number>(dvobjs, 'labels', 'btnSize', 12)
                        };
                        
                    return style;
                }
                catch(e){
                    return viewModel;
                }
            }
        
        
            export class Visual implements IVisual {
                private target: HTMLElement;
                private flexiSlicerViewModel: FlexiSlicerViewModel;
                //private numberCats: number;
                private selectionManager: ISelectionManager;
                private selectionIds: any = {};
                private host: IVisualHost;
                private isEventUpdate: boolean = false;
                private lastSelectedValue: any;
        
                constructor(options: VisualConstructorOptions) {
                    this.target = options.element;
                    this.host = options.host;
                    this.selectionManager = options.host.createSelectionManager();
                }
        
                public update(options: VisualUpdateOptions) {
                    this.flexiSlicerViewModel = visualTransform(options, this.host);
                    if (this.flexiSlicerViewModel && !this.isEventUpdate){             
                        this.init(options);
        
                        //update if default item is set
                        if(this.flexiSlicerViewModel.defaultId > 0){
                            this.selectionManager.clear(); // Clean up previous filter before applying another one.                    
                            // Find the selectionId and select it
                            this.selectionManager.select(this.lastSelectedValue).then((ids: ISelectionId[]) => {  });                
                            // This call applys the previously selected selectionId                    
                            this.selectionManager.applySelectionFilter();
                        }
                    }
                }
        
                public init(options: VisualUpdateOptions) {
                    // Return if we don't have a category
                    if (!options ||
                        !options.dataViews ||
                        !options.dataViews[0] ||
                        !options.dataViews[0].categorical ||
                        !options.dataViews[0].categorical.categories ||
                        !options.dataViews[0].categorical.categories[0]) {
                        return;
                    }
                    let viewmodel = this.flexiSlicerViewModel;
        
                    // remove any children from previous renders
                    while (this.target.firstChild) {
                        this.target.removeChild(this.target.firstChild);
                    }
        
                    // clear out any previous selection ids
                    this.selectionIds = {};
        
                    // get the category data.
                    let category = options.dataViews[0].categorical.categories[0];
                    let values = category.values;
        
                    // build selection ids to be used by filtering capabilities later
                    var itemctr: number = 0;
                    let scroller = document.createElement("div");
                    scroller.className="container";
                    scroller.style.width = options.viewport.width.toString() +"px";
                    scroller.style.height = options.viewport.height.toString() +"px";
                    scroller.style.backgroundColor = viewmodel.backgroundColor;
        
                    values.forEach((item: number, index: number) => {
                        itemctr++;
                        // create an in-memory version of the selection id so it can be used in onclick event.
                        this.selectionIds[item] = this.host.createSelectionIdBuilder()
                            .withCategory(category, index)
                            .createSelectionId();               
        
                        let value = item.toString();
                        let radio = document.createElement("input");
                        radio.type = "radio";
                        radio.value = value;
                        radio.name = "values";
                        radio.id = itemctr.toString();
        
                        radio.style.height= viewmodel.radioBtnSize.toString()+"px";// viewmodel.fontSize.toString() + "px";
                        radio.style.width= viewmodel.radioBtnSize.toString()+"px";//viewmodel.fontSize.toString() + "px"; 
                        //set default checked item
                        if(itemctr == viewmodel.defaultId ){
                            radio.checked = true;
                            this.lastSelectedValue = this.selectionIds[value];
                        }
        
                        radio.onclick = function (ev) {
                            this.isEventUpdate = true;
                             // This is checked in the update method. If true it won't re-render, this prevents an infinite loop                   
                            this.selectionManager.clear(); // Clean up previous filter before applying another one.                    
                            // select saved selectionid
                            this.selectionManager.select(this.selectionIds[value]).then((ids: ISelectionId[]) => {  });                
                            // This call applys the previously saved selectionId                    
                            this.selectionManager.applySelectionFilter();
        
                        }.bind(this);
                    
                        let label = document.createElement("label");
                        label.innerHTML += value;
                        label.style.fontFamily= viewmodel.fontFamily;
                        label.style.color = viewmodel.fontColor;
                        label.style.fontSize = viewmodel.fontSize.toString() + "px";
                        label.htmlFor=itemctr.toString();
            
                        scroller.appendChild(radio);
                        scroller.appendChild(label);
        
                        if (viewmodel.vertical == true && itemctr < values.length ) {
                            scroller.appendChild(document.createElement("br"));
                        }
        
                    });
                    this.target.appendChild(scroller);
                }
        
        
                /**
                 * Enumerates through the objects defined in the capabilities and adds the properties to the format pane
                 *
                 * @function
                 * @param {EnumerateVisualObjectInstancesOptions} options - Map of defined objects
                 */
                //@logExceptions()
                public enumerateObjectInstances(options: EnumerateVisualObjectInstancesOptions): VisualObjectInstanceEnumeration {
                    let objectName = options.objectName;
                    let objectEnumeration: VisualObjectInstance[] = [];
                    let viewModel = this.flexiSlicerViewModel;
                    switch (objectName) {
                        case 'defaults':
                            var id: number = 0;
                            if(viewModel.defaultId > viewModel.numberCats) id= 0; else id= viewModel.defaultId;
        
                            objectEnumeration.push({
                                objectName: objectName,
                                properties: {                           
                                    defaultId: id
                                },
                                validValues: {                            
                                    defaultId: {
                                        numberRange: {
                                            min: 0,
                                            max: viewModel.numberCats
                                        }
                                    }
                                },
                                selector: null
                            });
                            break;
                        case 'labels':
                            objectEnumeration.push({
                                objectName: objectName,
                                properties: {
                                    vertical: viewModel.vertical,
                                    backgroundColor: viewModel.backgroundColor,
                                    fontSize: viewModel.fontSize,
                                    fontFamily: viewModel.fontFamily,
                                    fontColor: viewModel.fontColor,
                                    btnSize: viewModel.radioBtnSize
                                },
                                validValues: {                            
                                    btnSize: {
                                        numberRange: {
                                            min: 1,
                                            max: 40
                                        }
                                    }
                                },
                                selector: null
                            });
                            break;                
                    };
                    this.isEventUpdate = false;
                    
                    return objectEnumeration;
                }
            }
        }