Hi SharePoint fellows,
In this post, I would like to tackle a user experience I wanted to implement in one of my projects in the SharePoint modern experience: A contextual command opening a form contained in a panel.
Let’s use SPFx extensions
A contextual command means I would need to implement a ListView Command Set extension. Using this kind of solution, I am able to add a contextual button in the command bar and in the contextual item menu. For those of you familiar with the classic experience development, it is the successor of Custom Actions located in the ribbon and the ECB.
From the official documentation, we can find a very well explained tutorial explaining how to implement this custom command.
I don’t want a dialog !
When the need comes for a custom UI in the context of a ListView command set, the offered possibility is the use of the Dialog API. This API wraps prepared Office UI Fabric dialogs, either standard prompt or alert or with custom dialog content. It is quite easy to use once you know how to do it 😉 Check out the official tutorial to learn more.
In most cases, the dialog approach is a good fit, when UI is quite small and the user action flow is straight-forward, it’s probably not worthy to do anything else.
In my case, however, I needed a larger, more complex and dynamic UI. I had not a good feeling about using a dialog for that. Moreover, A nice-to-have I had in mind was that the user could be able to still see the content of the list while working in the custom form, the dialog wouldn’t fit this one.
What about a panel instead ?
Using the Office UI Fabric React library, we can easily use a Panel and implement our own content in it. The only thing we need is to call the render() method from the ReactDOM.
DOM Injection is officially discouraged !
Those who know me will be aware that I’m pretty in line with Microsoft’s recommendation about NOT doing DOM injection in SharePoint UI development. It actually means you should avoid messing around with the standard SharePoint UI. The SharePoint developers struggle to deliver the best user experience with many advanced features, and there are new stuff almost every week ! The DOM of the SharePoint pages is continuously evolving. you CAN’T and SHOULDN’T rely on the existence or the format or content of any particular HTML element because you have no assurance it will be there and remain the same forever. Moreover, altering these elements might end up in breaking a standard SharePoint UI feature.
With this recommendation taken into account, we can, however, be pretty sure that one special HTML element will always be there; the <body> element. We won’t alter or remove any child element from it but instead adding one simple <div> element that will be the container for our React control.
Let’s implement it !
As a sample, let’s assume the following requirements:
- The solution is for SharePoint Modern UI only
- We need a custom button in the command bar
- The button only appears when a single item is selected
- When the button is clicked, a panel pops up
- The panel contains a simple form allowing to update the Title field of the item
We will create such a solution in the form of a ListView Command Set solution. I won’t go too much into the basic details here, If you need more details, you can check the following tutorial.
The main thing we will require is to install the React dependencies in our solution in order to be able to use the Office UI Fabric React component. I also install the PnP JS library dependencies for the sake of simplicity
When your solution is scaffolded, type the following command in your console
npm install react react-dom @types/react @types/react-dom @pnp/common @pnp/logging @pnp/odata @pnp/sp –save
For some reasons I didn’t dig into, the latest versions of React libraries caused the solution not being able to build (something related to React prop types, …). If you run into the same problem, here are the React version that worked I just grab from another extension solution that I had:
Okay, ready to code !
First, let’s create a React component that will render the Panel and its content. In my solution, I have a component source file called CustomPanel.tsx within the src/components folder.
This component renders a simple form contained in a Panel component. When the Save button is clicked, A REST call (using PnP JS) will update the title of the current item.
In our Extension main class, let’s do some changes, First we need to create the container for our SPFx component :
We create the DOM element in the extension initialization step and keep a reference to it.
We implement the methods that will help display and close the Panel. Notice that we grab the List ID our component needs from the current page context.
And we finally edit the extension life cycle handlers to suit our needs. The command has a token CMD_PANEL. The button shows up only if a single list item is selected, and when clicked, will open up the panel passing it in the arguments that will help us working on the selected item.
Since we used a custom token for our command, we will need to update the manifest accordingly. Edit the content of the YourCommandSet.manifest.json and change the value of the items property to :
Select an item and see the Open editor button appearing in the command bar, click this button to see the following panel showing up
And there is it, we can now open up a panel with our custom content within the context of an SPFx ListView Command Set extension.
It works !
- We don’t affect anything in the existing DOM
- We rely on the only thing that will probably remain forever (except if HTML basics changes completely, which is unlikely…)