Adding Rendering wrapper in Experience editor is way to help Content author to build and edit pages more efficiently by highlighting each rendering hierarchical details making page structure distinguishable and less confusing if multiple child components are present.

Lets see how it can be done in JSS Sitecore:

Create processor that implements RenderRenderingProcessor

public class AddPageEditorMetadata : RenderRenderingProcessor
{
private readonly List<Regex> _parentNameMatcher = new List<Regex>();
private readonly List<Regex> _itemNameMatcher = new List<Regex>();

// checks the immediate parent folder of the rendering against each pattern in the list. If any pattern matches, the rendering is wrapped.
public void AddParentNamePattern(string pattern)
{
_parentNameMatcher.Add(new Regex(pattern));
}

//checks the name of the rendering itself against each regular expression in the list. If any pattern is matched, the rendering is wrapped.
public void AddItemNamePattern(string pattern)
{
_itemNameMatcher.Add(new Regex(pattern));
}

public override void Process(RenderRenderingArgs args)
{
if (args.Rendered || Context.Site == null || !Context.PageMode.IsExperienceEditorEditing)
{
return;
}

RenderingContext renderingContext = RenderingContext.CurrentOrNull;

Rendering rendering = renderingContext?.Rendering;

// rendering.RenderingItem is null when presentation details points to a rendering that is no longer in Sitecore
// RenderingXml property is only set on renderings that were bound to a placeholder via presentation details
if (rendering?.RenderingItem == null || rendering.Properties[“RenderingXml”].IsWhiteSpaceOrNull())
{
return;
}

DoProcess(rendering, args);
}

protected virtual void DoProcess(Rendering rendering, RenderRenderingArgs args)
{
Assert.ArgumentCondition(args.Disposables.Count > 0, “args.Disposables”, “expect to be more than zero”);

RenderingItem renderingItem = rendering.RenderingItem;

// do we need to wrap component with meta data to make it visible within EE, usually it is necessary for a structural component.
bool isMetaReq = _parentNameMatcher.Count > 0 && _parentNameMatcher.Any(x => x.IsMatch(renderingItem.InnerItem.Parent.Name))
|| _itemNameMatcher.Count > 0 && _itemNameMatcher.Any(x => x.IsMatch(renderingItem.InnerItem.Name));

int index = args.Disposables.FindIndex(x => x.GetType() == typeof(Wrapper));
if (index < 0)
{
Log.Warn($”Cannot find rendering chrome wrapper and will not insert SCORE metadata wrapper for [{rendering}]”, this);
return;
}

IMarker marker = isMetaReq ? new MetadataMarker(rendering, string.Empty) as IMarker : null;

if (marker == null) return;
args.Disposables.Insert(index, new Wrapper(args.Writer, marker));
}

}

Then create a class that implements IMarker and actually insert the HTML in wrapper.

public class MetadataMarker : IMarker
        {
            private readonly Rendering _rendering;
            private readonly string _xprIconHtml;

            public MetadataMarker(Rendering rendering, string xprIconHtml)
            {
                Assert.ArgumentNotNull(rendering, “_rendering”);

                _rendering = rendering;
                _xprIconHtml = xprIconHtml;
            }

            public virtual string GetStart()
            {
                var result = new StringBuilder();

                RenderingItem renderingItem = _rendering.RenderingItem;

                string folderName = renderingItem.InnerItem.Parent.Name;
                string componentName = renderingItem.DisplayName;
                var datasourceIcon = string.Empty;
                if (!string.IsNullOrEmpty(_rendering.DataSource))
                {
                    datasourceIcon = $”<span class=”glyphicon glyphicon-list-alt”></span>{(_rendering.Item != null ? _rendering.Item.DisplayName : string.Empty)}”;
                }

                var title = $”<span class=”panel-title”><span class=”score-pe-component-type”>{folderName}:</span>{componentName} {datasourceIcon} {_xprIconHtml}</span>”;

                result.AppendFormat(“{0}”, title);
                return result.ToString();
            }
        }

Finally, patch the new processors after Sitecore.Mvc.ExperienceEditor.Pipelines.Response.RenderRendering.AddWrapper

<mvc.renderRendering>
                <processor
                    patch:after=”processor[@type=’Sitecore.Mvc.ExperienceEditor.Pipelines.Response.RenderRendering.AddWrapper, Sitecore.Mvc.ExperienceEditor’]”
                    type=”………….AddPageEditorMetadata, ProjectName”>

                    <parentNamePatterns hint=”list:AddParentNamePattern”>
                        <pattern>(Product)|(Page Content)|(Structure)</pattern>
                    </parentNamePatterns>

                    <itemNamePatterns hint=”list:AddItemNamePattern”>
                        <pattern>(Full Width Container)|(Three Column Container)|(Three Column Large Middle)</pattern>
                    </itemNamePatterns>

                </processor>
            </mvc.renderRendering>

 

It renders something like below: