Skip to main content

Notifications

Community site session details

Community site session details

Session Id :
Power Apps - Power Apps Pro Dev & ISV
Unanswered

PCF Fluent UI Action button context.mode.trackContainerResize not updating width.

(0) ShareShare
ReportReport
Posted on by 33

Hi,

 

I am new to PCF, React and Fluent UI, but I started playing with some components form the PCF Gallery, mainly the Action button made by MscrmTools.

My goal was to make the button width responsive to the available space in the container the button is located in.

I have found the following function which is supposed to make this possible: 

This function allows the button to read the dimensions of the container through context.mode.allocatedWidth and context.mode.allocatedHeight and also kicks off the UpdateView if any of the container dimensions change.

 

This function does indeed kick of the UpdateView but for some reason the width of the button does not change. For example when I change the zoom of the page in a model-driven app it does put the correct pixel value into the width prop of the component but does not update the button accordingly.

 

I have added the following parts to the code of MscrmTools:

  • index.ts

 

 

import { IInputs, IOutputs } from "./generated/ManifestTypes";
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import ButtonControl, { IButtonControlProps } from "./QssFancyButton";
import { initializeIcons } from '@fluentui/font-icons-mdl2';


export class QssFancyButton implements ComponentFramework.StandardControl<IInputs, IOutputs> {

	private container: HTMLDivElement;
	private notifyOutputChanged: () => void;
	private currentValue: string | null;
	private buttonLabel: string;
	private id: string;
	private sendId: boolean;
	private controlType: string;
	/**
	 * Empty constructor.
	 */
	constructor() 
	{
		initializeIcons();
	}

	/**
	 * Used to initialize the control instance. Controls can kick off remote server calls and other initialization actions here.
	 * Data-set values are not initialized here, use updateView.
	 * @Param context The entire property bag available to control via Context Object; It contains values as set up by the customizer mapped to property names defined in the manifest, as well as utility functions.
	 * @Param notifyOutputChanged A callback method to alert the framework that the control has new outputs ready to be retrieved asynchronously.
	 * @Param state A piece of data that persists in one session for a single user. Can be set at any point in a controls life cycle by calling 'setControlState' in the Mode interface.
	 * @Param container If a control is marked control-type='standard', it will receive an empty div element within which it can render its content.
	 */
	public init(context: ComponentFramework.Context<IInputs>, notifyOutputChanged: () => void, state: ComponentFramework.Dictionary, container: HTMLDivElement) 
 {
		this.container = container;
		this.notifyOutputChanged = notifyOutputChanged;

		this.buttonLabel = context.parameters.buttonLabel.raw ?? "";
		this.id = context.parameters.buttonId.raw ?? "";
		this.sendId = context.parameters.sendId.raw === "1";
		context.mode.trackContainerResize(context.parameters.trackContainerResizeValue.raw === "1" ? true : false);

		// @TS-ignore
		this.controlType = context.parameters.buttonAttribute.attributes.Type;

		if (this.buttonLabel.trim().startsWith("{")) 
 {
			let json = JSON.parse(this.buttonLabel);
			try 
 {
				this.buttonLabel = json[context.userSettings.languageId];
				if (this.buttonLabel === undefined) 
 {
					this.buttonLabel = json[parseInt(Object.keys(json)[0])];
				}
			}
			catch 
 {
				this.buttonLabel = json[parseInt(Object.keys(json)[0])];
			}
		}

		this.renderControl(context);
	}

	private renderControl(context: ComponentFramework.Context<IInputs>): void 
 {

		let props: IButtonControlProps = 
 {
			text: this.buttonLabel,
			disabled: context.parameters.enableButtonOnDisabledForm.raw === "1" ? false : context.mode.isControlDisabled,
			style: 
 {
				backgroundColor: context.parameters.backgroundColor.raw ?? "#0078d4",
				borderColor: context.parameters.borderColor.raw ?? "#0078d4",
				color: context.parameters.textColor.raw ?? "#FFFFFF",
				width: context.parameters.trackContainerResizeValue.raw === "1" ? `${context.mode.allocatedWidth}px` : context.parameters.trackContainerResizeValue.raw === "0" && context.parameters.width.raw !== null ? context.parameters.width.raw : "auto"
			},
			hoverBackgroundColor: context.parameters.hoverBackgroundColor.raw ?? "#106EBE",
			hoverBorderColor: context.parameters.hoverBorderColor.raw ?? "#106EBE",
			hoverColor: context.parameters.hoverTextColor.raw ?? "#FFFFFF",
			checkedBackgroundColor: context.parameters.pressedBackgroundColor.raw ?? "#0078d4",
			checkedBorderColor: context.parameters.pressedBorderColor.raw ?? "#0078d4",
			checkedColor: context.parameters.pressedTextColor.raw ?? "#FFFFFF",
			iconName: context.parameters.iconName.raw,
			toolTip: context.parameters.tooltipText.raw ?? "",
			onClick: () => { this.notifyOutputChanged(); }
		}

		ReactDOM.render(React.createElement(ButtonControl, props), this.container);
	}


	/**
	 * Called when any value in the property bag has changed. This includes field values, data-sets, global values such as container height and width, offline status, control metadata values such as label, visible, etc.
	 * @Param context The entire property bag available to control via Context Object; It contains values as set up by the customizer mapped to names defined in the manifest, as well as utility functions
	 */
	public updateView(context: ComponentFramework.Context<IInputs>): void 
	{
		this.renderControl(context);

		this.currentValue = context.parameters.buttonAttribute.raw ?? "";
	}

	/** 
	 * It is called by the framework prior to a control receiving new data. 
	 * @returns an object based on nomenclature defined in manifest, expecting object[s] for property marked as “bound” or “output”
	 */
	public getOutputs(): IOutputs 
	{
		if (this.controlType === "string") 
		{
			return {
				buttonAttribute: this.sendId ? this.id : this.buttonLabel
			};
		}

		return {
			buttonAttribute: new Date()
		};
	}

	/** 
	 * Called when the control is to be removed from the DOM tree. Controls should use this call for cleanup.
	 * i.e. cancelling any pending remote calls, removing listeners, etc.
	 */
	public destroy(): void {
		// Add code to cleanup control if necessary
	}
}

 

 

  • Inside the public init:

    • context.mode.trackContainerResize(context.parameters.trackContainerResizeValue.raw === "1" ? true : false);

  • Inside the private renderControl:

    • width: context.parameters.trackContainerResizeValue.raw === "1" ? `${context.mode.allocatedWidth}px` : context.parameters.trackContainerResizeValue.raw === "0" && context.parameters.width.raw !== null ? context.parameters.width.raw : "auto"

  • ControlManifest.Input.xml

    • <property name="trackContainerResizeValue" display-name-key="trackContainerResizeValue_Display_Key" description-key="trackContainerResizeValue_Desc_Key" of-type="Enum" default-value="0" usage="input" required="true"> <value name="true" display-name-key="yes_Resize_Display_Key" description-key="yes_Resize_Desc_Key">1</value> <value name="false" display-name-key="no_Resize_Display_Key" description-key="no_Resize_Desc_Key">0</value> </property>

I did not change anything in the .tsx as there was no need to, if I'm not mistaken.
The following happens when changing the width of the component container in npm start watch, or from a model-driven app:

  • Run through index.ts
  1. trackContainerResizeValue = true
     
    Rubened_4-1719311226714.png
     
  2. width: 600px, sometimes it reads -1px but after a second run of renderControl(context) it does return a valid pixel value (only in npm start watch).
     
    Rubened_5-1719311238091.png
  3. Width of button does not change:
     
    Rubened_6-1719311248820.png

