SPFx Extensions List Item Clone Demo

I was fortunate enough to present in the PnP Monthly Community Call yesterday. I demoed a sample I contributed to the SharePoint Framework Extensions sample repository called JS-Command-Clone. The sample is a ListView Command Set SPFx Extension that enables the creation of duplicate list items and utilizes PnP JS Core.

Ryan Schouten has provided a great summary and write-up of the full call. You can also watch the recording directly on YouTube. Of course, you can even watch it here where I’ve directly queued up my demo:

Please feel free to reach out to me with any questions or other feedback. Thanks!

Here are some related links:

SPFx Extension Toastr Notifications Demo

Applies To: SharePoint Framework

I had the honor of taking part in the PnP JS SIG call (Patterns and Practices JavaScript Special Interest Group) earlier today. On the call I was able to demo a sample I contributed for the SharePoint Framework Extensions called jQuery-Application-Toastr.

You can find a great write-up of the call on the Tech Community. You can also just watch the video directly on YouTube. You can even watch it right here, conveniently queued up to my demo, wowee!

Here are some related links:

Thank You SPS Charlotte!

This weekend I had the honor of speaking at SharePoint Saturday Charlotte. Great attendance, great speakers, great sponsors, and great organizers! I had a fantastic time and I heard a lot of very positive feedback from attendees. SPSCLT 2017 was a huge success and I’m grateful I got to be a part of it.

SPSCLT17 Speakers
SPS CLT 2017 Speakers at TopGolf

Here’s a video of me hitting a golf ball for maybe the third time in my life. Behold my glory:

My Session

I presented Understanding SharePoint Framework Extensions. You can find the slides and a link to the samples demonstrated below. Here’s the outline:

  • SharePoint Development: A Brief History
  • Modern Pages
  • An Introduction to the SharePoint Framework
    • Client-side Web Parts
    • Tool Comparison
    • The SharePoint Workbench
    • Key npm and Gulp Tasks
    • Hello World Demo
  • SPFx Extensions: Extending Modern Pages
    • Types of SPFx Extensions
    • Current Extension Limitations
    • SPFx Extension Demos
  • SharePoint Patterns & Practices
    • What’s in PnP (Selective Highlights)
    • Staying Up to Date with PnP

Demo Troubles

Prior to SPS I noticed that there were some changes in my developer tenant that required some adjustments of the List Driven Placeholders sample. Specifically, the placeholders seemed to be switching from PageHeader to Top and from PageFooter to Bottom. This was pretty easy to work around, but I reached out via the issues list since I was nervous other changes were on their way.

Vesa reached out immediately and let me know I was “in slightly dangerous waters” and suggested I take screenshots just in case. I followed his advice (glad I did!) and even included his response in the slides as my official disclaimer, haha.

Needless to say, I was a little nervous and checked the demos constantly including 30 minutes before my session. Everything was working! However, during the actual session I received a lot of errors. I remember saying that I thought there might be some errors but that I had expected them on the site not during my gulp serve command. Perhaps I should’ve listened to myself.

Unfortunately, I let confirmation bias and the nerves of speaking overrule my troubleshooting skills. I assumed the dreaded breaking changes had gotten me! In reality, I hadn’t stopped my last gulp serve command from showing the hello world web part. A really simple and (normally) obvious mistake.

Fortunately, I was able to show the screenshots as Vesa had suggested and things worked out fine. I regret that I gave the impression that SPFx Extensions were temporarily broken. I’m also sorry my audience didn’t get to see the samples in their full glory (screenshots work, but aren’t as cool as seeing it actually do the magic).

Resources

You can find my slides here: Understanding SPFx Extensions – SPSCLT17

Many of these slides are original but several have been adapted from other sources (mostly Ignite).

Samples Demoed:

 

Despite my demo hiccup, things still went really well. It was a great day for the SharePoint community and the SPSCLT organizers should be very proud. Thanks Charlotte – see you next year!

Join me at Charlotte SharePoint Saturday on August 26th!

I will be presenting Understanding SharePoint Framework Extensions on Saturday, August 26th, 2017 in Charlotte, NC as part of SPS Charlotte!

I’m sure you’ll all attend just for my session – but stay for everything else. Just look at this lineup!

SPSCLT

Andrew Connell will be delivering the keynote, Where are We and Where are We Going?

My session is at 11:15 am and will focus is on understanding how the SharePoint Framework (SPFx) and the Extensions fit within the historical and modern approaches to SharePoint development. The goal is for attendees to know what SPFx is and when to use it, how the extensions fit in the framework, and to know where to go to get additional details. This session is perfect for power users, managers, and developers new to SPFx.

After my session, stick around for the unofficial “Part 2” at 2:15 pm, Andrew Connell’s Modern UI Extensions with the SharePoint Framework – Command Sets, Application, and Field Customizers where he’ll be providing additional depth, details, and demos.

