LDS Activity Timeline, Lightning Components and Visualforce
Overview
One of the aspects of the Lightning Design System (LDS) that I particularly like is the example components, which means that I don't have to find a standard page with the feature that I want and scrape the HTML to replicate it.A useful component for a variety of purposes is the Activity Timeline, which provides a visual overview of what has happened to a record, customer, anything you can think of really, and when. This is in use in a number of places in the new Lightning Experience UI to show activities, both future and historic.
In this post I'll show how to create an activity timeline component that displays the opportunities that have been closed won for a particular account record. I'll also be using the new Winter 16 feature that allows Lightning Components to be embedded inside a Visualforce page, which saves me having to write boilerplate JavaScript to pull the account Id parameter from the URL.
Remember that Winter 16 requires My Domain before Lightning Components can be displayed.
Show me the Code!
I didn't want to create a timeline that was tightly coupled to the opportunity sObject, instead I was looking for a more generic solution that could display any kind of data. I also didn't want the component to have to know too much about the information that it was displaying - components are much more re-usable if they just traverse a data structure and output what they find.To this end, the timeline is modelled as a single Apex class, BBTimeline, with an inner class to represent an entry in the timeline. Note that the class fields are all annotated @AuraEnabled, to make them available for use the lightning component that renders them:
public class BB_LTG_Timeline { @AuraEnabled public String name {get; set;} @AuraEnabled public List<Entry> entries {get; set;} public BB_LTG_Timeline() { entries= new List<Entry>(); } public class Entry { @AuraEnabled public Date theDate {get; set;} @AuraEnabled public String description {get; set;} } } |
public class BB_LTG_AccountOppTimelineCtrl { @AuraEnabled public static BB_LTG_Timeline GetTimeline(String accIdStr) { BB_LTG_Timeline result= new BB_LTG_Timeline(); try { Id accId=(Id) accIdStr; System.debug( 'Account id = ' + accId); Account acc=[select id, Name from Account where id=:accId]; result.name=acc.Name + ' closed deals' ; List<Opportunity> opps=[select CloseDate, Amount, Type from Opportunity where AccountId=:accId and StageName= 'Closed Won' order by CloseDate desc]; for (Opportunity opp : opps) { BB_LTG_Timeline.Entry entry= new BB_LTG_Timeline.Entry(); entry.theDate=opp.CloseDate; entry.description=opp.type + ' opportunity closed for ' + opp.amount; result.entries.add(entry); } } catch (Exception e) { System.debug( 'Exception - ' + e); } return result; } } |
<aura:component controller= "BB_LTG_AccountOppTimelineCtrl" > <aura:attribute name= "recordId" type= "String" /> <aura:attribute name= "timeline" type= "BB_LTG_Timeline" /> <ltng:require styles= "/resource/BB_SLDS091/assets/styles/salesforce-lightning-design-system-ltng.css" afterScriptsLoaded= "{!c.doInit}" /> <div class = "slds" > <c:BBAccountOppTimelineHeader /> <ul class = "slds-timeline" > <p class = "slds-m-around--medium" ><a href= "#" >{!v.timeline.name}</a></p> <aura:iteration items= "{!v.timeline.entries}" var= "entry" > <li class = "slds-timeline__item" > <span class = "slds-assistive-text" >Event</span> <div class = "slds-media slds-media--reverse" > <div class = "slds-media__figure" > <div class = "slds-timeline__actions" > <button class = "slds-button slds-button--icon-border-filled" > <c:BBsvg class = "slds-icon slds-icon-standard-event slds-timeline__icon" xlinkHref= "/resource/BB_SLDS091/assets/icons/standard-sprite/svg/symbols.svg#event" /> <span class = "slds-assistive-text" >Opportunity</span> </button> <p class = "slds-timeline__date" ><ui:outputDate value= "{!entry.theDate}" /></p> </div> </div> <div class = "slds-media__body" > <div class = "slds-media slds-media--timeline slds-timeline__media--event" > <div class = "slds-media__figure" > <c:BBsvg class = "slds-icon slds-icon-standard-opportunity slds-timeline__icon" xlinkHref= "/resource/BB_SLDS091/assets/icons/standard-sprite/svg/symbols.svg#opportunity" /> </div> <div class = "slds-media__body" > <ul class = "slds-list--vertical slds-text-body--small" > <li class = "slds-list__item slds-m-right--large" > <dl class = "slds-dl--inline" > <dt class = "slds-dl--inline__label" >Description:</dt> <dd class = "slds-dl--inline__detail" ><a href= "#" >{!entry.description}</a></dd> </dl> </li> </ul> </div> </div> </div> </div> </li> </aura:iteration> </ul> </div> </aura:component> |
In order to display a Lightning Component in a Visualforce page, you need to construct a simple Lightning Application that is used as the bridge between the two technologies. This needs to have a dependency on the Lightning Component that will display the content - BBAccountOppTimeline in this case:
<aura:application access= "GLOBAL" extends = "ltng:outApp" > <aura:dependency resource= "c:BBAccountOppTimeline" /> </aura:application> |
<apex:page sidebar= "false" showHeader= "false" standardStylesheets= "false" > <apex:includeScript value= "/lightning/lightning.out.js" /> <div id= "lightning" /> <script> $Lightning.use( "c:BBAccountOppTimelineApp" , function() { $Lightning.createComponent( "c:BBAccountOppTimeline" , { "recordId" : "{!$CurrentPage.parameters.id}" }, "lightning" , function(cmp) { // any further setup goes here }); }); </script> </apex:page> |
The Results
Once all this scaffolding is in place, accessing the Visualforce page with the id of an account with at least one closed won opportunity displays a timeline of these opportunities, with the most recent at the top:Where Can I Get It
As usual, I've added this into my BBLDS samples project available on github at :https://github.com/keirbowden/BBLDS