Office UI Fabric People Picker in Property Pane – SharePoint Framework (SPFx)

As we know Office UI Fabric which official front-end framework is for building a user interface that fits seamlessly into SharePoint modern experience. In this article, we going to see more detail about the adding custom controls like office UI fabric people picker and Persona in the property pane.

Creating a new SharePoint Framework web-part project

The first step we have to create a new SharePoint framework react web-part project, I have used SharePoint Framework version 1.8.2, if you have any question regarding set-up new SharePoint framework development environment then you can refer my one of the previous article where I explained simple steps to set up a new development environment and create the new project. While creating a project in the PowerShell you must select react framework.

Important files

In the new SharePoint framework web part solution contains many files, but in this article, we are going take look only files which are under src/webpart

Web part TS file (PropertyPaneSpFxWebPart.ts)

In this file, we create react element and created element assigned to the react DOM, also we are passing web part context as props so we can access these props in while create react component.

import * as React from 'react';
import * as ReactDom from 'react-dom';
import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base';
import {
  IPropertyPaneConfiguration
} from '@microsoft/sp-property-pane';
import * as strings from 'PropertyPaneSpFxWebPartStrings';
import PropertyPaneSpFx from './components/PropertyPaneSpFx';
import { PropertyPanetextboxcustom } from './components/PropertyPaneControl';

export interface IPropertyPaneSpFxWebPartProps {
  selectedusers: any;
  onchange(stringvalue:string):void;
}

export default class PropertyPaneSpFxWebPart extends BaseClientSideWebPart<IPropertyPaneSpFxWebPartProps> {

  public render(): void {
    if(this.properties.selectedusers == "[]")
    {
      this.properties.selectedusers=[];
    }
    const element: React.ReactElement<IPropertyPaneSpFxWebPartProps> = React.createElement(
      PropertyPaneSpFx,
      {
        selectedusers: this.properties.selectedusers,
        onchange:this.onChanged
      }
    );
    ReactDom.render(element, this.domElement);
  }

  private onChanged(stringvalue:any): void {
    this.properties.selectedusers=stringvalue;
    this.render();
  }


  protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
    return {
      pages: [
        {
          header: {
            description: strings.PropertyPaneDescription
          },
          groups: [
            {
              groupName: strings.BasicGroupName,
              groupFields: [
                PropertyPanetextboxcustom(this.properties.selectedusers,this.onChanged.bind(this), this.context)
              ]
            }
          ]
        }
      ]
    };
  }
}

React component (CustomPeoplePicker.tsx)

In this file we creating the office UI fabric people picker component, So created component will be turned as property pane custom field in another file.

import * as React from 'react';
import { IPersonaProps } from 'office-ui-fabric-react/lib/Persona';
import { IPropertyFieldMessageBarPropsInternal } from './PropertyPaneControl';  
import { NormalPeoplePicker } from 'office-ui-fabric-react/lib/Pickers';
import { SPHttpClient, SPHttpClientResponse } from '@microsoft/sp-http'; 
import { autobind } from 'office-ui-fabric-react/lib//Utilities'; 

export default class customtext extends React.Component<IPropertyFieldMessageBarPropsInternal, {}> {

  public render(): React.ReactElement<{}> {
    return (
      <div>
          <NormalPeoplePicker onResolveSuggestions={this._onFilterChanged}  resolveDelay={200}  onChange={this._onChange1}/>
      </div>
    );
  }

  private _onChange1 = (items?:IPersonaProps[]) => {
    this.props.onPropertyChange(items);
  };

  @autobind 
  private _onFilterChanged(filterText: string) { 
    if (filterText) { 
      if (filterText.length > 2) { 
        return this.searchPeople(filterText);         
      } 
    } else { 
      return []; 
    } 
  } 

  private searchPeople(terms: string): IPersonaProps[] | Promise<IPersonaProps[]> { 
    return new Promise<IPersonaProps[]>((resolve, reject) => 
      this.props.spcontect.spHttpClient.get(`${this.props.spcontect.pageContext.web.absoluteUrl}/_api/search/query?querytext='*${terms}*'&rowlimit=10&sourceid='b09a7990-05ea-4af9-81ef-edfab16c4e31'`, 
        SPHttpClient.configurations.v1, 
        { 
          headers: { 
            'Accept': 'application/json;odata=nometadata', 
            'odata-version': '' 
          } 
        }).then((response: SPHttpClientResponse): Promise<{ PrimaryQueryResult: any }> => { 
          return response.json(); 
        }).then((response: { PrimaryQueryResult: any }): void => { 
          let relevantResults: any = response.PrimaryQueryResult.RelevantResults; 
          let resultCount: number = relevantResults.TotalRows; 
          let people = []; 
          if (resultCount > 0) { 
            relevantResults.Table.Rows.forEach(function (row) { 
              let persona: IPersonaProps = {}; 
              row.Cells.forEach(function (cell) { 
                if (cell.Key === 'JobTitle') 
                  persona.secondaryText = cell.Value; 
                if (cell.Key === 'PictureURL') 
                  persona.imageUrl = cell.Value; 
                if (cell.Key === 'PreferredName') 
                  persona.primaryText = cell.Value; 
              }); 
              people.push(persona); 
            }); 
          } 
          resolve(people); 
        }, (error: any): void => { 
          reject(); 
        })); 
  }
}

