DEV Community

Guido Zambarda
Guido Zambarda

Posted on • Originally published at iamguidozam.blog on

Discover the ListView control from the PnP reusable React controls

Introduction

Proceeding with the appointments with the PnP React controls today I want to talk about the ListView control.

If you’re interested you can find the code of this sample here.


The ListView control is used to display a view for specific items.

Visual appearance

There are a lot of possibilities with this control, here I want to show some of those functionalities and I will start, as usual, with the visual appearance of the various features.

Starting with the minimal configuration, this is how the control would display:

In the next configuration the columns are manually defined and overrides the automatic generated fields:

If needed, the control has a compact display option which will reduce the space taken by the control:

A great feature is the built-in filtering option. This option will render a text field in the upper section of the component:

This text field will automatically filter the items. The contains filtering operation is performed automatically on every field of the items:

Using the filtering option, is also possible to specify a pre-defined filter for those situation when you want to use a filter specified via code:

An interesting feature is the ability to allow users to drag and drop files onto the component. Here you can see the initial state of the component:

Once a few files gets dropped, a custom function will be called and parse the files to populate the items to be shown in the control:

By default the headers of the component will be scrolled alongside the content, but a specific option enables the headers to be fixed at the top of the control:

Here you can see how the control appears when the content gets scrolled all the way down:

As usual, in those kind of controls, it’s possible to handle the selection of an item and this is also possible with this control:

This will also enable the call of a custom function to handle the selection, but I will cover that in the code section of this post.

As long as the single selection, it’s possible to enable the multi selection:

Another awesome feature is that it’s also possible to group the items by a custom column(s), the resulting UI will be as follows:

Aside from filtering and grouping is also possible define a custom sorting method (aside the default one). In this sample the initial UI rendering is the following:

Once clicked on an header, the custom sorting will be executed. The custom sorting of this example gives the priority to the items with a high priority (red exclamation point) set to true and then apply the default sorting for the selected column:

This works also when the sorting of the column is reversed:

As a final visual example, the ListView control offers the ability to define a custom rendering for each of the items displayed:

Now that you have a clear understanding of what’s possible, let’s deep dive into the code!

Show me the code

Prerequisites

To use the PnP React controls, first you need to install the package:

npm install @pnp/spfx-controls-react --save --save-exact
Enter fullscreen mode Exit fullscreen mode

After the installation of the package you can proceed with the following instructions to use the ListView component.


To use the control you first need to import it and that can be done as following:

import { ListView } from "@pnp/spfx-controls-react";
Enter fullscreen mode Exit fullscreen mode

in this sample, the full import has a few more items since it is used for different operations. The full import for this sample is the following:

import { GroupOrder, IGrouping, IViewField, ListView, SelectionMode } from "@pnp/spfx-controls-react";
Enter fullscreen mode Exit fullscreen mode

Now that you have a clear understanding about how to install and import the component, let’s cover how to use it in the different instances.

Minimal instance

This instance shows how it’s possible to have the control defined using only the items to show. The control will take care of creating the columns. This is simply achieved as follows:

<ListView items={items}/>
Enter fullscreen mode Exit fullscreen mode

View field instance

The next step is the ability to define which are the fields that will be shown in the control. The fields are defined with a custom function which returns an array of IViewField:

private _getViewFields(): IViewField[] {
    return [
      {
        name: 'title',
        displayName: strings.Fields.Title,
        maxWidth: 150,
        minWidth: 100,
        sorting: true
      },
      {
        name: 'createdBy',
        displayName: strings.Fields.CreatedBy,
        maxWidth: 100,
        minWidth: 100,
        sorting: true
      },
      {
        name: 'version',
        displayName: strings.Fields.Version,
        maxWidth: 100,
        minWidth: 80,
        sorting: true
      },
      {
        name: 'created',
        displayName: strings.Fields.CreatedDate,
        maxWidth: 100,
        minWidth: 100,
        sorting: true,
        render: (item: any) => {
          if (!item || !item.created) {
            return <span>{strings.Fields.NoDate}</span>;
          }

          return <span>{item.created}</span>;
        }
      }
    ];
  }
Enter fullscreen mode Exit fullscreen mode

The previous method will be used also in other instances and is bound to the ListView control using the viewFields property:

<ListView 
  items={items} 
  viewFields={this._getViewFields()}
/>
Enter fullscreen mode Exit fullscreen mode

Compact instance

The compact view can be used as simple as setting the compact property to true:

<ListView
  items={items}
  compact={true}
/>
Enter fullscreen mode Exit fullscreen mode

Show Filter instance

In this instance there are a couple of properties set in order to use the filter. The first property, which enables the control to show the text box, is the showFilter which accept a boolean value (true to use the filtering).

The second property, the filterPlaceHolder one, is used to specify a string to be shown in the text field.

<ListView
  items={items}
  showFilter={true}
  filterPlaceHolder={strings.FilterPlaceHolder}
  viewFields={this._getViewFields()}
/>
Enter fullscreen mode Exit fullscreen mode

