Tag: JavaScript

An Outlook Add-in with SharePoint Framework (SPFx) – Store custom metadata with your mail

An Outlook Add-in with SharePoint Framework (SPFx) – Store custom metadata with your mail

Since SharePoint Framework version 1.10 you can also develop Office Add-Ins with SPFx starting with Outlook Web Access in public preview. The great benefit is you already have the prepared context for access of Microsoft Graph. In a demo scenario I showed you how to create a valuable Outlook Add-In with the capability to store complete mails to OneDrive, Office 365 Groups or Microsoft Teams.

Recently I presented that solution in the Microsoft SharePoint Framework bi-weekly community call.

During the call in the chat the idea was born to enhance this solution with some metadata. In fact to store with the mail so it is persisted by when this mail was saved where. Many thanks to Neelamugilan Gobalakrishnan and my colleague and MVP Wictor Wilen for your feedback on this.

Now I want to show you a simple solution for that. The existing project will only be enhanced by a simple operation to save an openExtension to the message. And when we open the Add-In we first try to get an existing openExtension for that mail and display as a reminder when and where we previously saved that mail in case that openExtension exists.

Now why an open extension you might think? Of course it depends if you use the (more simple) scenario of an open extension or the more complex but also more valuable schema extension scenario.

As per this great post from Mikael Svenson I have three arguments for the choice of open extensions:

  • I do not want to run a filter query on my messages based on the open extensions
  • I do not need my schema going public
  • I do not want that metadata schema being irreversible stored (you can only deprecate it for reasons once ‘Available’)

So lets start with the expected result. Once we open our existing add-in to store our mail somewhere we might want to have a hint like this that we already stored our mail previously and where:

Outlook add-in informs you about previous save operation

To achieve this we first need to store that information during our save process. In our controller we create another function that stores our metadata as an open extension to that mail. This works simply like that:

The function retrieves the id of the mail, a displayName, an url of the target location and finally a date. The displayName we construct from the target (OneDrive or Group/Team name) combined with the path we already displayed in our breadcrumb. That’s all for the metadata storage.

Now back to the start of our add-in where the requirement is to retrieve potentially existing metadata to remind the user in case the mail was already stored somewhere. In our base component we try to get the metadata once our graphController is established. As per the get an existing openExtension documentation you can either get the specific extension or expand the extension when getting a known instance, that is your mail. We will do the latter one as we do not know if the extension exists at all and in the former option a non existing extension would raise an error (which we could handle but that’s not the elegant way of course).

The graph request tries to retrieve the mail again only with id and subject but also with the expanded extension. In case there is metadata we return that as an object otherwise null. The result will be written to the components state and rendered in case it is not null.

Display in case metadata is filled
Display in case metadata is null

That’s all with my little enhancement using custom metadata for mails based on Microsoft Graph open extensions for my Outlook add-in.
You can check the full code repository here in my GitHub.

Markus is a SharePoint architect and technical consultant with focus on latest technology stack in Office 365 and SharePoint Online development. He loves the new SharePoint Framework as well as some backend stuff around Azure Automation or Azure Functions and also has a passion for Microsoft Graph.
He works for Avanade and is based in Munich.
Although if partially inspired by his daily work opinions are always personal.
An Outlook Add-in with SharePoint Framework (SPFx) – Retrieving the mail as Mime

An Outlook Add-in with SharePoint Framework (SPFx) – Retrieving the mail as Mime

Since SharePoint Framework version 1.10 you can also develop Office Add-Ins with SPFx starting with Outlook Web Access in public preview. The great benefit is you already have the prepared context for access of Microsoft Graph. In this demo scenario I want to show you how to create a valuable Outlook Add-In with the capability to store complete mails to OneDrive, Office 365 Groups or Microsoft Teams.

An Outlook Add-In to copy a whole mail to Teams, Groups or OneDrive

In this part you will see how to retrieve a mail as a whole mimestream so you can later store it as a file. In the past this was possible with the (meanwhile deprecated) Exchange asmx web-Services. Nowadays at there is also a Microsoft Graph endpoint for this.

The Api is simple me/messages/${mail.id}/$value
In the following function we are calling that endpoint and using the response to handover to next function for storing it somewhere.

GraphController: Retrieve Mail as MimeStream

