Creating a breakout custom attribute editor in Page Designer for Salesforce Commerce Cloud (SFCC)

By Srdjan Djukić
29/05/2020

How to implement a custom attribute editor in Salesforce Commerce Cloud? This is what we will focus on in this article.

 

The next logical question would be “Why do I need to implement a custom attribute editor in SFCC?”. Well the answer is quite simple. If your storefront website needs a component which doesn’t have a predefined Page Designer type, implementing a custom attribute editor is the way to go.

 

In most cases, the requirements for a custom attribute editor include a UI that doesn’t have any room on the standard overlay that you have on the right side of the Page Designer when picking a component. This is why I will try to explain how to make a Breakout type custom editor. Breakout type in this case will be a popup that comes out in a separate window inside PD.

 

PREREQUISITES:

 

1.  Page Designer enabled in your storefront Business Manager (described in the first blog about Page Designer)

2.  Setup workspace in VSCode or Eclipse

 

Implementing a breakout editor involves these high-level steps:

 

1.  Create server-side meta definition and script files for the trigger editor and the breakout editor

2.  In the script file for the trigger editor, define the breakout editor as a dependency

3.  Develop client-side UI code that implements the trigger and breakout editors, opens and closes the breakout modal window, and passes a value from the breakout editor to the trigger editor to Page Designer when the merchant clicks Apply

Anatomy

As in the previous blog, it’s important to keep the project hierarchy clean, so that you have a better overview of the entire project but also your key files. Make sure it looks like this, especially the js and css files for the editors, as Page Designer will search for them only in the static folder of your cartridge.

 

bee-it-blog

The difference between productBannerStrip.json file in this blog and the previous one is the definition of your trigger editor. Keep in mind that he will be the one instantiating the Breakout Editor of Page Designer.

        
        

{
        "id": "customCarEditor",
          "name": "Custom Car Editor",
          "type": "custom",
          "required": true,
          "editor_definition": {
              "type": "com.sfcc.customCarEditorTrigger",
              "configuration": {
                  "options": {
                      "config": [
                          "Yugo",
                          "Golf",
                          "Mercedes"
                      ]
                  }
              }
          }
      }

Add this snippet of the code to the above mentioned file in attribute_definitions array.

Server-side meta definition and script files

Let’s start with custom trigger editor definition and script files.

We need a customCarEditorTrigger.json

        
        

{
    "name": "Car Custom Editor",
    "description": "Custom editor with car picker breakout functionality!",
    "resources": {
        "scripts": [
            "https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.0/jquery.min.js",
            "/experience/editors/com/sfcc/customCarEditorTriggerScript.js"
        ],
        "styles": [     "https://cdnjs.cloudflare.com/ajax/libs/design-system/2.8.3/styles/salesforce-lightning-design-system.min.css",
            "/experience/editors/com/sfcc/customCarEditorTriggerCSS.css"
        ]
    }
}

scripts array parameter represents included jQuery and a js script for the client side of your editor. Make sure that the js file is inside the static folder of your cartridge.

styles array parameter represents the default css by the sfcc and a customized css file, which also needs to be put inside the static folder of your cartridge.

 

Next we need a customCarEditorTrigger.js. This is the server side script for your custom trigger.

        
        

'use strict';

var HashMap = require('dw/util/HashMap');
var Resource = require('dw/web/Resource');
var PageMgr = require('dw/experience/PageMgr');

module.exports.init = function (editor) {
    // Default values properties
    var defaults = {
        buttonBreakout: 'Select',
        titleBreakout: 'Cars',
        placeholder: 'Select your custom car',
        description: 'Description of Custom Car Editor',
        group1: 'car group1 configuration'
    };

    // Add some localizations
    var localization = Object.keys(defaults).reduce(function (acc, key) {
        acc.put(key, Resource.msg(key, 'experience.editors.com.sfcc.customCarEditorTrigger', defaults[key]));
        return acc;
    }, new HashMap());
    editor.configuration.put('localization', localization);

    // Pass through property `options.config` from the `attribute_definition` to be used in a breakout editor
    var options = new HashMap();
    options.put('config', editor.configuration.options.config);

    // Create a configuration for a custom editor to be displayed in a modal breakout dialog (breakout editor)
    var breakoutEditorConfig = new HashMap();
    breakoutEditorConfig.put('localization', localization);
    breakoutEditorConfig.put('options', options);

    // Add a dependency to the configured breakout editor
    var breakoutEditor = PageMgr.getCustomEditor('com.sfcc.customCarEditorBreakout', breakoutEditorConfig);
    editor.dependencies.put('customCarEditorBreakoutScript', breakoutEditor);
};

