Creating A Dynamic & Reusable Lightning Design Sales Path/Status Visual In Salesforce Visualforce

Creating A Dynamic & Reusable Lightning Design Sales Path/Status Visual In Salesforce Visualforce

Hello There! Welcome Back

So, there is this Sales Path for Opportunities in Salesforce Lightning Experience which helps get a quick peek of the Opportunity stage which is beautiful, dynamic, and advantageous.

What if we could get the same thing for other objects too, both standard and custom? It would be very helpful, right?

So today, we are gonna learn to create a Reusable & Dynamic Lightning Design Sales Path/Status Visual for our records in Salesforce visualforce. Let's get started!

Things we will be using:
1) Salesforce Lightning Design System
2) jQuery

Step 1: Gathering the resources we need.

  1. Salesforce Lightning Design System:

    1. Download the Custom Scoped CSS form here with your desired Scoping Class. (be sure to prefix your scoping class with a dot)
      The Scoping class wraps your content using the Lightning Design System to avoid CSS conflicts.
      I am using “slds” as a scoping class in this blog post.

    2. The file you download will be named “salesforce-lightning-design-system.zip”, just rename it to something convenient and short like “SLDS”.

    3. Upload the zip file as your Static Resource.

  2. jQuery:
    We will be using CDN for this here, you can always have it as a static resource. Either way is fine.
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>

Now we have all the stuff we need, lets code what we want.

Step 2: The code.

We can do this in two ways:

  1. Get the picklist values using apex and show it on the page

  2. Get the picklist values without using apex (Also Recommended for Picklists with Record Types)

Type 1: Get the picklist values using apex and show it on the page

  1. Set the Standard controller(the Object), the Extension Controller(to get the picklist values of the field you want to visualize) and doctype to html-5.0 (Visualforce Code: Line 2)

  2. We put all the resources we need into the page. (Visualforce Code: Line 4, 6)

  3. Create SLDS Sales Path Component as shown in the SLDS website here, but without the List items as we are going to add the list items to it dynamically. (Visualforce Code: Lines 9 to 20)
    To know more on getting started with The Lightning Design System you can refer my another blog post.

  4. Write code in the extension controller to get the picklist values dynamically and send it to visualforce. (The Apex Extension Controller Code Block)

  5. Use the picklist values and the current picklist value of the record and create the Sales Path and add it to the ul tag of the Sales Path component. (Visualforce Code: Lines 24 to 79)
    So, the Sales Path has 3 phases – the completed stages (Visualforce Code: Lines 37 to 44), the current stage (Visualforce Code: Lines 46 to 66), and the next stages (Visualforce Code: Lines 68 to 75). Hence, the for loop checks the current stage in all the picklist values and prints the list elements accordingly.

Visualforce Page

