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:
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
}
}
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:
- trackContainerResizeValue = true

- 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).

- Width of button does not change:

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.