Above each piece of code I have added comments which could be helpful in understanding how the entire solution works.

 

Now we move on to the breakout part of editor server-side script and meta definition. Again, we first start with meta description.

customCarEditorBreakout.json

        
        

{
    "name": "Custom Car Editor Breakout",
    "description": "An car editor with breakout functionality",
    "resources": {
        "scripts": [
            "https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.0/jquery.min.js",
            "/experience/editors/com/sfcc/customCarEditorBreakoutScript.js"
        ],
        "styles":[
"https://cdnjs.cloudflare.com/ajax/libs/design-system/2.9.4/styles/salesforce-lightning-design-system.min.css",
            "/experience/editors/com/sfcc/customCarEditorBreakoutCSS.css"
        ]
    }
}

The same as in trigger editor, scripts file and style file need to be placed in the static folder of your cartridge.

To finish up the meta file we need a script, as always :) Go ahead and add customCarEditorBreakout.js

        
        

'use strict'; 
var URLUtils = require('dw/web/URLUtils');

module.exports.init = function (editor) {
    var optionsConfig = editor.configuration.options.config;
    var optionsInit = [
        'Yugo',
        'Fiat'
    ];

    // Init editor configuration
    editor.configuration.options.put('init', optionsInit);

    // Provide `baseUrl` to the static assets/content
    editor.configuration.put('baseUrl', URLUtils.staticURL('/experience/editors/com/sfcc/').https().toString());
};

Before we move on to client side scripts and styles, make sure that you have added your app cartridge to the Business Manager cartridge path. Otherwise, Page Designer won’t be able to see your config and script files.

 

Starting with trigger editor client side script:

 

Once the editor is ready, a `sfcc:breakout` event is sent. Usually this happens when a user clicks on a certain <button> or interacts with the trigger editor in some way.

 

When the breakout is closed (either programmatically, or via the built-in action <button>s), the callback is invoked, containing information about the "type" of the action that caused the invocation.

 

A breakout can be closed with the intent to either "Cancel" or "Apply" an editing process. The intent can be derived from the "type" information that comes as a part of the callback response (either `sfcc:breakoutCancel` or `sfcc:breakoutApply`).

 

In case of an "Apply" intent will also include a "value" available as a part of the response, reflecting the value that was last set via `sfcc:value` in the breakout editor.

 

handleBreakoutOpen() - The user interacted with the <button> and the triggering editor requests that Page Designer opens a breakout on its behalf using an editor ID. This ID refers to the key under which an editor has been put into the "dependencies" key-value store of the triggering editor's configuration. A callback is provided along with the event emission to be invoked once the breakout editor gets closed.

 

customCarEditorTriggerScript.js

        
        

// Code in the client-side JavaScript file for the trigger editor
(() => {
    subscribe('sfcc:ready', () => {

        var template = "<div><p>Open car picker editor</p> <button  id='breakout-trigger'>Open Car Picker</button></div>";
        $.append(template);
        var openButtonEl = $('#breakout-trigger');
        openButtonEl.on('click', handleBreakoutOpen);
    });

    function handleBreakoutOpen() {
        emit({
            type: 'sfcc:breakout',
            payload: {
                id: 'customCarEditorBreakoutScript',
                title: 'The title to be displayed in the modal'
            }
        }, handleBreakoutClose);
    }

    function handleBreakoutClose({ type, value }) {
        // Now the "value" can be passed back to Page Designer
        if (type === 'sfcc:breakoutApply') {
            handleBreakoutApply(value);
        } else {
            handleBreakoutCancel();
        }
    }

    function handleBreakoutCancel() {
        // left empty in case you want to do more customization on this event
    }

    function handleBreakoutApply(value) {
        // Emit value update to Page Designer host application
        emit({
            type: 'sfcc:value',
            payload: value
        });
    }
})();

