SPFx – Inquisitive M365 https://thomasdaly.net Yet another SharePoint / Office 365 blog Tue, 19 Sep 2023 16:49:30 +0000 en-US hourly 1 116451836 Adding Font Awesome to SPFx React project https://thomasdaly.net/2022/01/20/adding-font-awesome-to-spfx-react-project/ https://thomasdaly.net/2022/01/20/adding-font-awesome-to-spfx-react-project/#comments Thu, 20 Jan 2022 18:39:00 +0000 https://thomasdaly.net/?p=2688 Introduction

I love the FontAwesome library and use it on many projects. I was a huge fan of FontAwesome 4.0 and had a little struggle moving over to using 5.0 + 6.0 in my projects. I was use to the old way of including the link to the CSS files and using the class names. You can still do that if you wish but this article shows using the React component approach. It’s simple once you’ve done it a few times.

Install React Font Awesome

npm i –save @fortawesome/react-fontawesome

Install the Core Packages [required]

npm i --save @fortawesome/fontawesome-svg-core

Install the Icon Packages, use only the ones you need. If you only use solid icons then choose that. If you are not sure right now then you can include them all and remove them later.

npm i --save @fortawesome/free-solid-svg-icons
npm i --save @fortawesome/free-regular-svg-icons
npm i --save @fortawesome/free-brands-svg-icons

Go Find a Fonthttps://fontawesome.com/icons

Keep in mind that you only have the free library so you can only use those unless you buy the pro license

If you pick an icon and it says ‘Start Using this Pro Icon’ then you need to choose something else or purchase a license

You can filter the free ones by clicking this on the left.

I’ve selected the fish icon, next you need to take note of the type ‘Solid’ or ‘Regular’

Now import the FontAwesomeIcon component and the icon you want from the correct package

This fish icon is in the Solid package.

If I wanted to use something from the regular package like this icon

This font is in ‘Regular’ package you would import it from that package instead

There is intellisense inside visual studio to help get the correct name of the icon

Now the result

If you wanted to use the brand icons you would follow the same approach. I believe that all the brands icons are free.

I love FontAwesome library of icons and use them in many projects. The PRO version unlocks so many more fonts and customizations that it’s worth buying if you plan to use them and want to support this project.

]]>
https://thomasdaly.net/2022/01/20/adding-font-awesome-to-spfx-react-project/feed/ 2 2688
Alerts Header – SPFx project https://thomasdaly.net/2021/10/10/alerts-header-spfx-project/ https://thomasdaly.net/2021/10/10/alerts-header-spfx-project/#comments Sun, 10 Oct 2021 21:24:00 +0000 https://thomasdaly.net/?p=2660

Introduction

