NextJS/JSS Edit Frames Before JSS v21.1.0

> It is possible. We have the technology.
Cover Image for NextJS/JSS Edit Frames Before JSS v21.1.0

Overview

I wanted to implement edit Frames on a NextJS/TS/JSS v20.1.3 site. I was disappointed to find out that official support for edit frames was only added in v21.1.0, so I went out looking for other options.

Why Edit Frames?

Edit frames are a useful tool for content authors to edit arbitrary fields on arbitrary items via arbitrarily placed clickable elements on a page. One example would be if you wanted a way for a user to edit a background image on a section. You might render an absolutely positioned clickable icon that would allow the background image field to be edited. The possibilities are endless.

The Implementation

I was inspired by this SSE post about possible solutions: https://sitecore.stackexchange.com/questions/24307/edit-frame-with-sitecore-jss.

One of the answers points to a custom implementation of edit frames that someone wrote ~3.5 years before Sitecore added official support for it 👀: https://github.com/Sitecore/jss/pull/290. I refactored / modernized the code to work with strict TypeScript and the latest React patterns.

The Code

import React from 'react';
import { useSitecoreContext } from '@sitecore-jss/sitecore-jss-nextjs';
interface FieldEditFrameProps {
itemId?: string;
fields?: string[];
header?: string;
icon?: string;
disabledIcon?: string;
tooltip?: string;
command?: string;
commandDisplayName?: string;
databaseName?: string;
language?: string;
hideIfNotPageEditing?: boolean;
}
const FieldEditFrame: React.FC<FieldEditFrameProps> = ({
itemId = '',
fields = [],
header = 'Edit Fields',
icon = '/temp/iconcache/people/16x16/cubes_blue.png',
disabledIcon = '/temp/cubes_blue_disabled16x16.png',
tooltip,
command = '{70C4EED5-D4CD-4D7D-9763-80C42504F5E7}',
commandDisplayName,
databaseName,
language,
hideIfNotPageEditing,
children,
}) => {
const { sitecoreContext } = useSitecoreContext();
const { pageEditing, language: sitecoreLanguage, route } = sitecoreContext;
const shouldRender = pageEditing && itemId && fields && fields.length;
if (!shouldRender && hideIfNotPageEditing) {
return null;
}
if (!itemId || !fields) {
return null;
}
// TODO: handle route undefined
const contextItemUri = `sitecore://${databaseName || route?.databaseName}/${itemId}?lang=${
language || sitecoreLanguage
}`;
const clickCommandAction = `webedit:fieldeditor(command=${command},fields=${fields.join(
'|'
)},id=${itemId})`;
const clickCommand = {
click: `javascript:Sitecore.PageModes.PageEditor.postRequest('${clickCommandAction}',null,false)`,
header: header,
icon: icon,
disabledIcon: disabledIcon,
isDivider: false,
tooltip: tooltip || `Edit the following fields: ${fields.join(', ')}`,
type: null,
};
const commandData = {
commands: [clickCommand],
contextItemUri: contextItemUri,
custom: {},
displayName: commandDisplayName || 'Edit Properties',
expandedDisplayName: '',
};
const divAttrs = {
sc_item: contextItemUri,
'sc-part-of': 'editframe',
};
return (
<div className="scLooseFrameZone scEnabledChrome" {...divAttrs}>
<span className="scChromeData">{JSON.stringify(commandData)}</span>
{children}
</div>
);
};
export default FieldEditFrame;

Example Implementation

import FieldEditFrame from './../../utils/FieldEditFrame';
// other code
return (
<>
<FieldEditFrame
// of course, its better not to hardcode these values
itemId="{FEA43559-D12F-47C9-911C-8170B7FAEFA4}"
fields={['Alert Label']}
>
<h2>Goo Goo Ga Ga</h2>
</FieldEditFrame>
</>
);

Now the element is clickable: Edit fields popup

And clicking the blocks icon opens the field editor window: Field editor window

Further Steps

Be sure to do some extra testing on your local:

  • How does it behave during hot reloads?
  • Any console errors?
  • Does it work with multiple edit frames on the same page?

Build or beg,

Marcel


More Posts