Introduction
I love the SharePoint Design web site. It is a beautiful web site that provides design guidance on how to create beautiful and fast sites, pages, and web parts with SharePoint in Office 365.
However, the site does not provide you with enough code samples to tell you how to create the beautiful web parts they show you.
This series is intended as a companion to the SharePoint Design site. It provides you with code samples and detailed how-to information for every design topic.
It should help you create web parts that look exactly like the ones on the SharePoint Design site.
Today’s post is the third on property panes. In part I, we covered the various types of property panes. In part II we showed how to create non-reactive web parts and how to add a loading indicator.
Today, we’ll look at out-of-the-box web parts and we’ll discuss how to replicate some of their property panes in your own web parts.
If you want to download the code samples used in this article, download from the .
Create a choice group with images
One of the most frequently asked questions I see with property panes is about the property pane choice groups with images.
The out-of-the-box Highlighted content, Hero, Image gallery are examples of how the choice group property field is used.
You should use the property pane choice group when you have a small number of choices — I recommend less than 7, no more than 9 choices — where the user may not be immediately able to understand the differences between the choices.
For example, the difference between the Brick, Grid and Carousel layout in the Image gallery web part may not be immediately obvious to everyone. Adding images to represent each choice makes it easier to understand:
As it turns out, it’s not very complicated to do.
Let’s reproduce the Image gallery layout setting for our demo. To do so, follow these steps:
- In your
[YourWebPartName]WebPart.ts
file, create a property to store the new setting you want. We’ll uselayout
. To make things easier to read, I’ll just use possible values ofBrick
,Grid
, orCarousel
, but feel free to use an integer value, anenum
, or any other data type you want:export interface IChoiceGroupWebPartProps { layout: 'Brick'|'Grid'|'Carousel'; }
- At the top of the file, there should already an import statement for the
@microsoft/sp-property-pane
. To it, add an import forPropertyPaneChoiceGroup
, which is the type of field we’ll need to use:import { IPropertyPaneConfiguration, PropertyPaneChoiceGroup } from '@microsoft/sp-property-pane';
- You’ll need an image or an icon for every choice you want to show in your choice group. I like to use SVG files with dimensions of 32px by 32px. I also like to store my web part images in a folder called
assets
in the[YourWebPartName]
folder. For this sample, we’ll usebrick.svg
,grid.svg
andcarousel.svg
. Feel free to store the images wherever you like.
- The easiest way to include the image in your bundle is to use a
require
statement with the path to the file. Because I’ll need to use each image twice (for each choice’simageSrc
andselectedImageSrc
), we’ll define a variable for each image we’ll need at the top of thegetPropertyPaneConfiguration
:protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration { const layoutBrick: string = require('./assets/brick.svg'); const layoutGrid: string = require('./assets/grid.svg'); const layoutCarousel: string = require('./assets/carousel.svg'); ...
- Add a
propertyPaneChoiceGroup
if your property pane’sgroupFields
, making sure to pass the same image for theimageSrc
andselectedImageSrc
. Of course, you can have a different image for the selected and un-selected images if you’d like, but I don’t usually. YougetPropertyPaneConfiguration
method will look as follows:protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration { const layoutBrick: string = require('./assets/brick.svg'); const layoutGrid: string = require('./assets/grid.svg'); const layoutCarousel: string = require('./assets/carousel.svg'); return { pages: [ { header: { description: null }, groups: [ { //groupName: strings.BasicGroupName, groupFields: [ PropertyPaneChoiceGroup('layout', { label: "Layout", // don't forget to localize your test in a real-world solution options: [ { key: 'Brick', text: 'Brick', selectedImageSrc: layoutBrick, imageSrc: layoutBrick, }, { key: 'Grid', text: 'Grid', selectedImageSrc: layoutGrid, imageSrc: layoutGrid, }, { key: 'Carousel', text: 'Carousel', selectedImageSrc: layoutCarousel, imageSrc: layoutCarousel, } ] }), ] } ] } ] }; }
Note that in the above code, I did not localize the text to make the code easier to read. Please consider localizing all your text.
- I recommend that you define a default
layout
value in your[YourWebPartName]WebPart.manifest.json
‘spreconfiguredEntries
:"properties": { "layout": "Brick" }
Remember that your changes to the
manifest.json
file will not take effect until you re-build your web part and re-add it to your workbench.
If you run gulp serve
on your web part, you should see the following property pane:
Create a choice group with Fabric icons
If you’re lucky enough to find Office UI Fabric icons that suit your needs, it is even easier to create a choice group with icons.
To do so, follow these steps:
- Add a property to store your new setting. We’ll use
shape
and we’ll allowCircle
,Square
, orTriangle
:export interface IChoiceGroupWebPartProps { layout: 'Brick'|'Grid'|'Carousel'; // ADDED: For icon choice group shape: 'Circle'|'Square'|'Triangle'; // END: added }
- Add a
PropertyPaneChoicegroup
control in yourgetPropertyPaneConfiguration
method, making sure to pass aiconProps
value instead of animageSrc
andselectedImageSrc
. TheiconProps
should contain a single propertyofficeFabricIconFontName
, which should be set to the name of the icon you wish to use:protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration { const layoutBrick: string = require('./assets/brick.svg'); const layoutGrid: string = require('./assets/grid.svg'); const layoutCarousel: string = require('./assets/carousel.svg'); return { pages: [ { header: { description: null }, groups: [ { //groupName: strings.BasicGroupName, groupFields: [ PropertyPaneChoiceGroup('layout', { label: "Layout", // don't forget to localize your test in a real-world solution options: [ { key: 'Brick', text: 'Brick', selectedImageSrc: layoutBrick, imageSrc: layoutBrick, }, { key: 'Grid', text: 'Grid', selectedImageSrc: layoutGrid, imageSrc: layoutGrid, }, { key: 'Carousel', text: 'Carousel', selectedImageSrc: layoutCarousel, imageSrc: layoutCarousel, } ] }), // ADDED: For icon-based choice group PropertyPaneChoiceGroup('shape', { label: "Shape", // don't forget to localize your test in a real-world solution options: [ { key: 'Circle', text: 'Circle', iconProps: { officeFabricIconFontName: 'CircleShapeSolid' } }, { key: 'Square', text: 'Square', iconProps: { officeFabricIconFontName: 'SquareShapeSolid' } }, { key: 'Triangle', text: 'Triangle', iconProps: { officeFabricIconFontName: 'TriangleShapeSolid' } } ] }) // END: Added ] } ] } ] }; }
- As before, I recommend that you define a default
shape
value in your[YourWebPartName]WebPart.manifest.json
‘spreconfiguredEntries
:"properties": { "shape": "Square" }
Running gulp serve
will give you the following:
Conditional field in property panes
Sometimes, you want to show settings that are dependent on each other. If a user selects one choice, you may want to show a dependent setting, but if they select another choice, you may want to hide that setting.
The out-of-the-box Yammer web part does this by hiding or showing the Number of conversations to show setting depending on the conversation source the user selects.
This is what I’ll call conditional fields for the purpose of this conversation.
Making fields conditional can help improve the user experience by removing choices that do not apply, therefore reducing the complexity of choice (remember Hick’s law).
However, there are some guidelines you should follow:
- Make sure that the user easily understands the reason why a field is shown or hidden. For example, the Yammer web part lets you see plainly that the Number of conversations is impacted by the conversation source.
- Make sure that fields that depend on each other are logically grouped/visible together. For example, do not place one field on a property pane page (or step) and the field that depends on it on another page.
- If you find it difficult to convey the relationship between two fields, you may wish to enable and disable the dependent field instead of hiding it. For bonus points, you should add some text (or, at least, some form of tooltip) that explains why the field is disabled.
- If you want some options to be available some times, and not available some other times, consider disabling the option instead of hiding it. If the option will never be available because of a specific condition (e.g.: user doesn’t have sufficient permissions, the wrong source selected, etc.), consider hiding the field instead.
To demonstrate this concept, we’ll recreate the Select conversation source, Search for a sourcem and Number of conversations to show fields from the Yammer web part. To do so, follow these step steps:
- In your
[YourWebPartName]WebPart.ts
, add properties to store the conversation source, the search criteria, and the number of conversations:export interface IConditionalFieldWebPartProps { conversationSource: 'Group'|'User'|'Topic'|'Home'; searchCriteria: string; numberOfConversations: number; }
- In the
getPropertyPaneConfiguration
, add the following code to render the property pane fields. Again, code is not localized to make it easier to read, please localize text in a real-world scenario:protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration { return { pages: [ { header: { description: "Select conversation source from groups, topics, users, or home." }, groups: [ { groupFields: [ PropertyPaneDropdown('conversationSource',{ label: "Select conversation source", selectedKey: this.properties.conversationSource, options: [ { key: "Group", text: "Group" }, { key: "User", text: "User" }, { key: "Topic", text: "Topic" }, { key: "Home", text: "Home" }, ] }), PropertyPaneTextField('searchCriteria', { label: "Search for a source", placeholder: "Type to search" }), PropertyPaneDropdown('numberOfConversations',{ label: "Number of conversations to show", selectedKey: this.properties.conversationSource, options: [ { key: 4, text: "Small - 4 conversations" }, { key: 8, text: "Medium - 8 conversations" }, { key: 12, text: "Large - 12 conversations" } ] }) ] } ] } ] }; }
-
To Make the Search for a source field hide if the conversation source field is set to Home, add
this.properties.conversationSource !== "Home" &&
in front of the line that begins withPropertyPaneDropdown('numberOfConversations'
. Doing so says "Only execute the next line if the conversation source is not equal to Home". The newgetPropertyPaneConfiguration
method should look like this:protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration { return { pages: [ { header: { description: "Select conversation source from groups, topics, users, or home." }, groups: [ { groupFields: [ PropertyPaneDropdown('conversationSource',{ label: "Select conversation source", selectedKey: this.properties.conversationSource, options: [ { key: "Group", text: "Group" }, { key: "User", text: "User" }, { key: "Topic", text: "Topic" }, { key: "Home", text: "Home" }, ] }), // ADDED: conditional rendering for field this.properties.conversationSource !== "Home" && PropertyPaneTextField('searchCriteria', { label: "Search for a source", placeholder: "Type to search" }), PropertyPaneDropdown('numberOfConversations',{ label: "Number of conversations to show", selectedKey: this.properties.conversationSource, options: [ { key: 4, text: "Small - 4 conversations" }, { key: 8, text: "Medium - 8 conversations" }, { key: 12, text: "Large - 12 conversations" } ] }) ] } ] } ] }; }
-
No necessary for this example, but if you want the mimic the Yammer web part and enable/disable the Number of conversations field on the selection, add a
isDisabled
attribute to thePropertyPaneDropDown
field, as follows:protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration { return { pages: [ { header: { description: "Select conversation source from groups, topics, users, or home." }, groups: [ { groupFields: [ PropertyPaneDropdown('conversationSource',{ label: "Select conversation source", selectedKey: this.properties.conversationSource, options: [ { key: "Group", text: "Group" }, { key: "User", text: "User" }, { key: "Topic", text: "Topic" }, { key: "Home", text: "Home" }, ] }), this.properties.conversationSource !== "Home" && PropertyPaneTextField('searchCriteria', { label: "Search for a source", placeholder: "Type to search" }), PropertyPaneDropdown('numberOfConversations',{ //ADDED: To enable/disable field based on selection disabled: this.properties.conversationSource !== "Home", label: "Number of conversations to show", selectedKey: this.properties.conversationSource, options: [ { key: 4, text: "Small - 4 conversations" }, { key: 8, text: "Medium - 8 conversations" }, { key: 12, text: "Large - 12 conversations" } ] }) ] } ] } ] }; }
- Set default property values in your
[YourWebPartName]WebPart.manifest.json
, underpreconfiguredEntries
:"properties": { "conversationSource": "Group", "searchCriteria": "", "numberOfConversations": 8 }
Running a new gulp serve
should give you a web part pane that behaves like the Yammer web part:
The logic for enabling/disabling and hiding/showing property pane fields in Yammer is more complicated than what we cover in this example, but I hope it’ll do for now.
In my opinion, the Yammer Number of conversations should probably tell you why it is disabled. If it is never supposed to be available from any other sources than Home, it should probably be hidden instead of disabled — but that’s my personal preference, not a hard rule.
Note: Don’t worry, we’ll cover how to create a drop-down box with icons in a later post.
Creating conditional property pane groups
The out-of-the-box Quick chart web part allows users to choose between a Column chart or a Pie chart. If the user selects a Column chart, the property pane will show a Data property group and a Layout property group; If they choose Pie chart, the property pane will hide the Layout property group and only show the Data property group.
By hiding or showing only the property pane groups that are relevant to the user’s selected chart type, the Quick chart web part helps the user quickly configure the web part without presenting unnecessary settings.
Imagine if — instead of hiding the Layout property pane group — we disabled the property group entirely. The screen would be needlessly cluttered.
A while ago, I created a sample for the SharePoint , which extends on the Quick chart web part and allows users to configure 9 different types of charts. Depending on which chart type the user selects, multiple property pane groups appear and disappear to help users make sense of the options available to them. If we didn’t hide some sections, the property pane would be much too complicated to use.
For this code sample, we’ll emulate the Quick chart property pane and allow users to select one of two types of charts. Depending on the selection, we’ll hide or show the Layout property pane group. The Data property pane group will always be available.
To keep this sample simple, we won’t add any property fields in either property pane group — we’ll just put some text to illustrate the concept. If you really want to see how to implement the full Quick chart property pane, check out the .
To mimic the Quick chart web part property pane, follow these steps:
- As before, add a property to your web part to store the Chart type selection:
export interface IConditionalGroupWebPartProps { chartType: "Column" | "Pie"; }
-
Change the
getPropertyPaneConfiguration
method to create anIPropertyPaneConfiguration
variable to store the property pane configuration (instead of simply returning it) so that we can manipulate the content of the property pane configuration.protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration { const chartDescription: string = this.properties.chartType === "Column" ? "Use a column chart to show data changes over time or comparisons among items. Categories are typically shown along the horizontal axis and values along the vertical axis." : "Use a pie chart to show percentages of a whole. Best when used with fewer than seven categories."; const configuration: IPropertyPaneConfiguration = { pages: [ { header: { description: "Select a chart type and then select a data source. You can enter up to 12 data points, or show up to 50 data points if you use a SharePoint list on this site as the data source." }, displayGroupsAsAccordion: true, groups: [ { groupName: "Chart type", groupFields: [ PropertyPaneChoiceGroup('chartType', { options: [ { key: 'Column', text: 'Column chart', iconProps: { officeFabricIconFontName: 'BarChart4' } }, { key: 'Pie', text: 'Pie chart', iconProps: { officeFabricIconFontName: 'PieDouble' } } ] }), PropertyPaneLabel('chartType', { text: chartDescription }), ] }, { groupName: "Data", isCollapsed: false, groupFields: [ PropertyPaneLabel('data', { text: "This is some sample text for the data property group." }), ] } ] } ] }; return configuration; }
-
At the end of the same method, add some conditional logic to insert a new element in the
groups
array usingpush
:protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration { const chartDescription: string = this.properties.chartType === "Column" ? "Use a column chart to show data changes over time or comparisons among items. Categories are typically shown along the horizontal axis and values along the vertical axis." : "Use a pie chart to show percentages of a whole. Best when used with fewer than seven categories."; const configuration: IPropertyPaneConfiguration = { pages: [ { header: { description: "Select a chart type and then select a data source. You can enter up to 12 data points, or show up to 50 data points if you use a SharePoint list on this site as the data source." }, displayGroupsAsAccordion: true, groups: [ { groupName: "Chart type", groupFields: [ PropertyPaneChoiceGroup('chartType', { options: [ { key: 'Column', text: 'Column chart', iconProps: { officeFabricIconFontName: 'BarChart4' } }, { key: 'Pie', text: 'Pie chart', iconProps: { officeFabricIconFontName: 'PieDouble' } } ] }), PropertyPaneLabel('chartType', { text: chartDescription }), ] }, { groupName: "Data", isCollapsed: false, groupFields: [ PropertyPaneLabel('data', { text: "This is some sample text for the data property group." }), ] } ] } ] }; // ADDED: To insert a conditional group // If the selected type is not Column, we don't need to make any further changes if (this.properties.chartType !== "Column") { return configuration; } // Get the list of property groups const { groups } = configuration.pages[0]; // Insert a property pane groups.push({ groupName: "Layout", isCollapsed: false, groupFields: [ PropertyPaneLabel('layout', { text: "This is some sample text for the layout property group." }), ] }); // END: added return configuration; }
- Make sure to define some default properties in your web part’s
manifest.json
file:"properties": { "chartType": "Column" }
On your next gulp serve
, you’ll have a web part property pane hides/shows conditional property groups just like the Quick chart web part:
At the risk of repeating myself: the sample does not localize the text to help keep the code easy to read. It is killing me to do this. Please localize your text in a real solution.
Conclusion
In today’s post, we discussed how to create image choice groups, create conditional property pane fields and conditional property pane groups.
All the code samples for this post can be found in from the .
Every time I created a web part for this series so far, I wanted to change the body of the web part to make it look better (instead of using the default styles rendered by the Yeoman generator). It took everything in me not to tinker with the web part body and create something that would be worthy of the SharePoint Design series.
In our next post, we’ll focus on the web part body.
Finally!!!
2 Comments
Hi, i would like to know if we can make the width of our webparts properties panel wider… I want it to make it 600px so it will fit better my big textarea.
Hi Bruno,
I don’t believe you can, but you can use a custom panel to make the text area a “pop out” field. Check out the sample at for an example of how I do that