Property pane component file (PropertyPaneControl.ts)

In this file, we implement the generic interface of IPropertyPaneField and rendering the office UI fabric people picker component which we created. at the end of the file, we also created one function for calling the property pane field, basically this will help us to call without calling as a new class we can directly call this function.  

import * as React from 'react';
import * as ReactDom from 'react-dom';
import {  
  IPropertyPaneCustomFieldProps,  
  IPropertyPaneField,  
  PropertyPaneFieldType  
} from '@microsoft/sp-webpart-base';  
import customtext from './CustomPeoplePicker';

export interface IPropertyFieldMessageBarPropsInternal extends IPropertyPaneCustomFieldProps {  
  onPropertyChange(items:any[]): void;    
  onRender(elem: HTMLElement): void;
  onDispose(elem: HTMLElement): void;
  key: string;  
  spcontect?:any|null;
}  


export default class propertypanecontrol implements IPropertyPaneField<IPropertyFieldMessageBarPropsInternal> {
  public properties: IPropertyFieldMessageBarPropsInternal;  
  public targetProperty: string;  
  public type: PropertyPaneFieldType = PropertyPaneFieldType.Custom;  
 // private onPropertyChange: (propertyPath: string, oldValue: any, newValue: any) => void;  
  private key: string;  
  private elem: HTMLElement;

  constructor(targetProperty: string,  userproperties: IPropertyFieldMessageBarPropsInternal) {
    this.targetProperty = targetProperty;
    this.render = this.render.bind(this);  
    this.properties = userproperties;  
    this.properties.onDispose = this.dispose;  
    this.properties.onRender = this.render;  
    this.properties.spcontect=userproperties.spcontect;
  //  this.onPropertyChange = userproperties.onPropertyChange;  
    this.key = userproperties.key;  
  }

  public render(elem: HTMLElement): void {
    if (!this.elem) {
      this.elem = elem;
    }
    const element: React.ReactElement<IPropertyFieldMessageBarPropsInternal> = React.createElement(customtext, {  
      onDispose: null,  
      onRender: null,  
      onPropertyChange: this.onChanged.bind(this),
      key: this.key,
      spcontect: this.properties.spcontect 
    });
    ReactDom.render(element, elem);
  }

  private onChanged(selectedusers: any): void {
    this.properties.onPropertyChange(selectedusers);
  }

  private dispose(elem: HTMLElement): void {  
  }
}

export function PropertyPanetextboxcustom(selectedusers:any, onChanged,spcontect?:any|null): IPropertyPaneField<IPropertyPaneCustomFieldProps> { 
  var newProperties: IPropertyFieldMessageBarPropsInternal = {  
    onPropertyChange: onChanged,  
    onDispose: null,  
    onRender: null,  
    key: 'test',
    spcontect:spcontect
  };  
  return new propertypanecontrol(selectedusers,newProperties);  
} 

Web part body react component (PropertyPaneSpFx.tsx)

In this file we creating web parts body part as react component, we used the office UI fabric Persona component to list the selected users 

import * as React from 'react';
import styles from './PropertyPaneSpFx.module.scss';
import { IPropertyPaneSpFxWebPartProps } from '../PropertyPaneSpFxWebPart';
import { escape } from '@microsoft/sp-lodash-subset';
import { Persona } from 'office-ui-fabric-react/lib/Persona';

export default class PropertyPaneSpFx extends React.Component<IPropertyPaneSpFxWebPartProps, {}> {
  public render(): React.ReactElement<IPropertyPaneSpFxWebPartProps> {
    return (
      <div className={ styles.propertyPaneSpFx }>
        <div className={ styles.container }>
          <div className={ styles.row }>
            <div className={ styles.column }>
              <span className={ styles.title }>Welcome to Ravichandran blog!</span>
              <p className={ styles.subTitle }>Seleted Users</p>
                {this.props.selectedusers.map((row, index) => (
                        <Persona {...row}  />
                ))}
            </div>
          </div>
        </div>
      </div>
    );
  }
}

Click below button to redirect Microsoft code galley to download this project

Sharing is caring!

If you have any questions, feel free to let me know in the comments section.
Happy coding!!!

4 thoughts on “Office UI Fabric People Picker in Property Pane – SharePoint Framework (SPFx)

Comments

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s