Friday 28 April 2023

Sitecore JSS/Headless ColumnSplitter in 10.2 (with bonus Tailwind styles)

I saw a post in the Sitecore Slack channel recently asking about ColumnSplitter support in 10.2. This was something I have back-ported recently (as columns really are an essential component in all designs these days) and have been meaning to post about, so it was a good kick to get this post going. Thanks @gatogordo!

You may think that the ColumnSplitter component provided in the XM Cloud starter kit looks fairly straightforward and should work in older versions of Sitecore, but there are a few key changes that are required, both in the JS and in Sitecore, which I will go through now.

Note: I'm going to be using SXA and Tailwind in this post, to get up and running as quickly and painlessly as possible. No doubt this would work without them, but there would be additional / different steps involved.

TL;DR checkout the repo: https://github.com/moo2u2/jss-column-splitter-example


My repo includes a storybook demo

This is what we're going for!


First, ensure you have a 10.2 instance of Sitecore with (the correct version of) SXA and Headless modules installed. If you want to restore the items from the repo above, you'll also need Sitecore Management Services (for serialization) installed.

We'll start by creating a headless Tenant and Site in Sitecore, as well as creating a new Next.JS project with the same name as the site, and the correct version for Sitecore 10.2.

npx create-sitecore-jss@ver20 nextjs --appName jss-column-splitter-example

At this point it's worth running a jss setup to configure your Sitecore connection, and verifying that your app runs correctly in connected mode, because we're about to get into the fun part and add/change a bunch of stuff.

We'll start by creating a ColumnSplitter rendering, which I originally grabbed from the XM Cloud Starter repo (or you can just grab my back-ported version).  Next you'll want to add this to your placeholder settings (in my case, jss-main) so that you can add it to the page in Experience Editor.

then go ahead and add the component to your page (in that placeholder), either in Content Editor or Experience Editor.  

At this point it's probably worth taking a pause to explain how the column splitter works: 
Obviously (by default) it displays no content, so all the settings live in the rendering parameters (no datasource).
The rendering parameters are made up of 2 main parts: the grid, and styles. Both contain CSS classes, however they come from different parts of the content tree.

There are grid + styles options for each column, up to 8 columns (out of the box), as well as the styles for the component itself.

Rendering parameters live at: /sitecore/templates/Feature/JSS Experience Accelerator/Page Structure/Rendering Parameters/ColumnSplitter or {181740EA-A7AE-4799-A649-A75917570E38} and references a couple of other Templates you may not have: Base Rendering Parameters ({4247AAD4-EBDE-4994-998F-E067A51B1FE4}) and _PerSiteStandardValues ({44A022DB-56D3-419A-B43B-E27E4D8E9C41}).

If you open the rendering parameters at the monent, you'll likely notice that they are not displaying correctly:



Starting with styles: as with regular SXA, we need to create a "Styles" item under your Tenant > Site > Presentation, of type: /sitecore/templates/Foundation/Experience Accelerator/Presentation/Styles - {C6DC7393-15BB-4CD7-B798-AB63E77EBAC4}. Under that you can add your styles (I've added various backgrounds for demo purposes).


Now we can see the styles listed in our rendering parameters!


The grid parameters are slightly more complex, as they depend on the SXA grid system, so if you aren't familiar with it I would check out the documentation on how to Create a custom grid.

Because Bootstrap is old school and we're using headless, let's create a faux "grid" for Tailwind! I've added a few different widths for our columns:

Next we need to update our site settings to have the field for, and reference, this grid:
In /sitecore/templates/Foundation/JSS Experience Accelerator/Multisite/JSS Settings - {EC848505-D30C-4BDC-A0AA-7CC9D320085E} add /sitecore/templates/Foundation/Experience Accelerator/Grid/_Grid Mapping - {9D81C61A-0341-4312-816D-E5204385EA3C} to the inherited templates



Back up in our site settings we should see Bootstrap 5 selected as the default grid, which we can now change to our Tailwind grid!

This is the default - change it to our new Tailwind grid

At this point our rendering parameters should all be good and useable!


Finally we need to add our placeholder settings for each column. This is one of the key differences in back-porting it, as we don't use any wildcard asterisk. Just a plain old placeholder name.


With your ColumnSplitter added to the page, select a size (and optionally a style) for a couple of columns and let's take a look at the layout service to double check it's all good for the app to consume: 
/sitecore/api/layout/render/default?item=/&sc_apikey={YOUR_KEY}

We can see the component added to the placeholder, and values in the grid and styles but those aren't useable values...


This is where I introduce you to my mate Andy's blog post on Serializing rendering parameters in the layout service
Note: If you have selected multiple grid sizes (for different devices), you will end up with multiple pipe-separated GUIDs, which his blog doesn't cover. You will need to update your code to cater for this.
With the fix in place, you should see the rendering parameters serialized in JSON format:


We're now in a state where we're good to consume this from the headless app we created at the start! 
Run your app in connected mode, and input the following into your site settings:

Server side rendering enginehttp
Server side rendering engine endpoint URLhttp://localhost:3000/api/editing/render
ServerSideRenderingEngineApplicationUrlhttp://localhost:3000

and you should be able to see your component in the Experience Editor!

You can have a play around with different grid sizes and styles, and experiment with adding different content to your placeholders.

Extra Tidying 

Since we're using SXA, let's make the "Add randering" popup display the SXA way:
Under your Tenant > Site > Presentation, add an "Available Renderings" item of type  /sitecore/templates/Foundation/Experience Accelerator/Presentation/Available Renderings/Available Renderings Folder - {39A9ED72-407D-4440-BA50-30956834794E} and add your renderings.

This should make the modal popup much more user-friendly!



Thursday 6 April 2023

Sitecore Forms long labels truncated

The cause of this is fairly straightforward, but given how painful it is to debug I thought I'd post this for anyone else who comes across it.

If you are using Sitecore Forms, and ever have the need to use a very long label in a list item (eg. a checkbox list) you will notice it gets truncated. You may even note that the length is suspiciously similar to the max length of an item name...

After a lot of digging you'll note that these list item label/value strings are altered and set in Sitecore.ExperienceForms.Mvc.DataSource.DataSourceSettingsManager, specifically in the UpdateStaticItems method where there is a call to ItemUtil.ProposeValidItemName() for both label text and value. This is certainly required for one (if it's being set as the name or display name) however not for both.

Thanks to dependency injection the fix is nice and easy to patch in:

<?xml version="1.0"?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/"  xmlns:role="http://www.sitecore.net/xmlconfig/role/">
  <sitecore>
    <services>
      <register serviceType="Sitecore.ExperienceForms.FieldSettings.IFieldSettingsManager`1[[Sitecore.ExperienceForms.Mvc.Models.ListFieldItemCollection, Sitecore.ExperienceForms.Mvc]], Sitecore.ExperienceForms" 
        patch:instead="register[@serviceType='Sitecore.ExperienceForms.FieldSettings.IFieldSettingsManager`1[[Sitecore.ExperienceForms.Mvc.Models.ListFieldItemCollection, Sitecore.ExperienceForms.Mvc]], Sitecore.ExperienceForms']"
        implementationType="MyProject.Feature.Forms.DataSource.DataSourceSettingsManager, MyProject.Feature.Forms" lifetime="Transient" />
    </services>
  </sitecore>
</configuration>

Saturday 1 April 2023

next-auth error: "Can't resolve module 'next/headers'"

The Issue

Recently I've been working on a site using Sitecore headless (JSS) with authentication. As we're using Next.JS it makes the most sense to utilise NextAuth.JS which has been around for a while, has super easy setup, and integrates with loads of providers - including a couple which we needed for the project.

Everything seemed to be working well, until we started using getServerSession() to fetch the session info server-side, when everything started to fall apart in errors - particularly one mentioning:

Can't resolve module 'next/headers'
A quick search led to a few results such as issue 6559 which indicated we may need to revert to version 4.15.1 (we were using the latest, 4.20.1). This did indeed appear to get around that particular error, though we weren't sure at what cost (and we weren't about to go looking through the change history between those 2 versions). 

Instead I made a small git repo with a minimal reproduction of the issue (all JSS mocked out), logged issue #7103 on the next-auth github repo, and surprisingly heard back from one of the authors within 24 hours with a workaround. Not as good as a fix, of course, but it unblocked us which is definitely a great outcome!

The Fix

Checkout my branch patch-fix for pre-fixed version.

Perform the following steps in your local project:

npm install --save-dev patch-package

Remove the 'if' block from packages/next-auth/src/next/index.ts#L121-L139 leaving just the 'else' lines packages/next-auth/src/next/index.ts#L136-L138.

npx patch-package next-auth

This should resolve the issue for anyone else using the repo!

Note: Please keep in mind this is only relevant for Next.JS v12 (not v13) - see the comment from balazorban44 in the issue