Several parameters are not really needed here they are just handed in to handover to the next function for handling the response. The only needed parameter here is the mail respectively its id.

Once we have a positive result we need to check the size of the response, that is our mimestream. This is because for a file save operation via Microsoft Graph there are two different scenarios one simple scenario for files smaller than 4MB and a more complex one for files larger than 4MB. But this difference we will cover in the final part where we handle the storage operation of the the mimestream to a Group, Team, SharePoint or OneDrive. So stay tuned.

Meanwhile you can check the full code repository here in my GitHub.

Markus is a SharePoint architect and technical consultant with focus on latest technology stack in Office 365 and SharePoint Online development. He loves the new SharePoint Framework as well as some backend stuff around Azure Automation or Azure Functions and also has a passion for Microsoft Graph.
He works for Avanade and is based in Munich.
Although if partially inspired by his daily work opinions are always personal.
An Outlook Add-in with SharePoint Framework (SPFx) – Browsing your target OneDrive, Groups, Teams

An Outlook Add-in with SharePoint Framework (SPFx) – Browsing your target OneDrive, Groups, Teams

Since SharePoint Framework version 1.10 you can also develop Office Add-Ins with SPFx starting with Outlook Web Access in public preview. The great benefit is you already have the prepared context for access of Microsoft Graph. In this demo scenario I want to show you how to create a valuable Outlook Add-In with the capability to store complete mails to OneDrive, Office 365 Groups or Microsoft Teams.

An Outlook Add-In to copy a whole mail to Teams, Groups or OneDrive

In this part you will see how to retrieve all joined Groups or Teams and further retrieve the drives or folders, also from OneDrive, to browse them and select one as the target.

The UI simply follows quite the same process than moving an item within OneDrive or SharePoint. At first you can select your target system OneDrive, Office 365 Groups or Microsoft Teams.

Once you click one, and here we start with Groups respectively Teams as they are “higher” in terms of object hierarchy, we need to list all available Groups or Teams to the user . Therefore we have two functions in our GraphController.

GraphController: GetJoined Groups and Teams

As we do not need specific attributes of Groups and Teams, only as a parent container for browsing we treat them as a folder (IFolder interface). Drives and folders itself will only need the same attributes (id, name, optionally reference to parent item).

At the beginning of our “Groups” or “Teams” component we call that method and put the retrieved items as folders to our state.

We then render them in a simple way. This will be repeated later for all child items the same way.

Teams component (Groups component quite similar)
Folder component

Interesting is the subFolderCallback attribute of each folder which is set in the render function. In case we have a parent item (which has a parentFolder of null) we know this is a Group or Team. So once you click on it you want to see the drives below. In all other cases such as a drive or a folder a click on it wants to retrieve folders as child components. This is always the same method.

GraphController: GetDrives and SubFolders

As a folder has a parentFolder attribute of same type IFolder that parent also has/can have a parentFolder. With that information we can also create our simple breadcrumb.

Breadcrumb component

This breadcrumb can have up to three parts:

A << root anchor which brings you back to the root. The grandParenFolder (here “Folder 1”) which has a link and brings you one level up and a parentFolder (here “Subfolder 1”) which is not linked as it is the current location where you would store your mail right now in case you push the button and from which you might see eventually available child items.

In OneDrive it works quite the same but with two levels less as you only have one OneDrive (and not several Groups or Teams) and directly want to list the first level of folders once you open it. From there the functionality is quite the same.

In the next part we will get to know how to retrieve our mail as a full MimeStream object from Microsoft Graph.

Meanwhile you can check the full code repository here in my GitHub.

Markus is a SharePoint architect and technical consultant with focus on latest technology stack in Office 365 and SharePoint Online development. He loves the new SharePoint Framework as well as some backend stuff around Azure Automation or Azure Functions and also has a passion for Microsoft Graph.
He works for Avanade and is based in Munich.
Although if partially inspired by his daily work opinions are always personal.
An Outlook Add-in with SharePoint Framework (SPFx) – Introduction

An Outlook Add-in with SharePoint Framework (SPFx) – Introduction

Since SharePoint Framework version 1.10 you can also develop Office Add-Ins with SPFx starting with Outlook Web Access in public preview. The great benefit is you already have the prepared context for access of Microsoft Graph. In this demo scenario I want to show you how to create a valuable Outlook Add-In with the capability to store complete mails to OneDrive, Office 365 Groups or Microsoft Teams.

