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

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...

My journey from starting at Bee IT to getting certified as a Salesforce Commerce Developer

In September 2019, after graduating from the Faculty of technical sciences in Novi Sad I was at the crossroads in my life. I wanted to be independent and get a job as a developer but it was difficult since I only had freelance experience in that field. Due to the lack of experience, willingness to see how a professional workspace operates, and because I wanted to put certain things in practice I decided to apply for an internship at BeeIT. Read More...

Coronavirus: Why Do People Act in Undisciplined Manner?

I'm pretty sure one of the most googled words this year is a coronavirus. And, there is a good reason why this is a hot topic. The current situation is very unstable and dangerous for most people. It affects individuals, groups and communities- the entire world is in crisis. Despite the real and objective danger we are all exposed to, I have noticed that many people don’t follow the advice of the competent authorities. Is it a lack of information? I wouldn't say so, because the media and the general public are constantly talking about one thing- Coronavirus. You can hear often about safety measures and tips on how to protect yourself in this moment of crisis. However, some people still behave in an undisciplined and irresponsible manner. They look at people with masks and gloves with a sneer refusing to maintain the recommended social distance, Read More...

Customizing Page Designer for Salesforce Commerce Cloud (SFCC)

Whether your customers are using SiteGenesis or RefArch, PD is there to help you reduce the developer dependency, improve the speed of the market and significantly lower the Quality Assurance time. If your storefront has a need for marketing content, heavy pages PD can help elevate your productivity by focusing on the key concepts and rules by which the designed pages will work. With PD you can create and manage pages, drag & drop content, preview the current state your page is in, create reusable components which will skyrocket the marketing level of your storefront Read More...

Internships at Bee IT Hive: What to Expect When You Apply for Internship at Bee IT

Our team is small but carefully selected. Currently our Bee IT hive has 34 members and, as our CEO and Co-Founder Zoran Tovarloža said in his recent blog post about Bee IT’s New Year’s resolutions, we do not plan to stop growing. Read More...

Forms and Validations

Forms and validations are playing a big role in any project. We want to prevent invalid data inputs and teach users to enter data that is appropriate for certain fields. Read More...