Now for the breakout part we have a customCarEditorBreakoutScript.js

        
        

// Code in the client-side JavaScript file for the breakout editor
(() => {
    subscribe('sfcc:ready', () => {
        // Once the breakout editor is ready, the custom code is able to select or 
        // Create a value. Any meaningful change to a value/selection needs to be 
        // reflected back into the host container via a `sfcc:value` event.
        const template = document.createElement('template');
        template.innerHTML = "<div><p>Yugo</p><p>Mercedes</p><p>BMW</p><p>Seat</p></div>";
        const clone = document.importNode(template.content, true);
        document.body.appendChild(clone);
        const openButtonEl = document.querySelector('p');
        openButtonEl.addEventListener('click', handleSelect);
    });

    function handleSelect({ target }) {
        // The value changed and the breakout editor's host is informed about the
        // value update via a `sfcc:value` event.
        const selectedValue = target.innerText;
        emit({
            type: 'sfcc:value',
            payload: selectedValue ? { value: selectedValue } : null
        });
    }
})();

Since I’ve mentioned a lot of events that go between the JS files of breakout and trigger, it’s only fair to explain what those Event Types actually mean.

 

Each custom attribute editor is wrapped in a host component that contains an HTML iframe element. The iframe encapsulates the code and the styling of the editor and represents a self-contained sandbox environment in which the editor runs so that different custom attribute editors on the same page don't interfere with each other. The host and the custom attribute editor in the iframe communicate by passing events back and forth on a special messaging channel.

 

Two main methods are in charge of communicating in this way.

 

subscribe() - Subscribes to events sent from the host to the editor. You can subscribe for a certain event type and when that event is sent from the host, a callback that uses a payload and optional context is invoked.

 

emit() - Sends events from the editor to the host.

 

Having this in mind, let’s split Host’s events and custom attribute editor’s events.

 

Host’s Events

 

sfcc:ready - The custom attribute editor is initialized. All the scripts and styles have been loaded into the editor's environment. When the host emits this event, it doesn't necessarily mean that all synchronously loaded assets and code have finished loading. For some components, you might need to manually listen to browser events, such as load or DOMContentLoaded, to get more information. The sfcc:ready event includes information required to display the editor, such as initial value, configuration data, data locale, display locale, and initial validity.

 

sfcc:value -The value of the attribute. Not sent on the initial load. Sent only when the value changes because of external modifications.

 

sfcc:required - Indicates whether this attribute is required. Not sent on the initial load. Sent only when the required status changes after the initialization ready phase. The custom attribute editor might use this information to display certain styling or indicators in the editor.

 

sfcc:disabled - Indicates whether this attribute is disabled. Not sent on the initial load. Sent only when the disabled status changes after the initialization ready phase. The custom attribute editor might use this information to render elements differently or display certain styling or indicators in the editor.

 

sfcc:valid - Indicates whether the value of the attribute is valid. Not sent on the initial load. Sent only when the validity status changes after the initialization ready phase.

 

Events Emitted by the Custom Attribute Editor

 

sfcc:value - The value of the attribute. Sent when the value changes inside the editor. Page Designer requires that the value is a plain JavaScript object.

 

sfcc:valid - Indicates whether the value of the attribute is valid. Can include an error message.

 

sfcc:interacted - Indicates that the user has interacted with the custom attribute editor. The editor is implicitly marked as interacted when it is blurred. For example, when the editor's contentWindow loses focus. Page Designer supports an interacted (or touched) state for form elements. This state marks a field that a user has already interacted with, for example, by tabbing through it. Being able to mark a field as touched allows for error messages in forms to be hidden initially and only displayed for fields with which a user has interacted.

Ok, let's see what our custom trigger editor looks now and what it does:

 

bee-it-blog-post

 

By opening your breakout editor you should be able to see the popup :

 

bee-it-blog-post

 

Now, by selecting one of the cars, and clicking on the Apply button, your breakout should close. In code the return value is a printed out JSON of an object you selected, so that it makes more sense to you.

 