After attending these 2 sessions you should have everything you need to dive into SPFx extensions just in time for the upcoming Release Candidate 1 and General Availability coming later this year!

jQuery Types Errors with the SharePoint Framework (and how to fix them)

Applies To: SharePoint Framework

The other day I was creating a SharePoint Framework solution and I decided to reference jQuery (cause I’m hip and modern). So, I created a webpart with no framework, added jQuery from a CDN to the externals section of my config/config.json, installed the jQuery types, and then slapped an import statement on my code. When I typed gulp serve, however, all I got was sadness. Specifically this sadness:

jQuery Types Errors

It’s tough to screenshot, but that’s 1000+ errors all related to the @types/jQuery node module. Most were some variation of:

Error – typescript – node_modules\@types\jQuery\index.d.ts(40,45): error TS1005: ‘,’ expected.

Why did this happen? I’ve literally only written one import statement! After some investigation, I found the cause of the issue and, fortunately, a solution.

The Problem

The issue can be found in the version of TypeScript included in the Yeoman Generator for the SharePoint Framework (currently 1.1.1). You can find the version in use by typing this command:

npm list typescript versions

This will show you the version installed in the local solution. The result will look something like this:

SPFx typescript

So, the SharePoint Framework is using TypeScript 2.2.2. What’s the big deal?

Well, if you get as desperate as I did, you might take a look at the barrel for @types/jquery (index.d.ts) where you’ll find a comment stating it requires TypeScript Version 2.3:

jQuery TypeScript.PNG

This mismatch is causing all the build errors for this dependency! ARG!!

The Solution

This fix is actually pretty simple once you know the problem. All you need to do is use a version of the @types/jQuery package that supports TypeScript 2.2.2.

The first step is to remove the current version:

npm uninstall @types/jquery

Then install a previous version compatible with TypeScript 2.2.2:

npm install @types/jquery@2.0.48 --save-dev

Now when you run gulp serve everything should work as expected!

 

Other Stuff

If you’re still having trouble, or you found this article while trying to figure out how to reference jQuery (or some other library), here’s some important details I skipped over previously:

Referencing jQuery

To reference an external library, you adjust the externals section of your config/config.json file. You can do this the easy way if the code is a Module or a slightly less easy way if it isn’t. You can determine which your external library is by using this helpful tool: SPFx Script Check

jQuery is a Module so you can just do this:

jquery config

Importing jQuery

To use jQuery in your code, just add the following import statement near the top of your file:

import * as $ from 'jQuery';

 

Extending SharePoint Framework Build Tasks

Applies To: SharePoint Framework

The SharePoint Framework (SPFx) uses gulp as the task runner that builds your solution. You can find out what tasks are available by typing gulp --tasks in the console in the root of your solution. But what if you want to extend these tasks?

There’s an excellent tutorial by Chaks Chandran that demonstrates how to do this: Integrate gulp tasks in SharePoint Framework toolchain. You can find the corresponding sample here: js-extend-gulp.

In this post, I’m going to demonstrate a few items that weren’t addressed in that tutorial including:

  • Checking for a custom parameter
  • Ending your task without a stream and not killing the pipeline
  • Getting a value from one of the Config files
  • Copying a solution file to another location

What Are We Building

I previously discussed how to specify your SharePoint Framework App Icon and in that post I noted that you should not only specify your icon in the solution package itself, but you should make it available outside as well so that you can set the App Catalog Icon URL property.

The best place to host this image is with the rest of your assets in your CDN. So, wouldn’t it be nice if the App Icon image file was copied to the dist folder when the rest of your solution was bundled? Let’s do it!

Basic Rigging

If your familiar with gulp you know that generally gulp tasks are defined in the gulpfile.js file in the root of your solution. More likely, however, you’re not familiar with gulp and don’t really know what a gulp task is for outside of the idea that they do things when you type them in the console.

A gulp task is just a JavaScript function that gets registered with a name (the command) and you can make it do whatever you want. In SPFx, however, all the gulp tasks are defined deep within the node_modules/@microsoft/gulp* folders. You don’t need to do anything with these (although they are worth checking out if your curious how they’re doing all this magic).

To jump in on the fun, you’ll need to use the build object and either rig your task into the pipeline or register it as a standalone task. Again, more detail can be found in the official tutorial. For our purposes, we don’t want a standalone task that we would have to call separately. We want our task to be executed along with the build.

Defining the Task

Open up your gulpfile.js file. Before the build.initialze(gulp) command add your sub task definition:

'use strict';

const gulp = require('gulp');
const build = require('@microsoft/sp-build-web');

let appIconToBundle = build.subTask('app-icon-to-bundle', function(gulp, buildOptions, done) {
	this.log('Wowee!');
});