An Outlook Add-In to copy a whole mail to Teams, Groups or OneDrive

Let’s start the first part by creating our first Outlook Add-In. It’s already documented quite well in a Microsoft tutorial and I won’t repeat the necessary steps here and they are also quite familiar to you I suppose as you are not new to SharePoint Framework.

The only difference I made to the tutorial above and what you can do as well of course: Use the “React Framework” instead of “No JavaScript Framework”.

At first lets only take a short look into the Outlook Add-In manifest. I won’t go into details about changing position or logo or something but at least a custom title we should give. Therefore open the XML file in the \officeAddin folder and set the following settings according your choice:

What we need to do next is to get our current mail as we want to run our add in in the context of a selected mail. The following code snippet simply handles that:

Base Webpart render function

We first check this.context.sdks.office and if we have that we try to use this.context.sdks.office.context.mailbox.item. From there we can retrieve the mail ID and the subject which we will need at a later point of time. We hand them over to our first React component and in case we found nothing we will handle that null value adequately. On top we hand over the msGraphClientFactory which we will use to connect to Graph for folder retrieval, mail storage and so on.

That component I reduced a bit in code and explanation here. The main task is to render the initial logic for browsing your Teams, Groups or OneDrive folders but also hold the storage methods. Both I will cover in separate parts. The full code you can find in my GitHub repository.

Base Component

First we instantiate a GraphController and once ready it is set to the state so we know it’s there and can display our entry points for OneDrive, Groups and Teams accordingly.

In the second part we will get to know the browsing logic through all available Teams, Groups respectively OneDrive Folders where we can store our mail.

Meanwhile you can check the full code repository here in my GitHub.

Markus is a SharePoint architect and technical consultant with focus on latest technology stack in Office 365 and SharePoint Online development. He loves the new SharePoint Framework as well as some backend stuff around Azure Automation or Azure Functions and also has a passion for Microsoft Graph.
He works for Avanade and is based in Munich.
Although if partially inspired by his daily work opinions are always personal.

AppSettings in SharePoint Framework (SPFx) and Staging

With SharePoint Framework 1.6.0 the great capability to use 3rd party Apis went GA. To use such a 3rd party Api you regularly need to use a Url. A simple scenario how to do that is described in Microsoft’s tutorial or the other one when you have a multi-tenant scenario (your Office 365 tenant is not the same like the managed Azure Active Directory, which can be the case especially in staging environments).

When it comes to handling of Urls in enterprise solutions you automatically get to the staging challenge. You might have a test stage where your test SPFx webpart might want to call a test azure function and an acceptance SPFx webpart might want to call the acceptance version of your azure function for instance.

So how to handle that?

I assume you already have established a Release Pipeline in your VSTS environment. If not Elio Struyf is your go-to resource. Furthermore there is a great post from Velin Georgiev who shows us how to use AppSettings inside your SPFx project. His post gave me the inspiration for this alternative solution I want to show here.

According to his post we simply need an appSettings file with following content:

Additionally as per Velin’s post we also need the corresponding d.ts file which in this case might look like this:

To use this in your code when calling the aadHttpClient it would look like the following:

We first import our appSettings. For the sake of brevity here I combined retrieving the aadHttpClient with the Azure Function call. In line 6 respectively line 9 we use the AppId / Url of our function retrieved from the appSettings. So far, so good.

But now comes the new stuff: How to update this on each new stage deployment? Therefore in my case I decided to put ALL stage settings in another json file which I call appSettings.all.json:

As you can guess now during deployment I will hand in the current stage name/abbreviation and copy from the “all” source to appSettings the right Url / AppID combination (we come to that in a minute). But at first I want to answer another question: Why do I put all my settings in the source code and not in a DevOps variable in VSTS for instance? Well, this parameters are no Fort-Knox secret like passwords, credentials e.g. They will occur in the JS code finally anyway when the user runs it. And in big solutions with lots of webparts you might have ONE release pipeline but a significant number of Azure Functions to call, maybe? That is my reason for this way.

Let’s go on with the final thing, that is a new gulp task to copy the right Url/ID for the related stage. The gulp task you find below is slightly similar to those from Elio in his VSTS posts where he updates the write-manifest.

So what does the task (which is quite simplified to get an easy understanding). It first gets the stage by an input parameter (run “gulp update-appSettings –stage DEV” for instance). Stages per our definition in the appSettings.all.json are DEV, TST, ACT & PRD.
Next is to retrieve the stage-corresponding Url and ID parameter (line 11,12,15,16) and finally writes them to the target appSettings.json file (Line 15,16) and stores that file (Line 17). That’s it.

Hope this helps you enhancing your DevOps release pipeline for SharePoint Framework (SPFx) in VSTS.

Executing batch requests with Microsoft Graph and SharePoint Framework (SPFx)

In my recent blogpost I wrote about using batch requests with the SharePoint Rest Api within a SharePoint Framework (SPFx) solution. Today I want to construct a similar scenario which shows batch requesting using the new Microsoft Graph access class within SPFx.

Please note that this approach  is currently, when writing this post (Jul-2018), in final preview mode and therefore not supported in production scenarios as it might be subject to change. A GA is expected with the next SPFx version (1.6.0) end of this month.

Update: This is now targeting the GA version from SPFx 1.6.0 on.

Lets start with a simple scenario. Suppose we have a bunch of Group IDs

Within our component we will do several things now. First within the constructor (you might use a Graph call several times within your code, otherwise you can also do it in your function) we instantiate a MSGraphClient.

As this is an async call now, it is debatable to do this in the constructor or at a later stage in componentDidMount. In larger projects I usually exclude the whole data stuff to a separate controller class which I instantiate in the constructor.
In a getGroupsData function we then create our requests based on the IDs of our Groups from above (Please note that we also handover an index which we can later use to simply identify the repsonse if needed as the repsonses might be ordered differently than the requests originally were):
What happens afterwards is one simple call to the Graph Api handing over all requests in the body. If no error occurs and we recieve a Status of 200 per response (also one single request might fail, for instance when we have provided a wrong Id) we can push the response part to our array based on our custom UnifiedGroup interface.
When every response is handled, we set our state which lets our component re-render as usual.
Please pay attention that there is a hard limit of 20 (!!) requests per batch request. So in case you would have more than 20 Ids (or something similar, this is a constructed scenarion of course) you need to create several batch requests, similar to a paging approach.
Before we can test our stuff, we also need to request permissions in the package-solution.json
For debugging with the workbench this is sufficient (this now works with SPFx 1.5.0!), when deploying our solution we have to grant that permissions by an admin tenant-wide also as described here.
Update: 2 additional things to mention here are the general request for “Windows Azure Active Directory” | “User.Read” which is required to have for all requests. On top I recently came across an obviously general limitation of having the string values for resources and scopes not more than 255 characters in total. So pay attention on this as well when having to request a significant number of Graph resources for instance. But as you only have to approve once you might split up your request over several packages but must rely on their all existence inside a tenant then.
Once we run our simple solution it might (simply rendered) look like this:
result
Finally for your reference the whole component’s source:

Use SharePoint Rest API Batch component to retrieve site logos

In the modern design of Office 365 and SharePoint Online in detail it is all about logos, photos and tiles or cards. Unfortunately there is still no easy Api to retrieve.

There is an undocumented version that helps maybe but only in case of ‘followed’ or ‘recent’ sites are what you want. Here is the issue and here the uservoice request.

In this post I want to show you another approach that uses the batch capability which is available for the SharePoint Rest API and can be easily used from the SPHttpClient class within SharePoint Framework (SPFx).

Furthermore the example is kept quite simple so you can also get an idea about batch requests with SharePoint Rest Api ans SPFx in general and adapt it to your own case.

Assume we have a bunch of site Urls in our use case:

When we start a batch we first need a url:

Now we have the problem that we cannot simply request the site properties as this would lead to totally different Urls which doesn’t work.

So in detail, a batch request with requests to
https://<yourtenant&gt;.shrepoint.com/sites/SiteA
https://<yourtenant&gt;.shrepoint.com/sites/SiteB
https://<yourtenant&gt;.shrepoint.com/sites/SiteC
won’t work but several request to the same site but different lists, items, e.g. will work.

Fortunately search helps out here. We can simply request the needed properties with a search query for a site with our specific URL from current context:

Now we have constructed and collected all of our requests. We can now send them to the server and handle the responses:

Two important things to explicitly mention here:
First we use Promise.All() to wait for all requests to be completed. If we would handle each one be one we would need to write to our state each time a request is done. Or we would store it in a variable but then the challenge is when are all done and we can transfer to state? A counter can help (increase on request, decrease on response) but Promise.All() is much simpler.
Second we have to handle another async call, the json() method. Therefore we build another array of requests and with another Promise.All() we handle our final JSon response.

As we have to handle the Response of a search call, we use the helper Interfaces as below:

This approach is taken from Elio Struyf’s SPFx search example webpart.

Finally we can render our data.

RestBatch_Result

As you can see I only used Groups respectively modern teamsites in my example. I like that they expose a site logo by default as a combination of an acronym and a randomly picked color. Modern communication sites display the same by default but do not expose the values (except in the above mentioned undocumented Api on followed or recent sites) or a built image as modern teamsites do. Mikael Svenson also commented on this a while ago, so we hope that Microsoft will improve the logo handling quite soon.
But I hope I gave you some ideas how to improve this yourself and also how to handle batch requests in general.

Now stay tuned for my next example where I will cover batch requests with Microsoft Graph and SharePoint Framework (SPFx).

Last not least the whole component class for your reference:

Extend PnP SharePoint Framework React ListView Control with a context menu

Extend PnP SharePoint Framework React ListView Control with a context menu

The ListView component from the PnP SharePoint Framework React Controls is really simple in use. And it’s furthermore easily extensible. In this little post I want to simply show how you can add a context menu to the list item like it’s available in the default modern experience of lists and like the “edit control block” in the classic experience.

Assume we have a simple list view webpart like the following

01_EmployeeListView_rel1

achieved by the following code

We now need to create another component which represents our context menu. Therefore we will use a combination of an IconButton and a ContextualMenu from the Office UI Fabric components.

The code for this additional component looks like this:

One of the tricks is here to give an empty iconName for the menuIconProps. This is because once you attach a menuProps Attribute to any Kind of Office UI fabric button a ChevronDown selector on the right side of the button will occur by default. With the menuIconProps Attribute you can adjust this, that is when specifying an empty Name you can remove it. This is what we want because we only want to have our “MoreVertical” icon which are the three dots in a vertical order.
To place this a bit more centric, we have small CSS manipulation as well:

Finally we need to put the pieces together. Therefore we will insert another Viewfield in the webpart code at the position of our choice, for instance after the “Lastname”:

{
        name: "",
        sorting: false,
        maxWidth: 40,
        render: (rowitem: IListitem) => {
	 const element:React.ReactElement<IECBProps> =React.createElement(
            ECB, {
	      item:rowitem
            }
          );
          return element;
        }      
},

We use the render method of IViewField. Inside we create our ECB component and as a property we handover the rowitem which is clicked. We could also handover functions that shall be executed inside the context menu component but be able to be bound to the parent component, that is the webpart which contains the ListView.

The result will look like the following:

02_EmployeeListView_ecb

And in action it shows which function and item was clicked:03_EmployeeListView_ecb_clicked

 

SharePoint Framework (SPFx) extensions – Simple Field Customizer to show a traffic light status (RAG)

SharePoint Framework (SPFx) extensions – Simple Field Customizer to show a traffic light status (RAG)

Recently the new SharePoint Framework extensions got available in developer preview. Lots of exciting possibilities to customize your SharePoint Online in modern experience view.

In my last blog post I showed how to realize a mega or global menu with the new SPFx application customizers. Here I give you a quick and simple example to render a status field as a traffic light (RAG) using the new SPFx field customizers.

Let’s assume we have a simple dropdown column with three text values (‘Red’, ‘ Yellow’, ‘Green’):
Status_Dropdown_RAG

In a real world scenario you might use other values such as cost margins or something else, based on that figures you might want to render a green or red light but lets keep it simple here.

Instead of displaying the simple text we want to show a traffic light depending on which color the text states. I like standard images from SharePoint and often use the following three ones:

RAG_Green   RAG_Amber   RAG_Red

We simply extend the Microsoft ‘Getting Started Example’ by editing the OnRenderCell() function.

There are two main objects available that might influence your rendering:
The current field and its configuration represented by

