MscrmTools / PCF-Controls

Controls using PowerApps Components Framework
GNU General Public License v3.0
83 stars 42 forks source link

[Question] Dynamically adjust Action button width to fit size of section in Model-driven app #168

Closed Ruben-e-d closed 4 months ago

Ruben-e-d commented 6 months ago

Hi,

I am new to PCF and Fluent-UI but I came across your Action button and started playing with it. I love the capabilities PCF has and how you've made the Action button, but I was wondering if there is a way to automatically adjust the width of the button based on the size of the section it is located in.

Unfortunately, I can't find anything about this anywhere else, so I figured I'll just contact the developer of the button. I have tried values as:

As per CSS documentation. But that probably only looks at the content of the button itself not at the section where the button is located in this case: image

Desired result: image

Desired result when section size changes: image

Schermafbeelding 2024-05-30 134716

The size of sections in Model-driven apps also dynamically adjust to the size of the screen, the zoom of the browser, aspect ratio and such. I was wondering if this is also possible with PCF in combination with React/Fluent UI.

I'd love to hear your thoughts on this. Thanks in advance.

MscrmTools commented 6 months ago

unfortunately, PCF are not aware of the context/UI they are added in. And playing with DOM is not supported so I'm not sure what you want is achievable

Ruben-e-d commented 5 months ago

Hi,

Thank you for your quick response. Since what I asked for is not possible, I was wondering if it is possible to manipulate PCF controls, like width in the example above, with form scripts? If that is the case I can achieve this by putting a JScript function on the on load of a form and change it that way. It will be the right size once the form loads for every user.

Thank you.

MscrmTools commented 5 months ago

I guess it should be possible but not supported. Browsing the DOM of the form should let you find some selectors to find the button and resize it dynamically

Ruben-e-d commented 5 months ago

In that case I'll look into that. Thanks for the answers. Have a good one.

Ruben-e-d commented 5 months ago

Sorry to bother you again, but I have been playing around with the button today and may have found something. While looking around I have found a function in PCF called context.mode.trackContainerResize(Boolean) that allows the dimensions of the container to be read through context.mode.allocatedWidth and context.mode.allocatedHeight and also kicks off the UpdateView if any of these dimensions change.

I have added the this into the configuration of the button but for some reason the width does not get updated even though it puts a pixel value into the width of the props.

Do you have any idea what I'm doing wrong? I have added the following parts to the code:

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:

  1. trackContainerResizeValue = true
    image
  2. width: 600px, sometimes it reads -1px but after a second run of renderControl(context) it does return a valid pixel value. image
  3. Width of button does not change: image

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

Ruben-e-d commented 4 months ago

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:

import { TooltipHost, ITooltipHostStyles } from '@fluentui/react/lib/Tooltip';

 tooltipStyles: ITooltipHostStyles = {
       root: {
           width: this.props.style?.width
       }
   }
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>
        );
    }
}