build.initialize(gulp);

In line 6, we’re simply calling the build.subTask function provided by Microsoft to register our function with the name app-icon-to-bundle (there’s nothing special about this name, just following the same over-hypenation as the default sub tasks).

This doesn’t actually do anything yet because we’ve neither registered it as a standalone task or integrated it into the build pipeline.

Rigging the Task to the Build

You can “rig” your task in 3 spots: PreBuild, PostTypescript, and PostBuild. You can think of these as specifying an event handler and the handler is your sub task. We’re going to add ours to the end of the build by adding line 10 below:

'use strict';

const gulp = require('gulp');
const build = require('@microsoft/sp-build-web');

let appIconToBundle = build.subTask('app-icon-to-bundle', function(gulp, buildOptions, done) {
	this.log('Wowee!');
});

build.rig.addPostBuildTask(appIconToBundle);

build.initialize(gulp);

Now if we execute one of the building tasks (like “bundle” for instance) you’ll see our amazing task getting executed:

Basic Run

Properly Ending Your Task

Some of you may have noticed that there is a message about terminating early. In fact, if you run the gulp serve command you may be surprised to see it stops at the same spot and does none of the actual fanciness of serving your solution. WHAT DID YOU DO!?

The easiest way to fix this is by returning a stream and that’s exactly what is demonstrated in the official sample. But what about when you aren’t doing that? For our task we want to check some things and only proceed when it makes sense. This is hard to do in the context of a stream. The other option is to simply take advantage of the done callback parameter you are provided:

let appIconToBundle = build.subTask('app-icon-to-bundle', function(gulp, buildOptions, done) {
	this.log('Wowee!');
	done();
});

Now when you run the standard tasks you’ll still see our message but the pipeline doesn’t come to a screeching halt. Wowee, indeed.

Checking for a Custom Parameter

For our task, we only want to move the icon file if the --bundleicon parameter is specified. This is a custom parameter and detecting it is surprisingly easy:

let appIconToBundle = build.subTask('app-icon-to-bundle', function(gulp, buildOptions, done) {
	var bundleIcon = (process.argv.indexOf('--bundleicon') !== -1);
	this.log(bundleIcon ? 'Bundling!' : 'Nope!');
	done();
});

In line 7, we simply check for the presence of our custom parameter in the arguments and set a Boolean value. In line 8, we change the message based on the parameter’s inclusion. This could “easily” be extended to actually pull in values after your parameter.

To test this, simply run gulp bundle and then gulp bundle --bundleicon to see the 2 different messages. Our custom parameter works!

Getting a Value From One of the Config Files

Now we know when to actually move the icon based on the parameter, but how do we know where the icon actually is? The App Icon is an image file stored somewhere in the sharepoint folder. It is specified as the solution.iconPath property in the config/package-solution.json file.  More details here: SharePoint Framework App Icon

So, how do we get a value from one of the config files? First, we’ll need 2 helper objects. These objects are already included in your node_modules folder since they are used by the Microsoft build tasks. To gain access to them simply add some require statements below the existing require statements:

const gulp = require('gulp');
const build = require('@microsoft/sp-build-web');

const path = require('path');
const fs = require('fs');