<!–Page docType Should Be Set To html-5.0 For Proper Rendering –>
<apex:page standardController="Case" extensions="CaseGetPicklist" docType="html-5.0">
   <!– Lightning Design System Source –>
   <apex:stylesheet value="{!URLFOR($Resource.SLDS102, ‘assets/styles/salesforce-lightning-design-system-vf.css’)}" />
   <!– jQuery CDN –>
   <script src="https://code.jquery.com/jquery-1.11.1.min.js"></script>
   <!– HTML Tag with xmlns attribute Important to Render SLDS Icons –>
   <html xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
      <div class="slds">
         <div class="slds-grid">
            <div class="slds-tabs–path" role="application">
               <!–Sales Path Content UL–>
               <ul class="slds-tabs–path__nav" role="tablist" id="TheLis">
               </ul>
            </div>
         </div>
      </div>
   </html>
   <script>
      var j$ = jQuery.noConflict();
      //Object Picklist Values From Apex
      var picArr = {!opts};
      var picArrLen = picArr.length;
      //Current Record Picklist Value
      var CurrentStg = ‘{!Case.Status}’;
      //Checking The Current Picklist Value Against All The Picklist Values
      var CurrentStgPos = picArr.indexOf(CurrentStg);
      var theiLis = ”;

      //Creating the Sale Path Content based on the Record’s Picklist Value
      for(var i=0; i<picArrLen; i++){

      // HTML Content For Completed Stages
      if(CurrentStgPos > i){
      theiLis += ‘<li class="slds-tabs–path__item slds-is-complete" role="presentation">’+
      ‘<a class="slds-tabs–path__link" id="tabs-path-1" aria-controls="content-path-1" aria-selected="false" tabindex="-1" role="tab" href="#void" aria-live="assertive">’+
      ‘<span class="slds-tabs–path__stage">’+
      ‘<svg aria-hidden="true" class="slds-icon slds-icon–x-small"><use xlink:href="{!$Resource.SLDS102}/assets/icons/utility-sprite/svg/symbols.svg#check"></use></svg></span>’+
      ‘<span class="slds-tabs–path__title">’+picArr[i]+'</span></a></li>’

      }
      // HTML Content For Current Stage
      else if(CurrentStgPos == i){
      // HTML Content If Current Stage is Last Stage
      if(i == picArrLen-1){
      theiLis += ‘<li class="slds-tabs–path__item slds-is-complete" role="presentation">’+
      ‘<a class="slds-tabs–path__link" id="tabs-path-1" aria-controls="content-path-1" aria-selected="false" tabindex="-1" role="tab" href="#void" aria-live="assertive">’+
      ‘<span class="slds-tabs–path__stage">’+
      ‘<svg aria-hidden="true" class="slds-icon slds-icon–x-small"><use xlink:href="{!$Resource.SLDS102}/assets/icons/utility-sprite/svg/symbols.svg#check"></use></svg></span>’+
      ‘<span class="slds-tabs–path__title">’+picArr[i]+'</span></a></li>’
      }
      // HTML Content If Current Stage is Not Last Stage
      else{

      theiLis += ‘<li class="slds-tabs–path__item slds-is-current" role="presentation">’+
      ‘<a class="slds-tabs–path__link" id="tabs-path-1" aria-controls="content-path-1" aria-selected="false" tabindex="-1" role="tab" href="#void" aria-live="assertive">’+
      ‘<span class="slds-tabs–path__stage">’+
      ‘<svg aria-hidden="true" class="slds-icon slds-icon–x-small"><use xlink:href="{!$Resource.SLDS102}/assets/icons/utility-sprite/svg/symbols.svg#check"></use></svg></span>’+
      ‘<span class="slds-tabs–path__title">’+picArr[i]+'</span></a></li>’

      }

      }
      // HTML Content For Next Stages
      else if(CurrentStgPos < i){

      theiLis += ‘<li class="slds-tabs–path__item slds-is-incomplete" role="presentation">’+
      ‘<a class="slds-tabs–path__link" id="tabs-path-1" aria-controls="content-path-1" aria-selected="false" tabindex="-1" role="tab" href="#void" aria-live="assertive">’+
      ‘<span class="slds-tabs–path__stage">’+
      ‘<svg aria-hidden="true" class="slds-icon slds-icon–x-small"><use xlink:href="{!$Resource.SLDS102}/assets/icons/utility-sprite/svg/symbols.svg#check"></use></svg></span>’+
      ‘<span class="slds-tabs–path__title">’+picArr[i]+'</span></a></li>’
      }
      }

      //Adding The Created Sales Path Content To Sales Path Body
      j$(‘#TheLis’).html(theiLis);

   </script>
</apex:page>

ApexClass - CaseGetPicklist

public class CaseGetPicklist {

    public List < String > options { get; set; }
    public string opts { get; set; }

    public CaseGetPicklist(ApexPages.StandardController controller) {

        options = new List < String > ();
        //Getting The Object’s Picklist’s Values
        Schema.DescribeFieldResult fieldResult = Case.Status.getDescribe();
        List < Schema.PicklistEntry > ple = fieldResult.getPicklistValues();

        //Adding the Picklist Values into An Array
        for (Schema.PicklistEntry f: ple) {
            options.add(f.getLabel());
        }
        //Stringifying The Array and Assigning It To A String
        opts = JSON.serialize(options);
    }
}

Test Class

@isTest
public class CaseGetPicklistTestCls {

    static testmethod void CaseGetPicklst() {
        Case c = New Case(Status = ‘New’, Origin = ‘Web’);
        CaseGetPicklist cp = new CaseGetPicklist(new ApexPages.StandardController(c));
    }

}

Congrats, we just created a Dynamic Lightning Design style Status Visual/Sales Path.
We created it for case but, what if we want to use it with any other object. Just dont worry, the code above is reusable & to use it with any other object we just need to change words in just 3 places

  1. Standard Controller. (Visualforce Code: Line 2)

  2. Object.Field Name in Extension Controller. (Apex Extension Controller Code: Line 10)

  3. Object.Field Name in the merge field in Visualforce Javascript. (Visualforce Code: Line 28)

That is it! And you will be good to go!
How easy & amazing, Right?

  1. Set the Standard controller(the Object) and doctype to html-5.0 (Visualforce Code: Line 2)

  2. We put all the resources we need into the page. (Visualforce Code: Line 4, 6)

  3. Create an with its value as the API name of the picklist that you want to viusalise with its id as VisualPicklist (Visualforce Code: Lines 12 to 14)
    To know more on getting started with The Lightning Design System you can refer my another blog post.

  4. Create SLDS Sales Path Component as shown in the SLDS website here, but without the List items as we are going to add the list items to it dynamically. (Visualforce Code: Lines 15 to 22)

  5. Use the picklist values and the current picklist value of the record and create the Sales Path and add it to the ul tag of the Sales Path component. (Visualforce Code: Lines 28 to 85)

So, the Sales Path has 3 phases – completed stages (Visualforce Code: Lines 43 to 50), current stage (Visualforce Code: Lines 52 to 72), and the next stages (Visualforce Code: Lines 74 to 81). Hence, the for loop checks the current stage in all the picklist values and prints the list elements accordingly.

Visualforce Page

<!–Page docType Should Be Set To html-5.0 For Proper Rendering –>
<apex:page standardController="case" docType="html-5.0">
   <!– Lightning Design System Source –>
   <apex:stylesheet value="{!URLFOR($Resource.slds, ‘assets/styles/salesforce-lightning-design-system-vf.css’)}" />
   <!– jQuery CDN –>
   <script src="https://code.jquery.com/jquery-1.11.1.min.js"></script>
   <!– HTML Tag with xmlns attribute Important to Render SLDS Icons –>
   <html xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
      <div class="slds">
         <!– Apex InputField for the Picklist you want to visualize with id as VisualPicklist –>
         <apex:form styleClass="slds-hide">
            <apex:inputField id="VisualPicklist" value="{!case.Status}"/>
         </apex:form>
         <div class="slds-grid">
            <div class="slds-tabs–path" role="application">
               <!–Sales Path Content UL–>
               <ul class="slds-tabs–path__nav" role="tablist" id="TheLis">
               </ul>
            </div>
         </div>
      </div>
   </html>
   <script>
      var j$ = jQuery.noConflict();
      //Object Values from The Picklist Input Field
      var picArr = [];
      j$(‘[id$=VisualPicklist] option’).each(function(){
      picArr.push(j$(this).text());
      });
      var picArrLen = picArr.length;
      //Current Record Picklist Value
      var CurrentStg = j$(‘[id$=VisualPicklist] option:selected’).text();
      //Checking The Current Picklist Value Against All The Picklist Values
      var CurrentStgPos = picArr.indexOf(CurrentStg);
      var theiLis = ”;
      //Creating the Sale Path Content based on the Records Picklist Value
      for(var i=0; i<picArrLen; i++){

      // HTML Content For Completed Stages
      if(CurrentStgPos > i){
      theiLis += ‘<li class="slds-tabs–path__item slds-is-complete" role="presentation">’+
      ‘<a class="slds-tabs–path__link" id="tabs-path-1" aria-controls="content-path-1" aria-selected="false" tabindex="-1" role="tab" href="#void" aria-live="assertive">’+
      ‘<span class="slds-tabs–path__stage">’+
      ‘<svg aria-hidden="true" class="slds-icon slds-icon–x-small"><use xlink:href="{!$Resource.slds}/assets/icons/utility-sprite/svg/symbols.svg#check"></use></svg></span>’+
      ‘<span class="slds-tabs–path__title">’+picArr[i]+'</span></a></li>’

      }
      // HTML Content For Current Stage
      else if(CurrentStgPos == i){
      // HTML Content If Current Stage is Last Stage
      if(i == picArrLen-1){
      theiLis += ‘<li class="slds-tabs–path__item slds-is-complete" role="presentation">’+
      ‘<a class="slds-tabs–path__link" id="tabs-path-1" aria-controls="content-path-1" aria-selected="false" tabindex="-1" role="tab" href="#void" aria-live="assertive">’+
      ‘<span class="slds-tabs–path__stage">’+
      ‘<svg aria-hidden="true" class="slds-icon slds-icon–x-small"><use xlink:href="{!$Resource.slds}/assets/icons/utility-sprite/svg/symbols.svg#check"></use></svg></span>’+
      ‘<span class="slds-tabs–path__title">’+picArr[i]+'</span></a></li>’
      }
      // HTML Content If Current Stage is Not Last Stage
      else{

      theiLis += ‘<li class="slds-tabs–path__item slds-is-current" role="presentation">’+
      ‘<a class="slds-tabs–path__link" id="tabs-path-1" aria-controls="content-path-1" aria-selected="false" tabindex="-1" role="tab" href="#void" aria-live="assertive">’+
      ‘<span class="slds-tabs–path__stage">’+
      ‘<svg aria-hidden="true" class="slds-icon slds-icon–x-small"><use xlink:href="{!$Resource.slds}/assets/icons/utility-sprite/svg/symbols.svg#check"></use></svg></span>’+
      ‘<span class="slds-tabs–path__title">’+picArr[i]+'</span></a></li>’

      }

      }
      // HTML Content For Next Stages
      else if(CurrentStgPos < i){

      theiLis += ‘<li class="slds-tabs–path__item slds-is-incomplete" role="presentation">’+
      ‘<a class="slds-tabs–path__link" id="tabs-path-1" aria-controls="content-path-1" aria-selected="false" tabindex="-1" role="tab" href="#void" aria-live="assertive">’+
      ‘<span class="slds-tabs–path__stage">’+
      ‘<svg aria-hidden="true" class="slds-icon slds-icon–x-small"><use xlink:href="{!$Resource.slds}/assets/icons/utility-sprite/svg/symbols.svg#check"></use></svg></span>’+
      ‘<span class="slds-tabs–path__title">’+picArr[i]+'</span></a></li>’
      }
      }

      //Adding The Created Sales Path Content To Sales Path Body
      j$(‘#TheLis’).html(theiLis);
   </script>
</apex:page>

Congrats, we just created a Dynamic Lightning Design style Status Visual/Sales Path.
We created it for case but, what if we want to use it with any other object. Just dont worry, the code above is reusable & to use it with any other object we just need to change words in just 2 places

  1. Standard Controller of The Page – Name of The Object (Visualforce Code: Line 2)

  2. Object.Field as value in the Apex:InputField. (Visualforce Code: Line 13)

That is it! And you will be good to go! How easy & Amazing, Right?

Save the Page & Controller and Add the page to the Record Detail Page Layout.
If you like the content, give it some sharing :)