Dataset PCF using FluentUI: Columns

This is a part of my blog “My Checklist for a Dataset PCF using FluentUI“. This time it’s about how to define the columns using FluentUI DetailsList.

Columns definition

The definition of the columns is easy using FluentUI DetailsList: just set the columns property of the Component.

<DetailsList                                  
    ...
    columns={columns}    
/>

The columns definition is basically a map of the dataset.columns to objects containing some properties like:

  • key: a unique key defining the column (column.name)
  • name: the text displayed for the column (column.displayName)
  • fieldName : this is needed to identify the data for the column, and is needed only if you don’t have an own renderer (column.name)
  • isSorted, isSortingDescending : boolean which tells if and how the column is sorted at the moment. I’ve set this using the react hook as described in the blog about sorting, but basically you just need to keep track of sorting in a internal state.
  • minWidth: Minimum width of the column in px.
  • isResizable: will allow the user to resize the column width (true)
  • onRender: custom renderer for the cell content. My renderer looks like this (if it’s not an optionset, I let the default renderer using undefined:
  onRender: isOptionSetRenderer===true  
               ? (item : any) => <ColorfulCell ...></ColorfulCell>
               : undefined

Column width

The column width definition is a little tricky. I might have implemented it too complicated, not sure if there is a much simpler way. In case you have a better way, please let me know.

The base of the column width (in pixel) is provided by the PCF DataSet :

column.vizualSizeFactor

This will give you the value which was customized in the view. But using it to set “minWidth” on visualSizeFactor, I saw that a lot of place is left on the right side of the grid.

I’ve got this setting the DetailsList property layoutMode on fixedColumns

layoutMode={DetailsListLayoutMode.fixedColumns}

Setting the layoutMode on justified, I’ve got another look, but not what I was expecting :

layoutMode={DetailsListLayoutMode.justified}

But the standard Grid doesn’t do that. In case there is place left, it distributes that place proportional to all columns, in order to get a 100% width.

In order to archive that, I’ve worked with layoutMode “justified”, and I’ve set for each column the maxWidth. That way the DetailsList tries to resize the columns according to the min/maxWidth.

But in order to calculate that, I had to sum first all the columns.visualSizeFactor, and compare with the width of the PCF component. The width of the pcf can be retrieved by setting trackContainerResize in the init method of the pcf

//set this in init method of the pcf
context.mode.trackContainerResize(true);

//then you can read the width of the pcf component
context.mode.allocatedWidth

So in my width calculation, I calculate a buffer to add to each column

const widthBuffer = (availableWidth > aggreatedWidth) 
      ? ((availableWidth - aggreatedWidth)/ aggregates.count) 
      : 0;

//so I have my minWidth, maxWidth
minWidth = column.visualSizeFactor,
maxWidth = column.visualSizeFactor + widthBuffer

Now my grid is distributed similar with the standard Dynamics 365 Grid.

This calculations are similar with the example I’ve found later in the pcf sdk examples, but there it’s calculated a proportional width considering the sum and the column.visualSizeFactor: https://docs.microsoft.com/en-us/powerapps/developer/component-framework/sample-controls/table-grid-control. Have a look to the getColumnWidthDistribution function. Maybe a little bit simpler than my approach.

Pitfalls of dataset columns

Columns order

The order of dataset.columns is not the order the columns were customized. To get the right order, you have to sort the columns, based on column.sort:

dataset.columns.sort((c1, c2) => c1.order - c2.order )

Columns added by property-set

A very useful feature of the PCFs of type dataset, is the possibility to define columns using property-set. This will add the columns to your dataset, even if the view didn’t defined it. But if you do that, you need to consider that this columns doesn’t have the order or the visualSizeFactor set.

//for property-set columns, which are not included in the view
column.visualSizeFactor === -1
column.order === -1

So when sorting the columns, you need to consider where they should be placed.

You also have to take care to define a minWidth for this columns, since using -1 will let this columns hidden.

Some other column properties

There are some more DataSet column properties, which might be interesting:

  • column.isHidden -> maybe this ones shouldn’t be shown, or considered for the width calculation
  • column.disabledSorting -> you can set for each column in DetailsList if the sorting is disabled

Putting it all together

Considering all the problems above, I’ve decided to parse the columns first, keeping parsed objects for columns. I’ve kept the column in a subproperty called “original”, and calculated some features (width, isVisible, order).

function parseColumns(originalColumns: ComponentFramework.PropertyHelper.DataSetApi.Column[]): IColumnsHookState{        
    const calculatedColumns = originalColumns.map((column) => {
       return {
           original : column,
           features: {
               width : column.visualSizeFactor===-1 
                       ? 75 
                       : column.visualSizeFactor,  
               isVisible : !column.isHidden===true, 
               order: column.order===-1 ? 100 : column.order
           },
           minWidth:0,
           maxWidth:0
       }
    }).sort((c1, c2) => c1.features.order - c2.features.order );
    return {
        calculatedColumns, 
        aggregates : calculateAggregatesBasedOnFeatures(calculatedColumns)
    }
}

function calculateAggregatesBasedOnFeatures(cols : IGridColumn[]) : IGridColumnAggregates{
    return cols.reduce(({sum, count}, column) => {
        return { 
        sum : sum + (column.features.isVisible===true 
                      ? column.features.width 
                     : 0) + 14,
        count : count + (column.features.isVisible===true ? 1 : 0)
        };
    }, {sum:0, count:0});   
}

Now comes the function for the calculating the maxWidth

function recalculateWidth(calculatedColumns: IGridColumn[], aggregates: IGridColumnAggregates, availableWidth?: number): IGridColumn[]{
    const aggreatedWidth =  aggregates.sum + 50;           
    const widthBuffer = (availableWidth != null && availableWidth > aggreatedWidth) 
            ? ((availableWidth - aggreatedWidth)/ aggregates.count) 
            : 0;

    return calculatedColumns.map((intermediateColumn) => ({
        original : intermediateColumn.original,
        features : intermediateColumn.features,
        minWidth: intermediateColumn.features.width,
        maxWidth : intermediateColumn.features.width + widthBuffer
    }));
}
<p value="<amp-fit-text layout="fixed-height" min-font-size="6" max-font-size="72" height="80">And that's pretty much it. For more details, you can check out the <a rel="noreferrer noopener" href="https://github.com/brasov2de/ColorfulOptionsetGrid/blob/master/ColorfulOptionsetGrid/App/Hooks/useColumns.ts&quot; target="_blank">github repository</a>.And that’s pretty much it. For more details, you can check out the github repository.

One thought on “Dataset PCF using FluentUI: Columns

Add yours

Leave a comment

Website Powered by WordPress.com.

Up ↑