Be mindful that this is a simple JS with a simple example of picking value, so you can make your trigger and breakout as complex as your JS/CSS skills allow you to.

 

FINAL WORDS

 

Page Designer is a powerful tool that allows you to implement the most advanced solution that will meet your customer needs. It gives you freedom regarding encapsulation because each custom editor works like a standalone unit, offering you enclosed parts of code which communicate with each other really effectively. Once you implement a Custom Attribute Editor with all the various possibilities (which are requirements from the client), you will make your content manager’s life so much easier. To wrap this up, working with Page Designer Custom Components helped me a lot with the development. But, then again, don't rush into things. If you Keep them clean and organized, your content will improve significantly.


Author

About the author

Srdjan Djukić

Salesforce Commerce Cloud developer

interesting read? Share it!

Latest blog posts

Handling Custom Shipping and Payment Methods in SFCC

You must have had shoppers that abandon their carts at the payment part of the checkout flow. Did it happen because your payment or shipping options didn’t suit them? To increase your conversion rates, you should consider adding new custom payment or shipping options that are not included in the base options of your site. In this guide, you will learn how to add a new custom payment and shipping methods to your Salesforce Commerce Cloud site. We will also show you how you can configure your custom shipping methods to suit your needs perfectly. Read More...

2020 – A Year of Adversity, Adaptation and Learning

This strange year is nearly reaching its end, so it’s a great time to look back at what we have accomplished, but above all learned during 2020. The year has been full of uncertainty and adversity for the entire world, but it is often hard times that offer the biggest lessons and opportunities for growth. Our company’s core values which are shared by all our employees are the foundation of our success, and this year has made us recognize and appreciate some of them even more. Read More...

Getting started with controllers, models and decorators (SFCC)

Controllers were introduced as an update of pipelines and, eventually they replaced them completely. They are one of the most important parts of the Salesforce Commerce Cloud project because they control the flow of data in our application. On the other hand, models fetch data from a server and provide it as a JSON object that will be used to render a page. In this article we will show you how you can build on your existing RefArch site (or SFRA) to add a new functionality with controllers and models. Read More...

Using the full potential of Salesforce Commerce Cloud (SFCC) SEO functionality

In today’s world, it is increasingly important to do search engine optimization on your website because nearly half of customers start their shopping using a search engine. If we want our business to be successful online it is vital to use all available resources that Commerce Cloud offers to optimize your website for search engines. Based on SEO that we can manage, we can divide it into two categories: On-Page SEO and Crawlability. On-Page SEO refers to the keyword strategy that we will apply on on-site content across different pages of our website. It includes storefront URLs, titles, descriptions, H1 tags, Image Alt tags, and other on-site content. Another category we must pay attention to is crawlability, which refers to the ability of search engines to find and index pages on our website. It includes the following elements: sitemaps, canonical tags, robot.txt, redirects, error handling, and 404 pages. Let’s start exploring all that we are offered in Merchant Tools under SEO. Read More...

The Basics of Salesforce Commerce Cloud SEO

Before we start going into basics and best practices of SEO on your Salesforce site, you should also understand URL Request Analyzer. This tool can show you what URL requests are being processed on your storefront so you can see what configuration is being used to open that page. This can help us understand some parts of SEO we are still not familiar with and let us reach our goal faster. Basics of every page: title and description.... Read More...

Salesforce Commerce Cloud OCAPI and Hooks

In this blog, I will give an overview of what hooks actually are, and how they can help in developing great features. Hooks help you a lot when you are in need of dynamic functionality that will execute only at certain times, and only when it&#39;s crucial for your software solution to do so. Hooks listen to certain events in the shop or in the data layer of your storefront. Which events will the hooks listen to is totally up to the development plan and customer needs. Read More...

Creating a breakout custom attribute editor in Page Designer for Salesforce Commerce Cloud (SFCC)

How to implement a custom attribute editor in Salesforce Commerce Cloud? This is what we will focus on in this article. The next logical question would be “Why do I need to implement a custom attribute editor in SFCC?”. Well the answer is quite simple. If your storefront website needs a component which doesn’t have a predefined Page Designer type, implementing a custom attribute editor is the way to go. Read More...