this.context.column.field
and the IFieldCustomizerCellEventParameters represented by

event

In our case we doublecheck the internalFieldName (assuming our script might be deployed for more than one field later on) and we use the value that the field contains in our current item (this.context.pageContext.listItem would be another possibility to get this) with
switch (event.cellValue)
In the end we adjust the innerHTML of our whole DIV cell by

event.cellDiv.innerHTML = '...
in our case this is enough in other cases, such as those from the Microsoft example you can access/manipulate the default rendering by
CellFormatter.renderAsText(this.context.column, 'This is the field value');
This function will give you the rendered HTML string based on the current field(type) and its value.
In the end we have our listview with the renderings.
RAG_Listview

Micrososft says that currently field customization is only available in read mode. This made me assume that it would also work in a DisplayForm but when debugging it I received the warning message to “Load debug scripts” but neither OnRenderCell() nor OnInit() functions were hit when I placed a

debugger;

statement. Lets see how this evolves over the next time.

For your reference the whole source, based on the Microsoft example:

Additionally you can add the column as a site column together with your field Extension in an app. Therefore use the following elements.xml and take care to use your own ClientSideComponentId

SharePoint Framework (SPFx) Application Customizer – Global Menu Example

SharePoint Framework (SPFx) Application Customizer – Global Menu Example

In Jun-2017 the new SharePoint Framework extensions got available in developer preview. Lots of exciting possibilities to customize your SharePoint Online in modern experience view. From my last blog post I assumed to first cover the new field customizers.

But having a first look at the demos, the possibility to realize a global menu without DOM dependencies came to my mind as a first example:
I updated this article in Jul-2018 to match the GA Version as I recognized that this article is often referneced and frequently read by many developers.

My example sticks close to the code sample using the page placeholders. In this Microsoft article the general behavior of the new page placeholders is described pretty good. Another good source is from Chris O’Brien.

I want to explain why a global menu is a good example and how to realize it in a quick way (first) and potentially later in a more robust version.

In the past there were two possibilities to attach such a global menu: The SPWebApplication.SuiteBarBrandingElementHtml property in on-premise and a DOM injection in SharePoint Online. The latter one was no good idea as I know more than one company that “lost” their menu due to DOM changes in O365 😉

So with the new application customizer placeholders, that is the PageHeader in our case, we can insert our global menu into the SharePoint Online page. As we did with on-premise or classic view and custom actions we can apply those to site, web or list level. But for a global menu applying to many, many sites will make sense.

In our render method (which we have to call from onInit, see MS example or my Code at the bottom), we first attach the ‘Top’ placeholder. This is nothing Special (but changed since preivew) and taken from the Microsoft example.
Then we add our ‘Burger Menu’ Icon:

The three inner DIVs (menubar1-3) represent the bars of the burger and everything is styled with the following CSS:


.header {
    height:40px; 
    text-align:left; 
    line-height:2.5; 
    font-weight:bold;
    display: flex;
    position: relative;
}
.menuicon {
    width: 30px;
    height: 20px;
    float: left;
    margin: 4px;
    cursor: pointer;
}
.menubar {
    width: 30px;
    height: 4px;
    margin: 4px 0;
    background-color: #fff;
    transition: 0.4s;
}

Furthermore the following CSS makes the bars (1 and 3) move to a cross, respectively disappear (2) once clicked:


/* Rotate first bar */
.change #menubar1 {
  -webkit-transform: rotate(-45deg) translate(-6px, 6px) ;
  transform: rotate(-45deg) translate(-6px, 6px) ;
}

/* Fade out the second bar */
.change #menubar2 {
  opacity: 0;
}

/* Rotate last bar */
.change #menubar3 {
  -webkit-transform: rotate(45deg) translate(-6px, -6px) ;
  transform: rotate(45deg) translate(-6px, -6px) ;
}

What is important to reference the CSS is to import our styles with the following line of code (I used the same file names than in the MS example):


import styles from './MegaMenuApplicationCustomizer.module.scss';

and to use “ for our strings, not the usual single quotes ” !!

What we have so far, is a Top placeholder with our burger icon:

Closed Burger Menu

Now lets first render our global menu and afterwards show it onClick of the menu-icon. Of course we need a data source for our menu. As this is a first example with focus on the new SPFx capability I use a simple in-line JSON object as we know from client-side-webpart Mockup data. In a robust production solution there are better options. Some are mentioned in a very good guidance of Microsoft on well performing SharePoint Online development. But my personal favorite would be to use a mix of global termstore (available only once, UI for maintaining exists) and caching (much faster than accessing termstore). Unfortunately termstore is still not accessible with REST and so the new SPHttpClient cannot be used but you would have to use the good old JSOM (which is not invested in anymore!). Anyway lets use a simple JSON object array here.

I created an own class here called GlobalMenu in a separate file (GlobalMenu.ts):

The MenuItem has three properties: A ‘Col’ for the number of the column, a ‘Label’ for text to display an an ‘Url’ (for headers I treated them as optional, see the if (item.Url != …).

The main stuff occurs in our getHTML() function which we later call in our main class.
In an outer loop we iterate the ‘headers’ to create headlines per column and afterwards with an inner loop we iterate our menuitems per column, check each subitem if it belongs to our current column and if so, render it as an

At last we close our HTML tags an return the whole HTML of our menu to the calling main class. Having a different data source you would just adapt it to this class and in the end return quite the same HTML.
Above we skipped that but here we notice that the HTML is inserted just below our burger menu Icon in our main onRender function:


${GlobalMenu.getHTML()}

Finally there is also some CSS to style our menu as you could guess from the used escapes, such as class=”${styles.colCell}”.


.globalMenu {
  position: absolute;
      top: 35px;
  left: 0px;
  background-color: rgba(225, 225, 225, 0.5);
  z-index: 0;
  line-height: normal;
  padding-bottom: 25px; 
  padding-top: 5px;
  height: 0;
  transition: opacity 1s ease-out;
  opacity: 0;
  overflow: hidden;
  width: 100%;
  display: table;
}
.globalMenu.change {
  opacity: 1;
  height: auto;
  z-index: 20000;
}
.row12 {
  width: 100%;
  display: table-row;
}
.colCell {
  float: left;
  display: table-cell;
  width: 12%;
  margin-left: 10px;
  min-height: 240px;
  background-color: #fff;
  border: 1px solid rgba(18, 98, 101, 1);
  
.globalMenu {
  position: absolute;
      top: 35px;
  left: 0px;
  background-color: rgba(225, 225, 225, 0.5);
  z-index: 0;
  line-height: normal;
  padding-bottom: 25px; 
  padding-top: 5px;
  height: 0;
  transition: opacity 1s ease-out;
  opacity: 0;
  overflow: hidden;
  width: 100%;
  display: table;
}
.globalMenu.change {
  opacity: 1;
  height: auto;
  z-index: 20000;
}
.row12 {
  width: 100%;
  display: table-row;
}
.colCell {
  float: left;
  display: table-cell;
  width: 12%;
  margin-left: 10px;
  min-height: 240px;
  background-color: #fff;
  border: 1px solid rgba(18, 98, 101, 1);
border-left: 10px solid rgba(18, 98, 101, 1);
}
.colCell > h2 {
  color: rgba(18, 98, 101, 1);
}
.colCell > ul > li:hover {
  background-color: rgba(24, 131, 135, 1);
}
.colCell > ul > li > a:hover {
  color: #fff;
}

As the both classes (MegaMenuApplicationCustomizer and GlobalMenu.ts) build one HTML component and act together (open / close), I used only one scss module for both. See reference below.

You see that we build our menu with DIVs in table style. To not show it from the start we used z-index and a height of 0 (which is not the best from an accessibility Point of view,  but for simplicity here…). Additionally we used a little nice hover effect for our items. That’s it.

In the end we only need to add an onClick Event to our menu-icon. This event simply needs to add a CSS class called ‘change’. On its existence it depends if the menu is shown or not and if we see a burger or a cross (latter one see picture below).


document.getElementById('menu-icon').onclick = this.menuClick;

adds the event listener within onRender and


private menuClick(): void {
  document.getElementById('GlobalMenu').classList.toggle(`${styles.change}`);
  document.getElementById('menu-icon').classList.toggle(`${styles.change}`);
}

is the function that toggles the ‘change’ class.
In the end we have our menu occur on click:

BurgerMenu_Open

For test, deploy and so on refer to original Microsoft documention.

For your reference find the whole code of the main class and the SCSS below: