Skip to main content
cancel
Showing results for 
Search instead for 
Did you mean: 

Register now to learn Fabric in free live sessions led by the best Microsoft experts. From Apr 16 to May 9, in English and Spanish.

Reply
saviourofdp
Frequent Visitor

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 controls2 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 

1 ACCEPTED SOLUTION
v-viig
Community Champion
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

pbicvsupport@microsoft.com

View solution in original post

12 REPLIES 12
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
Community Champion
Community Champion

Not sure. Please share your code sample.

 

Ignat Vilesov,

Software Engineer

 

Microsoft Power BI Custom Visuals

pbicvsupport@microsoft.com

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;
        }
    }
}
v-viig
Community Champion
Community Champion

Can you share full source code as a zip file?

 

Ignat Vilesov,

Software Engineer

 

Microsoft Power BI Custom Visuals

pbicvsupport@microsoft.com

v-viig
Community Champion
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

pbicvsupport@microsoft.com

tcd1nc
Frequent Visitor

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

v-viig
Community Champion
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

pbicvsupport@microsoft.com

Do you have a list of actual values that work?

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

pbicvsupport@microsoft.com

 

 

 

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

 

 

using secFontSize results in errors:

 


 info   JSON change detected. Rebuilding...
 error  JSON  capabilities.json :  instance.objects.dataPoint.properties.d1FontSize2.type.formatting additionalProperty "secFontSize" exists in instance when not allowed
 error  JSON  capabilities.json :  instance.objects.dataPoint.properties.d1FontSize2.type.formatting is not exactly one from [subschema 0],[subschema 1],[subschema 2]
 error  JSON  capabilities.json :  instance.objects.dataPoint.properties.fontSize.type.formatting additionalProperty "textSize" exists in instance when not allowed
 error  JSON  capabilities.json :  instance.objects.dataPoint.properties.fontSize.type.formatting is not exactly one from [subschema 0],[subschema 1],[subschema 2]

 

 

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

pbicvsupport@microsoft.com

Helpful resources

Announcements
Microsoft Fabric Learn Together

Microsoft Fabric Learn Together

Covering the world! 9:00-10:30 AM Sydney, 4:00-5:30 PM CET (Paris/Berlin), 7:00-8:30 PM Mexico City

PBI_APRIL_CAROUSEL1

Power BI Monthly Update - April 2024

Check out the April 2024 Power BI update to learn about new features.

April Fabric Community Update

Fabric Community Update - April 2024

Find out what's new and trending in the Fabric Community.

Top Kudoed Authors