Default Filter instance

This instance shows how to specify a default filtering using the defaultFilter property. Here you can just specify a text which will be used to filter the available items.

<ListView
  items={items}
  defaultFilter='joan'
  viewFields={this._getViewFields()}
  showFilter={true}
/>
Enter fullscreen mode Exit fullscreen mode

Drag and Drop Files instance

To enable the drag and drop feature, it’s enough to set a couple parameters:

  • dragDropFiles: which accepts a boolean value, setting it to true will enable the feature.
  • onDrop: accept the method that will be used to handle the drag and drop operation.
<ListView
  items={droppedItems}
  dragDropFiles={true}
  onDrop={this._getDropFiles}
/>
Enter fullscreen mode Exit fullscreen mode

As a sample, this is the function used when files are dropped:

private _getDropFiles = (files: any[]): void => {
  const droppedItems = [...this.state.droppedItems];

  for (let i = 0; i < files.length; i++) {
    droppedItems.push({ title: files[i].name });
  }

  this.setState({
    droppedItems: droppedItems
  });
}
Enter fullscreen mode Exit fullscreen mode

Sticky Header instance

Enabling the headers to be sticked at the top of the component is easy as setting the stickyHeader property to true:

<ListView
  items={this._getMoreItems()}
  stickyHeader={true}
/>
Enter fullscreen mode Exit fullscreen mode

Handle Selection instance

This instance is used to show how to handle the selection of an item.

The selection of items is enabled with two properties:

  • selection: accepts the method called when an item is selected.
  • selectionMode: define which type of selection is allowed which, in this case, is the single mode one.
<ListView
  items={items}
  selection={this._getSelection}
  selectionMode={SelectionMode.single}
/>
Enter fullscreen mode Exit fullscreen mode

Just for reference, the sample function just write to the console log the selected item. This will be used also for the multiple selection instance.

private _getSelection(items: any[]): void {
  console.log('Selected items:', items);
}
Enter fullscreen mode Exit fullscreen mode

Multiple Selection Mode instance

The only difference from the previous instance is the different value for the selectionMode property. Here the SelectionMode used is the multiple one.

<ListView
  items={items}
  selection={this._getSelection}
  selectionMode={SelectionMode.multiple}
/>
Enter fullscreen mode Exit fullscreen mode

Group By Fields instance

To enable the grouping you can use the groupByFields property.

<ListView
  items={items}
  groupByFields={groupByFields}
/>
Enter fullscreen mode Exit fullscreen mode

The property accepts an array which contains the columns used for the grouping and also the sorting order.

const groupByFields: IGrouping[] = [
  {
    name: "createdBy",
    order: GroupOrder.ascending
  }
];
Enter fullscreen mode Exit fullscreen mode

Custom Sorting instance

This instance is used to show how to use the custom sorting. To specify a custom sorting function simply a method to the sortItems property:

<ListView
  items={items}
  viewFields={this._getAdditionalViewFields()}
  sortItems={this._sortItems}
/>
Enter fullscreen mode Exit fullscreen mode

In brief, the function accepts the items to be sorted, the column name and the sorting direction. The code here sorts the items first by the highPriority field- and then by the column name parameter:

private _sortItems = (items: SampleItem[], columnName: string, descending: boolean): any[] => {
  return items.slice().sort((a, b) => {

    if (a.highPriority && !b.highPriority) {
      return -1;
    }

    if (!a.highPriority && b.highPriority) {
      return 1;
    }

    const aValue = (a as any)[columnName];
    const bValue = (b as any)[columnName];

    if (aValue < bValue) {
      return descending ? 1 : -1;
    }

    if (aValue > bValue) {
      return descending ? -1 : 1;
    }

    return 0;
  });
}
Enter fullscreen mode Exit fullscreen mode

Custom Render Row instance

At last, here is the instance that uses a custom template for each row. The template is set to the onRenderRow property. Here you can specify how the rows will be displayed.

<ListView
  items={items}
  viewFields={[]}
  onRenderRow={(props) => {
    return (
<div className={`${styles.tableRow} ${props.item.highPriority ? styles.highPriority : ''}`}>
  <div className={styles.smallTableCell}>{props.item.highPriority ? '!' : ''}</div>
  <div className={styles.tableCell}>{props.item.createdBy}</div>
  <div className={styles.tableCell}>{props.item.title}</div>
  <div className={styles.tableCell}>{props.item.version}</div>
</div>
    );
  }}
/>
Enter fullscreen mode Exit fullscreen mode

Conclusions

In my opinion, the ListView control is a practical and reliable tool for handling many common user interface needs. It handles a lot of routine tasks with its built-in features, which can save time and effort during development. At the same time, it offers plenty of customization options, so you can adjust its appearance and behavior to suit specific requirements. Whether you’re working on something simple or more complex, ListView is straightforward to use and flexible enough to fit a variety of use cases.

If you’re interested in knowing more you can check the official documentation here.

Hope this helps!

Top comments (0)