This new project is a nice little feature that you can easily add to your SharePoint site to enable site messages or alerts. Having the ability to display site alerts is one of the top 10 most requested features to enhance SharePoint. This customization is built as an SPFx application customizer, with a little bit of UI hacking. This project can be used as-is or as a starting point for you to extend and create your own combinations of notifications, icons and colors.

  • Git Repo
  • Pre-Build SPPKG
  • Overview

    The Alerts Header solution contains an SPFx application customizer and a list schema for the alerts messages. The list will automatically be created for you when the app is added to your site. Currently the app displays messages from the site it’s in but could be easily modified to pull notifications from another site. Initially this was designed to be deployed the the entire intranet and activated on each site. Each site had the option to display ‘local’ alerts, alerts contained within the site and remote alerts, alerts contained at the root site collection “/”. The remote alert functionality has been commented out for simplicity in the Git Hub repo but could be reenabled with just a little effort.

    Implementation

    The List

    The solution installs an ‘Alerts’ list in your site. This list contains a number of required fields to properly display the alerts.

    Title – The first part of the text that is displayed

    Description – The second part of the text that is displayed. The description will fill the reminder of the space and truncate with ‘…’

    Alert Type – The icon that is displayed. Warning, Actionable or Info as show below from top to bottom.

    Link – The navigation url and the text that will be displayed

    Start Date / Time – The date and time that the alert will be displayed

    End Date / Time – The date and time that the alert will no longer be displayed

    Items will immediately start to show up once they are added to the list and hit the duration between the start and end date.

    The Web Part

    This view below shows the alerts application customizer with all three types of alerts and active.

    Icons – The icons used are from the Font Awesome 5.0 free library. You could easily update the code to change out the icons.

    Expand / Collapse – If the alert has a long description it will be truncated once it fills the available screen space. The up / down arrow will allow the user to expand and collapse the alert to read the full description.

    Dismiss / Close – The alert has and close icon at the far right to dismiss the alert. The alert will no longer show during that session. A new session will display the alert again. New Tabs are equivalent to new sessions.

    SharePoint UI Hack – I hinted earlier that I’ve included a bonus UI Hack for free, haha. Take a peek at the picture below. The normal location of an application customizer is at the very top of the page. I did not want the alerts to show up there so I opted to move them to the bottom of the header. This is a non supported customization according to Microsoft. I’m pointing this out for transparency.

    Responsive / Mobile Friendly – The web part was designed with the intention of working across all devices and screen sizes. The functionality is somewhat different on the mobile. Once the item is expanded it can only be closed. I’ve do it this way to intentionally close them out to regain the screen real estate back on the small devices.

    Customizations

    The web part comes with a few notification types. You can add more types directly to the list or to the list schema. After you add new Alert Types you would then need to do a few more updates

    IAlerts.types.ts – Add your new Alert Types to the enumeration

    AltertItem.tsx – Add new conditions for the new Alert Types and the icon you want to use

    AlertItem.module.scss – Copy / Paste an existing Alert Type combination and change the colors to meet your needs.

    Conclusion

    Site Alerts are one of the top 10 features requested from clients and customers to build. I’m happy to be able to post this feature to share even though it’s not 100% Microsoft compliant. The tiny UI hack positions this feature in the location where I felt it belongs. I hope and wish that Microsoft would give us more Zones in which we could target in the application customizers.

    ]]>
    https://thomasdaly.net/2021/10/10/alerts-header-spfx-project/feed/ 15 2660
    SharePoint – SPFX Back to Top https://thomasdaly.net/2021/08/14/sharepoint-spfx-back-to-top/ https://thomasdaly.net/2021/08/14/sharepoint-spfx-back-to-top/#comments Sat, 14 Aug 2021 04:35:03 +0000 https://thomasdaly.net/?p=2605 Introduction

    This post will demonstrate another SharePoint UI hack that is typically seen on many modern sites today. Back to top is a button that will position itself on the right hand bottom corner of the screen once you scroll a bit down. It’s very useful on long pages where your users might want to scroll up quickly and not get mouse scroll rash on their fingers in the process. This is another client request that I hear infrequently but I found this as something that the general population can implement and learn from.

    Before you continue, I must mention that SharePoint UI hacks are unsupported and frowned upon by Microsoft as we cannot rely on the UI [html, classes and DOM] to remain the same over time. With that said you are free to implement and learn more about SPFX and the current DOM given this example. When Microsoft implements a change this would potentially stop working and that is all. Post an issue on github and I would be happy to review and make a change.

    Overview

    I’m not going to go into the nitty gritty details of the code in this article, you can look at the code for that. This customization is based on an SPFX application customizer. This application customizer injects a button when the users scrolls past a certain point. The button would then jump the user back to the top of the scroll region.

    The scroll region might change at some point for SharePoint, rendering this customization ineffective.

    Implementation

    In the SPFX application customizer there is some code to detect the scroll event. There is also some code to detect the page navigation event as it’s somewhat tricky. When users navigate away from a page application customizers are not re-rendered. There is also some code to detect a navigation event and refresh the scroll area so that this SharePoint UI hack continues to work.

    The current scroll region is defined in the BackToTop.tsx. The current scroll region that is the target can be seen below.

    The customization is also theme aware and will respect the primary color of the theme.

    Installation

    As with many github solution you can always clone the repo and make any changes you desire to the solution, check out the REPO. Alternatively you can take the latest build and install it into your app catalog.

    Once you install the solution into your app catalog it will be available as an App on your site. It’s as simple as that.

    Summary

    I hope you enjoy this, yet another SharePoint UI Hack. It’s a great little enhancement if you have long content and want to scroll back up to the top of the page quickly. Feel free to use the github code to inspect how it’s implemented, make changes or enhancements or just implement it as the package is available for you. I’ll mention again that SharePoint UI hacks are not supported by Microsoft so don’t ask them for support. If you implement this you should always remove it before troubleshooting other issues. Generally it’s safe to apply as it won’t adjust or change anything permanently within your site, but take note that if there is a change with the SharePoint UI it could stop working as some point in the future. Please feel free to reach out or comment if your feedback or experience.

    ]]>
    https://thomasdaly.net/2021/08/14/sharepoint-spfx-back-to-top/feed/ 6 2605
    Using JSOM to update Taxonomy Fields in SPFx https://thomasdaly.net/2020/10/29/using-jsom-to-update-taxonomy-fields-in-spfx/ https://thomasdaly.net/2020/10/29/using-jsom-to-update-taxonomy-fields-in-spfx/#respond Thu, 29 Oct 2020 08:00:37 +0000 https://thomasdaly.net/?p=2119 Introduction

    Last week I was working on a project that involved setting some managed meta data fields, both the single value and multi values fields. I typically use PnP-JS to streamline the calls to SharePoint however I found there are issues with PNP-Js and managed meta data fields. It’s been said that MMD fields are unable to be set with REST calls (PnP-Js uses REST). There are mixed reviews out there regarding whether it works or not. For me it did not work. My particular scenario involved update 6 single value fields and 1 multi value fields at the same time. While investigating I reach out to a friend Beau Cameron to verify my code as he wrote 2 great articles for single value fields and multi value fields. The results were to flaky using REST so the decision was made to use JSOM. In this article I will share the end code as there were a few things I want to document for future reference.

    Using JSOM in SPFx

    Before you can start writing the code you must first get your project setup in order to work with the JSOM libraries.

    It’s a good idea to read up on the official guidance as this method follows it but I prefer to load the libraries differently. The Microsoft article loads the libraries in the configuration as external resource but you must specific the full url of your site and I prefer not to lock that value of the site url into a configuration file that would prevent me from using this in another tenant without rebuilding.

    Install the Typings

    install project dependencies

    npm install @types/microsoft-ajax @types/sharepoint --save-dev
    

    In the code editor, open the ./tsconfig.json file, and in the types property, after the webpack-env entry, add references to microsoft-ajax and sharepoint

    {
      "compilerOptions": {
        // ...
        "types": [
          "es6-promise",
          "es6-collections",
          "webpack-env",
          "microsoft-ajax",
          "sharepoint"
        ]
      }
    }

    Register the Scripts

    In the file you wish to use the JSOM library, require the scripts.

    require('sp-init'); 
    require('microsoft-ajax'); 
    require('sp-runtime'); 
    require('sharepoint');

    Loading the JSOM libraries

    In the previous section I mentioned that I prefer to dynamically load the libraries to avoid hard coding the site url into the configuration files. In the code below is how I am loading the libraries dynamically.

    Note: In the state I have 2 values

    1. loadingScripts: boolean, intially set to true
    2. Errors: any, initially set to []

    This way you can wait until the scripts are loading to rendering fields that require them and I’m also collecting the errors for later consumption.

    public componentDidMount() {
        this.loadScripts();
      }
    
    
      public getSiteCollectionUrl(): string {
        let baseUrl = window.location.protocol + "//" + window.location.host;
        const pathname = window.location.pathname;
        const siteCollectionDetector = "/sites/";
        if (pathname.indexOf(siteCollectionDetector) >= 0) {
          baseUrl += pathname.substring(
            0,
            pathname.indexOf("/", siteCollectionDetector.length)
          );
        }
        return baseUrl;
      }
    
      private loadScripts() {
        const siteColUrl = this.getSiteCollectionUrl();
        try {
          SPComponentLoader.loadScript(siteColUrl + "/_layouts/15/init.js", {
            globalExportsName: "$_global_init",
          })
            .then(
              (): Promise<{}> => {
                return SPComponentLoader.loadScript(
                  siteColUrl + "/_layouts/15/MicrosoftAjax.js",
                  {
                    globalExportsName: "Sys",
                  }
                );
              }
            )
            .then(
              (): Promise<{}> => {
                return SPComponentLoader.loadScript(
                  siteColUrl + "/_layouts/15/SP.Runtime.js",
                  {
                    globalExportsName: "SP",
                  }
                );
              }
            )
            .then(
              (): Promise<{}> => {
                return SPComponentLoader.loadScript(
                  siteColUrl + "/_layouts/15/SP.js",
                  {
                    globalExportsName: "SP",
                  }
                );
              }
            )
            .then(
              (): Promise<{}> => {
                return SPComponentLoader.loadScript(
                  siteColUrl + "/_layouts/15/SP.taxonomy.js",
                  {
                    globalExportsName: "SP",
                  }
                );
              }
            )
            .then((): void => {
              this.setState({ loadingScripts: false });
            })
            .catch((reason: any) => {
              this.setState({
                loadingScripts: false,
                errors: [...this.state.errors, reason],
              });
            });
        } catch (error) {
          this.setState({
            loadingScripts: false,
            errors: [...this.state.errors, error],
          });
        }
      }
    

    State values & Taxonomy Controls

    To work with the taxonomy fields I will be using the Taxonomy Picker from the PnP React Controls

                  <TaxonomyPicker
                    allowMultipleSelections={false}
                    termsetNameOrID="By Whom"
                    label="By Whom"
                    values={this.state.byWhom}
                    context={this.props.context}
                    onValueChanged={(pickerTerms) => {
                      this.setState({ byWhom: pickerTerms });
                    }}
                    loading={this.state.loading}
                  />

    Setting up the JSOM call

    This function I I am preparing the JSOM context getting the list and item and will be updating a number of MMD fields. My state values for the MMD are IPickerTerms from PnP React Controls.

    Warning: You can see I have a function called getItem that will retrieve the list item using CAML query. I must do this in order to get a ‘fresh’ version of the item. If you use the JSOM getItemById you will receive a cached copy and performing an update on it will throw an error that it is unable to make a change.  I also read that you can avoid that error by calling executeQueryAsync right after getItemById so it’s ‘fresh’ but I went this way.

    private updateItemMetaData = async (siteUrl: string, listName: string, itemId: number) => {
        console.log(siteUrl, listName, itemId);
        return new Promise(async (resolve, reject) => {
          let context = new SP.ClientContext(siteUrl);
          let list = context.get_web().get_lists().getByTitle(listName);
          console.log("ctx list", list);
          let item = await this.getItem(context, list, itemId); 
          console.log("ctx item", item);
          this.setTaxFieldProperty(context, item, list, "ByWhom", this.state.byWhom, true);
          this.setTaxFieldProperty(context, item, list, "SubCategory", this.state.subCategory, true);
          this.setTaxFieldProperty(context, item, list, "DocumentStatus", this.state.documentStatus, true);
          this.setTaxFieldProperty(context, item, list, "SubmittalAction", this.state.submittalAction, true);
          this.setTaxFieldProperty(context, item, list, "Phase", this.state.gaPhase, true);
          this.setTaxFieldProperty(context, item, list, "Agencies", this.state.agencies, true);
          this.setTaxFieldProperty(context, item, list, "Trades", this.state.trades, false);
          item.set_item("ActionDate", this.state.actionDate);
          item.update();
          context.load(item);
          context.executeQueryAsync(
            () => {
              console.log("success", item.get_id());
              resolve();
            },
            (sender, args) => {
              console.log("fail", args.get_message());
              resolve();
            }
          );
        });
      };
    
     private getItem = async (context: SP.ClientContext, list: SP.List, itemId: number): Promise<SP.ListItem> => {
        return new Promise((resolve, reject) => {
          const query = `<View Scope='Recursive'><Query><Where><Eq><FieldRef Name=\"ID\" /><Value Type=\"Integer\">${itemId}</Value></Eq></Where></Query></View>`;
          let camlQuery = new SP.CamlQuery();
          camlQuery.set_viewXml(query);
          let allItems = list.getItems(camlQuery);
          context.load(allItems, 'Include(Id)');
          context.executeQueryAsync(
            () => {
              console.log("success", allItems);
              resolve(allItems.get_item(0));
            },
            (sender, args) => {
              console.log("fail", args.get_message());
              resolve();
            }
          );
        });
      }
    

    The Update Function

    In this section we will get the field from the list and update it correctly if it’s a single value or a multi value. Each type must be handled differently. For single value we can set the term as an object but for the multi value we must create a string of the terms. Creating the string of terms is done in the getTermString function.

    private setTaxFieldProperty = (
        context: SP.ClientContext,
        item: SP.ListItem,
        list: SP.List,
        fieldName: string,
        fieldValues: IPickerTerms,
        isSingle: boolean
      ) => {
        let field = list.get_fields().getByInternalNameOrTitle(fieldName);
        let taxField = context.castTo(field, SP.Taxonomy.TaxonomyField) as SP.Taxonomy.TaxonomyField;
        if (isSingle) {
          if (fieldValues && fieldValues.length >= 1) {
            let termValue = new SP.Taxonomy.TaxonomyFieldValue();
            termValue.set_label(fieldValues[0].name);
            termValue.set_termGuid(new SP.Guid(fieldValues[0].key));
            termValue.set_wssId(-1);
            taxField.setFieldValueByValue(item, termValue);
          }
          else {
            taxField.validateSetValue(item, null);
          }
        }
        else {
          if(fieldValues && fieldValues.length >= 1) {
            let termString = this.getTermString(fieldValues);
            console.log(termString);
            let termValueCollection = new SP.Taxonomy.TaxonomyFieldValueCollection(context, termString, taxField);
            taxField.setFieldValueByValueCollection(item, termValueCollection);
          }
          else {
            taxField.validateSetValue(item, null);
          }
        }
      };
    
      private getTermString(pickerTerms) {
        let termString = "";
        pickerTerms.forEach((term) => {
          termString += `-1;#${term["name"]}|${term["key"]};#`;
        });
        return termString.slice(0, -2);
      }

    Summary

    This proof of concept was somewhat challenging to work with the taxonomy fields. So much so that I had to blog it at least for my reference on how I dealt with it. The code above is not meant to be a full solution just a snippet of the key pieces to reliable working with the taxonomy fields. If you are interested in the full look at the code you can check it out here, https://github.com/tom-daly/demos/tree/master/docprocessor. It goes without saying that it’s test/demo code.

    ]]>
    https://thomasdaly.net/2020/10/29/using-jsom-to-update-taxonomy-fields-in-spfx/feed/ 0 2119
    Making SPFx Web Parts Searchable – Lessons Learned https://thomasdaly.net/2020/06/19/making-spfx-web-parts-searchable-lessons-learned/ https://thomasdaly.net/2020/06/19/making-spfx-web-parts-searchable-lessons-learned/#comments Fri, 19 Jun 2020 08:00:00 +0000 https://thomasdaly.net/?p=1892 I recently built a handful of amazing content entry web parts for Office 365 using SharePoint Framework (SPFx). The client I was working with wanted various combinations of responsive columns, rich text editor, links, and pictures. This ultimately led to building out a few custom web parts to meet their needs.

    About a months later, as we started focusing on search, I noticed a huge problem. The contents of my web parts were not searchable! We couldn’t find anything. This was the first time I had to ensure the content was indexed on a modern page. It’s not the same as classic – lesson learned.

    Step 1 – Mark Your SPFx Web Part Properties

    I came across the official Microsoft guidance which tells you to mark your properties. I will not go into details of the article, you can read the full article here.

    As you can clearly see you need to mark the web parts as image, html, string, or link. If you have a combination of those then use html.

    Microsoft’s Example

    Yet another issue. What about web parts that have arrays, objects, arrays of objects or props that leverage lists as a data source?

    How to handle an arrays / objects / both … simplify

    Here is an example from one web part. I have a web part property which is an Array of Objects, that have multiple properties. According to the guidance, how do you mark it?

    There is no immediate way to tag this properties where it would get indexed correctly and make the content searchable. So how do you solve this? The list below are the main points.

    1. Create a new property ‘searchableText’
    2. Mark the new property ‘isHtmlString’
    3. Create an update function

    Step 1 and 2 are very straightforward. These are used inside the main .ts file of the web part. It will not be passed into the child components.

    Create New Property
    Mark New Property

    The 3rd step is to pass a function to child component. When the items are changed inside the web part, it will loop over the items and create an html string of the text & links to update searchableText. Once that is complete we will be able to search on the content of the web part properly.

    The purpose of this example is give you an idea of converting your objects / arrays into a String. It’s a little extra work but making it worthwhile when users can search the contents of your web parts. I have yet to figure out a way to make list based web part content searchable on the page. [I do not want to hit the list items directly in many cases] That might be a blog for another time.

    ]]>
    https://thomasdaly.net/2020/06/19/making-spfx-web-parts-searchable-lessons-learned/feed/ 6 1892
    SPFx Side Navigation Project for Modern Sites https://thomasdaly.net/2020/06/09/spfx-side-navigation-project-for-modern-sites/ https://thomasdaly.net/2020/06/09/spfx-side-navigation-project-for-modern-sites/#comments Tue, 09 Jun 2020 17:53:09 +0000 https://thomasdaly.net/?p=1870 I started this project over a year and a half ago and at the time Modern SharePoint starts gaining some traction with organizations. I remember SPFx v1.4 was being released at the time and how far we’ve come from that today. I still find this project very interesting and useful today. It’s something different and on communication, sites can give you an option for navigation. Without further delay, I present to you the Side Navigation Project [inspired by the Office 365 Admin Tenant Navigation].

    Currently Supported [O365/SP2019 – IE11, Chrome, FF]

    The Side Navigation Project with full documentation can be found in my github repo https://github.com/tom-daly/spfx-side-navigation . That’s where you will find the installation guide and documentation. Please post any issues there – I’ll do my best to help if you are having trouble. This blog’s purpose to give a bit of background on the project and help showcase it.

    About the Project

    The Side Navigation purpose is to add a left navigation on Modern Communication site template that is unobtrusive to the main UI. It’s normally slim and compact but can be easily expanded to see what the icons nodes are.

    What makes it very cool is the fact that you can incorporate ANY svg icon. The only requirement is that it’s a single color icon – we use the SVG code in the list item and this allows us to recolor it along with the navigation nodes!

    Installation

    On installation and activation within your site it will create a list called Side Nav List. This list contains all the information needed to sustain the navigation. You can even create sub nodes b/c the list contains a lookup to itself. [this could be extended to infinite levels however the css does not support that currently / the code does]

    The Side Navigation is an SPFx application customizer that lives in the header area (if you are familiar with the placeholders) but it styles itself to the left of the page and pushed the main content over 50px. It fits nicely under the waffle or app launcher.

    Customization

    Colors

    You might be looking at it and say those colors aren’t going to fit site theme. We’ll you can download the solutions and QUICKLY change the colors of the site-menu.css file.

    Icons

    You can use any monochrome SVG you want as long as you can access the source [just open it in notepad]. SVG’s are freely available for a few dollars. My favorite sites for SVGs

    Navigation (advanced)

    Lastly the data source. Currently a list is deployed to the site on installation of the app. However you can make this global if desired. You can check out my global navigation project. You can modify the navigation provider to point to another source and when you deploy this out to various sites it will connect back to that source list.

    Check out the global navigation project and the GlobalNavProvider code – here … and you can see how i feel that provider a url. The current project also has a SideNavProvider which is set to look for the list in the current site but with a little develop know how you can change that !

    Why Navigation?

    If you haven’t seen my github repo’s before I have specialized in UI for all versions of SharePoint and I have many navigation projects going back to 2010/2013. If you like this project you’ll also like the Global Navigation Solution. It’s literally the same code! 😉 just styled a bit differently. The global navigation has some other enhancements as people have helped by submitting issues and requests. You can check that out here https://github.com/tom-daly/spfx-global-navigation

    ]]>
    https://thomasdaly.net/2020/06/09/spfx-side-navigation-project-for-modern-sites/feed/ 23 1870
    SPFx Application Customizer that works on Classic SharePoint Sites https://thomasdaly.net/2019/08/28/spfx-application-customizer-that-works-on-classic-sharepoint-sites/ https://thomasdaly.net/2019/08/28/spfx-application-customizer-that-works-on-classic-sharepoint-sites/#comments Wed, 28 Aug 2019 13:48:41 +0000 https://thomasdaly.net/?p=1685 Introduction

    Over a year ago I create a cross site collection navigation solution aka global navigation for Modern SharePoint sites using all the Microsoft goodies like SPFx, PowerShell PnP, PnP-JS & Office UI Fabric. At the same time, I had a very similar solution for Classic SharePoint sites for a different batch of clients. It was fairly typical for clients new to Office 365 go straight Modern and some of the more traditional SharePoint clients to stick with Classic sites.

    One day I was asked to put the global navigation on the Classic site and the Modern sites. It just didn’t feel right to me having 2 separate solutions. One being built with jQuery and JavaScript while the other built in SPFx using TypeScript, SCSS & React. The SPFx being the more current way of developing client-side solutions has spoiled me. I don’t dislike jQuery / JavaScript solutions… I just prefer the tooling SPFx offers for us SharePoint developers.

    New call-to-action

    How can an SPFx Application Customizer work on Classic sites?

    If you break it down, part of the secret sauce behind SPFx is that it’s simple a wrapper around plain old JavaScript that tells SharePoint where your file is and load it. SPFx provides other greater capabilities, like property pane which passes parameters to your JavaScript or React components.

    For Modern sites when you create a web part or app customizer there is a top level file. For example: in the global navigation projectSpfxGlobalNavigationApplicationCustomizer.ts. This file registers the Application Customizer and then loads the GlobalNav component into the Header placeholder [available on all Modern pages]. Everything inside the GlobalNav.tsx is React and nothing special.

    For Classic sites there is no built-in framework to inject components. This is something that must be addressed. In the global navigation project I create a component called ClassicMode.ts which is the top level file.

    Inside that file there is a reference to include @bable/polyfill. That helps supplement any browser shortcomings. On a Modern site there are already some polyfills so this would be unnecessary.

    Next there is a secondary style sheet which would be used to handle style differences between Classic & Modern sites. In the global navigation project, I am reference the Office UI Fabric library for icons. On a Modern site those are also already there so this would be unnecessary.

    Finally, on window load, the GlobalNav component is created into the element #DeltaTopNavigation. The DeltaTopNavgiation is the element where the default navigation is on a Classic SharePoint site.

    ** For this navigation scenario I am overwriting the default out of the box navigation. You can create a new DOM element and inject that anywhere and mount your React component into that new element. **

    That’s it in terms of the code. The code of the GlobalNav component is the same for both Modern & Classic sites. The Modern Application Customizer points to the same files that the ClassicMode component points to.

    Building an SPFx Application Customizer for Classic Sites

    Going back to what I said earlier, SPFx is simply a wrapper around plain old JavaScript. SPFx allows you to write in TypeScript/React/SCSS but browsers only understand HTML/CSS/JS, not those other languages. All of the SPFx code gets compiled into a single JavaScript file.

    You can’t just extract the JavaScript file from the .sppkg package and pop it into the Classic site.. that’s not going to work. The next step is to generate a new JavaScript that uses our new ClassicMode.ts file as an entry point. The key to this is Webpack.

    In the SPFx Application Customizer folder create a webpack.config.js. This will tell webpack to use our ClassicModer.ts as the entry point and produce a new file called top-navigation.js in a folder called classic-dist

    To execute webpack against this new file just run the following command:
    npx webpack --config webpack.config.js

    Note: This webpack file uses a few new dependencies that are NOT included with an SPFx project. You must include these new dependencies before you attempt to run webpack against this new config file.

    Attaching Classic Mode build to SharePoint Site

    The last thing we need to cover is how to hook up this file into your Classic SharePoint site. It’s straight forward and if you made it this far it should be a piece of cake.

    Step 1 – Upload the file. You can manually copy / paste the file into SharePoint. Alternatively you could host this file on a CDN if the plan is to use it on more than 1 site collection.

    Step 2 – Connect the JavaScript file to your site collection. You can’t do this through the UI unless you use a tool like SP-Editor for Chrome to attach a site script link

    Alternatively, check out how I upload it and activate it using PowerShell PnP with a deploy script.

    Classic Mode Deploy Script

    Conclusion

    That wraps up how to use your SPFx Application Customizer on a Classic SharePoint site. The most complicated part for me was building out the webpack.config.js file. It was a little tricky finding the right polyfills, getting the loading to work and testing across versions of SharePoint. I have even used this approach, for this project SPFx Global Navigation Application Customizer, on a SharePoint 2013 site. Interested in that solution check out my other article Cross Site Collection Navigation for Modern & Classic SharePoint Sites. Enjoy!

    ]]>
    https://thomasdaly.net/2019/08/28/spfx-application-customizer-that-works-on-classic-sharepoint-sites/feed/ 4 1685
    Cross Site Collection Navigation for Modern & Classic SharePoint Sites https://thomasdaly.net/2019/08/28/cross-site-collection-navigation-for-modern-classic-sharepoint-sites/ https://thomasdaly.net/2019/08/28/cross-site-collection-navigation-for-modern-classic-sharepoint-sites/#comments Wed, 28 Aug 2019 13:47:43 +0000 https://thomasdaly.net/?p=1672 TL;DR Go here for the code + instructions: https://github.com/tom-daly/spfx-global-navigation
    Modern version works with: Office 365 & SharePoint 2019 – IE 11, Chrome, Firefox.
    Classic version works on any SharePoint [yes any] – IE 11, Chrome, Firefox

    Introduction

    Maintaining a consistent navigation across all site collections has always been a problem for SharePoint. Site collections do not share navigation. The story goes back to the beginning of SharePoint’s existence. In earlier versions 2007, 2010… it was recommended to break up sites into site collection to keep the content database sizes small. Microsoft had recommended maximums for best performance. Ultimately the content of SharePoint are table in a SQL databases and small databases meant faster, more manageable backups. Alternatively you could have all your sites in one site collection and then all the out of the box navigation options would be perfect.

    Enter Modern… Each modern site is a site collection. This approach creates a flat hierarchy of sites. Each site collection having its own navigation, security, look and feel, content, components and more.

    How can you create a consistent navigation across modern pages? Microsoft’s answer is to use Hub sites. “Hubbing” your sites will add a hub site navigation element to the top of the page.

    Great! Hub sites to the rescue… What if you have some classic sites? Most organizations are currently in a hybrid mode of classic and modern pages. You can’t hub classic sites.

    This blog will cover the solution that I built to add a list based navigation to modern pages using SPFx application customizer. But wait there’s more… It will also show you how you can use the same code base and repackage it to work on Classic SharePoint sites as well. Essentially creating the perfect cross site collection navigation solution that works on both classic and modern.

    Solution Overview

    The solution is broken down into a couple of parts. This blog is meant to explain the approach and the github repository contains an in depth guide to install and provision.

    1. Navigation List – PnP Powershell Template + Provisioning scripts
    2. SPFx application customizer – Global Navigation – React Component

    Step 1 – Provision the Global Navigation List

    I’m all about repeatable provisioning – as a developer I almost never create anything manually. In this solution I use Powershell PnP, mainly the provisioning template to provision the site columns, content types and list schema and connecting a lookup like with a cmdlet.

    Using the list based approach, I can centrally host the list in the root site of the tenant where my users generally have read access. In the case that you have secured the root site from users, you could break permissions on the Global Nav List and grant read only access.

    The list based approach has benefits that I like:

    1. It’s easy to talk to the list and using a simple rest query or PnP-JS as I do in this solution.
    2. Lists can be security trimmed so you can hide nodes from certain groups by breaking inheritance on those items. For those items they would have item level permissions.
    3. Lists are easy to work with for the end users, they don’t need lengthy instructions on how to manage the navigation

    The entire list is easily deployable via the deploy.ps1 in the provisioning folder of the project. Check that out for an example of a template and the standard script I use to provision assets via Powershell PnP

    Step 2 – SPFx Application Customizer

    As of now the only way to install the solution is to build it. Clone or fork the repo and you’ll need to have a SPFx development environment in order to complete the build. Check out the github for the full instructions. Once you have the code, the build and deployment is the standard process for SPFx apps.

    Future versions I might include a package that you download, let me know if that’s something you might want. Most people will want to at least customize the colors and best way is to get the code and do it yourself.

    The SPFx Application Customizer lives inside the Header placeholder on any modern site that you add the app to. The code has been written such that it makes a call to the Global Nav List on the “/” root site of the tenant. You could change that location in the code and rebuild if you desired by modifying the GlobalNavProvider.tsx

    Global Navigation

    Once you’ve installed the list, add some nodes, deploy the app customizer, activate the app on a site you should get something like this below. The navigation can support as many levels as you want.

    Classic Build

    Yes it works on Classic SharePoint as well. By far the coolest part of this whole project. How can that be? For the complete explanation check out this article: SPFx Application Customizer that works on Classic SharePoint Sites.

    Classic site deployment process is different. It’s not an app / app package. The solution creates a separate JavaScript file that is uploaded to your site and linked via a site script link.

    To generate the latest JavaScript run the ‘build.cmd‘. That will generate both a new .sppkg for Modern sites and the .js file for Classic sites. After a successful build, the top-navigation.js file will be in the ‘classic-dist‘ folder.

    If you want an automated deployment approach you would also want to run the deploy.ps1 file. The deploy script will upload the file and attach the JavaScript to your site collection. If deploying to multiple sites it’s a good ideal to centrally host this file in a CDN.

    Once the file is attached via a script link, it’s live. Your site should look like this.

    Conclusion

    There you have it, a global navigation solution built for Modern sites using SPFx, Powershell PnP, PnP-JS, and Office UI Fabric that also works on Classic sites. One code base to maintain for both versions of SharePoint. This provides a flexible, consistent navigation for customers that have a mix of Classic & Modern sites.

    ]]>
    https://thomasdaly.net/2019/08/28/cross-site-collection-navigation-for-modern-classic-sharepoint-sites/feed/ 27 1672
    Suppress SPFx warnings w/ Regex https://thomasdaly.net/2019/07/15/suppress-spfx-warnings-w-regex/ https://thomasdaly.net/2019/07/15/suppress-spfx-warnings-w-regex/#comments Mon, 15 Jul 2019 18:48:43 +0000 https://thomasdaly.net/?p=1651 My goal is to write clean and readable code. The least I can do is make sure it’s formatted correctly. One of my favorite Visual Studio plugins is called Prettier. I use that plugin to make sure everything is formatted consistently throughout my code. My biggest issue is that it adds semicolons to every single function.

    But it doesn’t treat all functions equal.

    New call-to-action

    The SPFx tslint rules don’t like that.

    Prettier has the option to enable / disable semicolons at the end of every statement. Disabling that setting takes away all the semicolons from all lines. I like to have semicolons after each statement.

    You could disable the semi-colon check in SPFx project’s tslint.json file

    OR you could edit your gulpfile.js and add suppression to prevent the message.

    The build.AddSuppression will accept a string or a regex. You can see here I’ve added a regex value to suppress errors that end with ‘error semicolon: Unecessary semicolon”

    Either way works for me – no more semicolon warnings!

    ]]>
    https://thomasdaly.net/2019/07/15/suppress-spfx-warnings-w-regex/feed/ 1 1651
    Update: SPFx Automatically Generating Revision Numbers + Versioning https://thomasdaly.net/2018/08/21/update-spfx-automatically-generating-revision-numbers-versioning/ https://thomasdaly.net/2018/08/21/update-spfx-automatically-generating-revision-numbers-versioning/#comments Tue, 21 Aug 2018 04:21:23 +0000 https://thomasdaly.net/?p=1611 I’ve been using the auto generated build numbers paired with Stefan Bauer: How to version new SharePoint Framework projects and it’s been working great for myself. There is however, one minor issue. After running ‘npm version major/minor/patch’ it will also run the build/bundle that automatically increments the revision numbers. This is an unwanted side effect.

    Running ‘npm version major/minor/patch’ requires a clean tree, but the build/bundle modifies the package-solution.json file causing a problem. I’ve updated the build/bundle code to accept a parameter that will NOT automatically increment the revision number. Ultimately I’m fixing a problem that I caused.

    Let’s take a see an example.

    First thing that you need is an extra npm package called ‘gulp-util’

    npm install --save-dev gulp-util
    

    Here are the gulpfile.js enhancements again, specifically gutil & fs

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

    Next add the gulp task to bump the revision. You can see that this task is looking for an argument called ‘–no-revision’. If  ‘–no-revision’ is specified in the build or bundle command it will noop on the bumpRevision task.

    var getJson = function (file) {
      return JSON.parse(fs.readFileSync(file, 'utf8'));
    };
    
    let bumpRevisionSubTask = build.subTask('bump-revision-subtask', function(gulp, buildOptions, done) {
      var skipBumpRevision = buildOptions.args["revision"] === false;
      if(!skipBumpRevision) {
        var pkgSolution = getJson('./config/package-solution.json');
        var oldVersionNumber = String(pkgSolution.solution.version);
        gutil.log('Old Version: ' + oldVersionNumber);
        var oldBuildNumber = parseInt(oldVersionNumber.split('.')[3]);
        gutil.log('Old Build Number: ' + oldBuildNumber);
        var newBuildNumber = oldBuildNumber+1;
        gutil.log('New Build Number: ' + newBuildNumber);
        var newVersionNumber = oldVersionNumber.substring(0, String(oldVersionNumber).length - String(oldBuildNumber).length) + String(newBuildNumber);
        gutil.log('New Version: ' + newVersionNumber);
        pkgSolution.solution.version = newVersionNumber;
        fs.writeFile('./config/package-solution.json', JSON.stringify(pkgSolution, null, 4));
      }
      return gulp.src('./config/package-solution.json')
      .pipe(skipBumpRevision ? gutil.noop() : gulp.dest('./config'))
    });
    
    let bumpRevisionTask = build.task('bump-revision', bumpRevisionSubTask);

    Lastly register the bumpRevisionTask with the build process.

    build.rig.addPreBuildTask(bumpRevisionTask);
    

    Again … all this is above the following

    build.initialize(gulp);
    

    Now in order to skip the bumpRevisionTask you can call build/bundle like this:

    gulp build --ship --no-revision
    
    gulp bundle --ship --no-revision

    That’s it… add this and you will get auto incrementing revision number w/ the option to NOT increment the build number.

     

    Why is that so great? Because, if you pair this with Stefan Bauer’s npm versioning post it will run when you execute the ‘npm version major/minor/patch’ command causing a version like v1.0.3.1 … instead of v1.0.3.0

     

    I’ll leave you with an example of my build commands for new versions that I’m releasing to dev/staging/prod.

    build-patch.cmd

    cls
    
    call gulp clean
    
    call gulp build --ship --no-revision
    
    call gulp bundle --ship --no-revision
    
    call npm version patch
    
    call gulp package-solution --ship
    
    call explorer .\sharepoint\solution\
    

    build-minor.cmd

    cls
    
    call gulp clean
    
    call gulp build --ship --no-revision
    
    call gulp bundle --ship --no-revision
    
    call npm version minor
    
    call gulp package-solution --ship
    
    call explorer .\sharepoint\solution\
    

    build-major.cmd

    cls
    
    call gulp clean
    
    call gulp build --ship --no-revision
    
    call gulp bundle --ship --no-revision
    
    call npm version minor
    
    call gulp package-solution --ship
    
    call explorer .\sharepoint\solution\
    

    I recommend reading some of my other posts on this subject

    Original Post: SPFx Automatically Generating Revision Numbers

    Simple Build Script for the SharePoint Framework

    Stefan Bauer: How to version new SharePoint Framework projects

     

    Enjoy! let me know how this works for you and your development teams.

    ]]>
    https://thomasdaly.net/2018/08/21/update-spfx-automatically-generating-revision-numbers-versioning/feed/ 4 1611