If anybody is willing to help me out that would be great, if you need my files, let me know but they are basically the same as MscrmTools his code with the lines above added to it.

  • Verified answer
    Rubened Profile Picture
    33 on at
    Re: PCF Fluent UI Action button context.mode.trackContainerResize not updating width.

    A coworker of mine has found the solution.
    Ignoring context.mode.trackContainerResize and instead utilizing width 100% he managed to fix the issue.
    Apparently, in this component if you don't set the width of the tooltip with the width of the button this behavior happens.

    So my coworker fixed it with the following added:

    1. import { TooltipHostITooltipHostStyles from '@fluentui/react/lib/Tooltip';
    2.    tooltipStylesITooltipHostStyles = {
             root: {
                 width: this.props.style?.width
             }
         }
    3. return (
                 <Stack horizontal>
                     {this.props.text?.trim().length ? (
                         <TooltipHost content={this.props.toolTip} id={toolTipId} styles={this.tooltipStyles}>
                             <PrimaryButton
                                 iconProps={this.icon}
                                 styles={this.styles}
                                 text={this.props.text}
                                 disabled={this.props.disabled}
                                 onClick={this.props.onClick}
                                 aria-describedby={toolTipId}
                             />
                         </TooltipHost>
                     ) 

    The code now works as intended and looks as follows:

     

    import * as React from 'react'
    import { Stack } from '@fluentui/react/lib/Stack';
    import { IBaseButtonProps, IBaseButtonState, IButtonStyles, PrimaryButton, IconButton } from '@fluentui/react/lib/Button';
    import { IIconProps } from '@fluentui/react';
    import { TooltipHost, ITooltipHostStyles } from '@fluentui/react/lib/Tooltip';
    
    export interface IButtonControlProps extends IBaseButtonProps {
     hoverBackgroundColor: string,
     hoverBorderColor: string,
     hoverColor: string,
     checkedBackgroundColor: string,
     checkedBorderColor: string,
     checkedColor: string,
     iconName: string | null,
     toolTip: string | undefined,
    }
    
    export default class ButtonControl extends React.Component<IButtonControlProps, IBaseButtonState>{
     constructor(props: IButtonControlProps) {
     super(props);
     }
     styles: IButtonStyles = {
     root: {
     backgroundColor: this.props.style?.backgroundColor ?? "#0078d4",
     borderColor: this.props.style?.borderColor ?? "#0078d4",
     color: this.props.style?.color ?? "#FFFFFF",
     width: this.props.style?.width
     },
     rootHovered: {
     backgroundColor: this.props.hoverBackgroundColor ?? "#106EBE",
     borderColor: this.props.hoverBorderColor ?? "#106EBE",
     color: this.props.hoverColor ?? "#FFFFFF",
     width: this.props.style?.width
     },
     rootPressed: {
     backgroundColor: this.props.checkedBackgroundColor ?? "#0078d4",
     borderColor: this.props.checkedBorderColor ?? "#0078d4",
     color: this.props.checkedColor ?? "#FFFFFF",
     width: this.props.style?.width
     }
     }
     tooltipStyles: ITooltipHostStyles = {
     root: {
     width: this.props.style?.width
     }
     }
    
     icon: IIconProps = { iconName: this.props.iconName ?? "" };
    
     render() {
     const toolTipId = `tooltip_${this.props.iconName}`;
    
     return (
     <Stack horizontal>
     {this.props.text?.trim().length ? (
     <TooltipHost content={this.props.toolTip} id={toolTipId} styles={this.tooltipStyles}>
     <PrimaryButton
     iconProps={this.icon}
     styles={this.styles}
     text={this.props.text}
     disabled={this.props.disabled}
     onClick={this.props.onClick}
     aria-describedby={toolTipId}
     />
     </TooltipHost>
     ) : (
     <TooltipHost content={this.props.toolTip} id={toolTipId} styles={this.tooltipStyles}>
     <IconButton
     iconProps={this.icon}
     styles={this.styles}
     disabled={this.props.disabled}
     onClick={this.props.onClick}
     aria-describedby={toolTipId}
     />
     </TooltipHost>
     )}
     </Stack>
     );
     }
    }

     

     

  • Rubened Profile Picture
    33 on at
    Re: PCF Fluent UI Action button context.mode.trackContainerResize not updating width.

    Hi a33ik,

     

    Thank you for your response.

    Unfortunately, when I put 100% into the width variable it just looks at the PCF container itself which is as big as the label and icon with some margin, it does not look, natively, at the container in a Model-driven app it is located.

     

    I build in the possibility to put a width manually which you can see in the following screenshot:

    Rubened_0-1719494218389.png

    But it does not change the width of the button to fit the whole section as you can see in the left part of the screenshot.

    To translate the options for the control:

    • Volg Container Breedte = The option to set whether context.mode.trackContainerResize is true (Ja) or not (Nee).
      • If this option is true (Ja), it will ignore the value that the user put in Width (Breedte).
    • Breedte = Width of the button.

    Once I put in a value like 250% or 500px into the width property manually it does make the button bigger:

    Rubened_1-1719494588092.png

    But when context.mode.trackContainerResize dynamically, for example when the size of the page changes, puts in a pixel value through context.mode.allocatedWidth into the width property of the button it does not change. which is strange.

  • a33ik Profile Picture
    3,304 Most Valuable Professional on at
    Re: PCF Fluent UI Action button context.mode.trackContainerResize not updating width.

    Hello,

    Did you try to set the button width to "100%"?

Under review

Thank you for your reply! To ensure a great experience for everyone, your content is awaiting approval by our Community Managers. Please check back later.

Helpful resources

Quick Links

Announcing the Engage with the Community forum!

This forum is your space to connect, share, and grow!

🌸 Community Spring Festival 2025 Challenge Winners! 🌸

Congratulations to all our community participants!

Warren Belz – Community Spotlight

We are honored to recognize Warren Belz as our May 2025 Community…

Leaderboard > Power Apps - Power Apps Pro Dev & ISV

#1
WarrenBelz Profile Picture

WarrenBelz 109 Most Valuable Professional

#2
Michael E. Gernaey Profile Picture

Michael E. Gernaey 82 Super User 2025 Season 1

#3
MS.Ragavendar Profile Picture

MS.Ragavendar 72

Overall leaderboard