PnP React Controls Part 1 – Placeholder Control

Introduction

Hi friends, this is a multi part series of posts where I will explain the usage of PnP React Controls. How the control can be used inside your SharePoint Framework (SPFx) project. At the end of the blog, I had provided the link to my github repo which has multiple web parts for different controls. Also, I recommend you to go through the readme file on the repo to understand the toolchain used in the project.

Since this is the first part in this series, I will go through the process of creating the web parts and the configuration. In the upcoming posts, I will directly jump into the control usage and how to use it in the project.

In this post, we will see how we can use the Placeholder control

Intro to Placeholder

As the name implies, this control can be used as a placeholder control for the entire web part. When the web part loads and there are some mandatory properties that needs to be configured, usually we will design a separate control or design to tell the user or admin that the properties has to be configured. Instead of designing the control from the scratch, we can leverage this control which comes in handy and saves a lot of time. When the properties are configured, the actual web part is displayed, when the mandatory properties are not configured, the placeholder control is shown. The control comes with few of the properties which can be configured to customize the look and feel of the control.

Focus on the Code

Let us start by creating a new SPFx webpart project using SharePoint yeoman generator, before that create a folder where you would like to create the web part.

You can also follow the documentation from Microsoft to create the new extension project.

Build your first SharePoint client-side web part

Navigate to the folder and run the below command.

yo @microsoft/sharepoint

The generator will asks you couple of questions,

  • Enter the webpart name as your solution name, and then select Enter.
  • Select Create a subfolder with solution name for where to place the files.
  • Select N to allow the solution to be deployed to all sites immediately.
  • Select N on the question if solution contains unique permissions.
  • Enter the webpart name
  • Enter the webpart description
  • Choose the framework as ‘React

NoteI have used @microsoft/generator-sharepoint version 1.14.0. All the other packages used in this project are also the updated latest version. You can also refer to the readme file for the packages installed.

Let us see the file webpart.ts.

import * as React from 'react';
import * as ReactDom from 'react-dom';
import { Version } from '@microsoft/sp-core-library';
import {
    IPropertyPaneConfiguration,
    PropertyPaneTextField
} from '@microsoft/sp-property-pane';
import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base';
import { IReadonlyTheme } from '@microsoft/sp-component-base';
import * as strings from 'PlaceholderDemoWebPartStrings';
import PlaceholderDemo from './components/PlaceholderDemo';
import { IPlaceholderDemoProps } from './components/IPlaceholderDemoProps';

export interface IPlaceholderDemoWebPartProps {
    property1: string;
    property2: string;
}

export default class PlaceholderDemoWebPart extends BaseClientSideWebPart<IPlaceholderDemoWebPartProps> {

    private _isDarkTheme: boolean = false;
    private _environmentMessage: string = '';

    protected onInit(): Promise<void> {
        this._environmentMessage = this._getEnvironmentMessage();

        return super.onInit();
    }

    public render(): void {
        const element: React.ReactElement<IPlaceholderDemoProps> = React.createElement(
            PlaceholderDemo,
            {
                property1: this.properties.property1,
                property2: this.properties.property2,
                onConfigurePropPane: this.configurePropPane,
                displayMode: this.displayMode
            }
        );

        ReactDom.render(element, this.domElement);
    }

    private _getEnvironmentMessage(): string {
        if (!!this.context.sdks.microsoftTeams) { // running in Teams
            return this.context.isServedFromLocalhost ? strings.AppLocalEnvironmentTeams : strings.AppTeamsTabEnvironment;
        }

        return this.context.isServedFromLocalhost ? strings.AppLocalEnvironmentSharePoint : strings.AppSharePointEnvironment;
    }

    protected onThemeChanged(currentTheme: IReadonlyTheme | undefined): void {
        if (!currentTheme) {
            return;
        }

        this._isDarkTheme = !!currentTheme.isInverted;
        const {
            semanticColors
        } = currentTheme;
        this.domElement.style.setProperty('--bodyText', semanticColors.bodyText);
        this.domElement.style.setProperty('--link', semanticColors.link);
        this.domElement.style.setProperty('--linkHovered', semanticColors.linkHovered);
    }

    protected onDispose(): void {
        ReactDom.unmountComponentAtNode(this.domElement);
    }

    protected get dataVersion(): Version {
        return Version.parse('1.0');
    }

    public configurePropPane = (): void => {
        this.context.propertyPane.open();
    }

    protected get disableReactivePropertyChanges(): boolean {
        return true;
    }

    protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
        return {
            pages: [
                {
                    header: {
                        description: strings.PropertyPaneDescription
                    },
                    groups: [
                        {
                            groupName: strings.BasicGroupName,
                            groupFields: [
                                PropertyPaneTextField('property1', {
                                    label: 'Property 1'
                                }),
                                PropertyPaneTextField('property2', {
                                    label: 'Property 2'
                                })
                            ]
                        }
                    ]
                }
            ]
        };
    }
}

Below are the changes that are done to the above file

  • Replaced the property description with the following 2 properties
    1. property1 – string
    2. property2 – string
  • Created a method named configurePropPane to opent the property pane when the button is clicked in the placeholder control
  • Also, passed in the DisplayMode property, so that the configure button is visible only when the page is in the edit mode.
  • Created 2 text field property to capture the properties of the web part.

Let us see the file webpart.tsx.

import * as React from 'react';
import { IPlaceholderDemoProps } from './IPlaceholderDemoProps';
import { Placeholder } from "@pnp/spfx-controls-react/lib/Placeholder";
import { DisplayMode } from '@microsoft/sp-core-library';

export default class PlaceholderDemo extends React.Component<IPlaceholderDemoProps, {}> {
    public render(): React.ReactElement<IPlaceholderDemoProps> {
        const {
            property1,
            property2,
        } = this.props;
        return (
            <div>
                {(property1 && property2) ? (
                    <>
                        <div>Property 1: {property1}</div>
                        <div>Property 2: {property2}</div>
                    </>
                ) : (
                    <Placeholder iconName='Edit'
                        iconText='Configure your web part'
                        description='Please edit the page and configure the web part.'
                        buttonLabel='Configure'
                        hideButton={this.props.displayMode === DisplayMode.Read}
                        onConfigure={this.props.onConfigurePropPane} />
                )}
            </div>
        );
    }
}

Below are the changes done to this file

  • Following are the properties that are passed to this file
    1. property1
    2. property2
    3. displayMode
    4. configurePropertyPane
  • Checking whether property1 & property2 has values or not. If it has values then display the actual web part with the configured values, if it doesn’t then display the placeholder control.
  • The placeholder control has few properties which are listed below
    • iconName – Fluent UI icon name to be displayed on the control
    • iconText – Text to be displayed on the right of the icon
    • description – Description to be displayed below the title icon and text
    • buttonLabel – Button text
    • hideButton – When the button should be hidden from the user. Usually when the page is in view mode, the button is hidden
    • onConfigure – The method to call when the button is clicked. Usually, the property pane panel is opened when the button is clicked.

Reference URL

Conclussion

This article is to help the community who are not aware of the PnP controls. There are lot of controls available and we can make use of those controls and we can save a lot of time and implement those in designing the business logic of the web part or the application.

sudharsank/pnpcontrols-demo: Demo project on different PnP React Controls. (github.com)

Happy Coding…

One thought on “PnP React Controls Part 1 – Placeholder Control

  1. Pingback: PnP React Controls Part 2 – Variant Theme Provider | Knowledge Share

Leave a Reply

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 )

Twitter picture

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

Facebook photo

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

Connecting to %s