let appIconToBundle = build.subTask('app-icon-to-bundle', function(gulp, buildOptions, done) {
...

Specifically line 6 and 7 above add the path and fs objects which make it easy to work with file paths and reading files. Here’s how we use them:

let appIconToBundle = build.subTask('app-icon-to-bundle', function(gulp, buildOptions, done) {
	var bundleIcon = (process.argv.indexOf('--bundleicon') !== -1);

	if(bundleIcon){
		//Get the config file path
		var psConfigPath = path.join(process.cwd(), 'config', "package-solution.json");

		//read the config file into a JSON object
		var psConfig = undefined;
        try {
            var content = fs.readFileSync(psConfigPath, 'utf8');
            psConfig = JSON.parse(content);
        }
        catch (e) { }

		//Verify an iconPath has been provided
        if(psConfig && psConfig.solution && psConfig.solution.iconPath){
			this.log('./sharepoint/' + psConfig.solution.iconPath)
		}
	}

	done();
});

In line 14 we use the path object to get an absolute path to the package-solution.json config file. Then in line 19 we use the fs object to read the text of the config file. Since the config files are all JSON files, we can simply parse it into an object in line 20.

In line 25 we verify that the property was actually set and has a value. If so, we write it to the log in line 26. We are adding the ‘./sharepoint/’ because the iconPath property is relative to the sharepoint folder in the root of our project.

Copying the App Icon to the Dist Folder

Copying the file to the dist folder can be done using standard gulp:

        if(psConfig && psConfig.solution && psConfig.solution.iconPath){
			gulp.src('./sharepoint/' + psConfig.solution.iconPath)
				.pipe(gulp.dest('./dist'));
		}

The Final Solution

Here’s the final gulpfile.js:

'use strict';

const gulp = require('gulp');
const build = require('@microsoft/sp-build-web');

const path = require('path');
const fs = require('fs');

//When the --bundleicon parameter is used, copies the App Icon image file (when specified) to the dist folder
let appIconToBundle = build.subTask('app-icon-to-bundle', function(gulp, buildOptions, done) {
	//Look for the --bundleicon parameter
	var bundleIcon = (process.argv.indexOf('--bundleicon') !== -1);

	if(bundleIcon){
		//Get the config file path
		var psConfigPath = path.join(process.cwd(), 'config', "package-solution.json");

		//read the config file into a JSON object
		var psConfig = undefined;
        try {
            var content = fs.readFileSync(psConfigPath, 'utf8');
            psConfig = JSON.parse(content);
        }
        catch (e) { }

		//Verify an iconPath has been provided
        if(psConfig && psConfig.solution && psConfig.solution.iconPath){
			//Copy the icon to the assets destination
			gulp.src('./sharepoint/' + psConfig.solution.iconPath)
				.pipe(gulp.dest('./dist'));
		}
	}

	done();
});

build.rig.addPostBuildTask(appIconToBundle);

build.initialize(gulp);

You can find the full solution here: https://github.com/thechriskent/spfx-AppIcon

SharePoint Framework App Icon

Applies To: SharePoint Framework

Recently, I showed you how to set your WebPart Icon (the image used in the authoring canvas toolbox). But there is another image, called the App Icon that is also important to set. This applies to both client-side webparts and SPFx extension apps.

The concept of an AppIcon is nothing new. When creating an Add-In, you could simply specify the icon property of the AppManifest (although it wasn’t actually stored in this file) and the appropriate rels and feature manifest files would be added to your app package. This is the image that was then used in the Site Contents view of your site.

The SharePoint Framework (SPFx) supports this same type of property. Although, as we’ll see, it’s of limited use on modern pages. Regardless, it is still a nice way to add a professional touch to your app.

Classic Site Contents
Look at that sweet cactus! Professionalism!!

Specifying an SPFx app App Icon

Create a suitable image file. App Icons must be 96×96 px. It’s also good practice to make them PNG files with only a white image on a transparent background. This allows them to match the native app icons and to maintain consistency with a site’s theme.

Super Easy Icon Generation

Here’s a terrible way to generate one using PowerPoint (everyone’s favorite image editor). Use the new Icons button on the Insert tab in the ribbon and choose one of the many beautiful icons:

PowerPoint Icons

Set the Graphics Fill color to white and set the size to 2.6″ (at least that’s the size that seems to equate to 96×96 px). Then right-click on the image object and choose Save as Picture…:

PowerPoint Save

Navigate to your SPFx solution folder. In the root folder, select the sharepoint folder (if it doesn’t exist, add it). Then save the image somewhere in there as a PNG file. I created a folder called Images and saved it in there:

Icon in VSCode

Configuring SPFx to Use Your AppIcon

In your SPFx solution, open the config/package-solution.json file. Under solution, add the iconPath property and give it a relative path within your sharepoint folder:

iconPath Property

From your console in your project directory, run gulp bundle. When that finishes, run gulp package-solution.

The bundle task has nothing to do with what we’re doing except to point out that the image does not get copied to your dist folder along with your other assets (this will be important later).

Rather, if you look in your sharepoint/solution folder you’ll find your sppkg file. In the accompanying debug folder you can see the contents of your sppkg file and you’ll see your AppIcon and all the rels and feature manifest files were added as expected.

Navigate to your app catalog and upload your sppkg file into the Apps for SharePoint library. Click the Deploy button when prompted.

You’ll find that when you add your app to a site that the tiny icon shown on the Modern version of the Site Contents page does not use your fancy new icon. Clicking for details of the app shows the generic app icon as well. However, switching to classic view for the site contents page does show your icon. Success!?

Providing an App Catalog Icon

Just like with Add-Ins, you’ll also need to provide a separate file for your app catalog icon. This can be done by copying your icon file to the same CDN location (or any URL really) where your bundle assets are going to go.

You then edit the properties of your app in the app catalog and specify that URL as the Icon URL property:

AppCatalog Icon

Now your custom icon will be used on the App Details page as well as within classic views:

App Profile

Conclusion

For modern pages, the SPFx iconPath (AppIcon) property has little to no effect. However, while the option to switch to Classic View still exists and especially while there is no easy option to switch back to the modern view (delete the cookie), providing both the app catalog icon and the AppIcon within your app is the best way to ensure your SPFx app looks professional and consistent to end users.