Plugin Components
Timeline plugins are Lightning Web Components that can interact with and control the Timeline component. They use the Lightning Message Service to communicate with the Timeline component and can trigger various actions like refreshing the timeline, applying filters, and responding to timeline events.
Architecture
Timeline plugins work through a bidirectional communication system:
- Outbound Events: Plugins can send publish events to the
TimelineEvent__cmessage channel - Inbound Events: Plugins can subscribe to Timeline events via the
TimelineEvent__cmessage channel - Context Sharing: Plugins can share context data with the Timeline component
Message Channel
The Timeline system uses the TimelineEvent__c Lightning Message Channel for communication between components. The message structure includes:
recordId: The record ID that the action is executed onparentId: The record ID of the record on which the timeline component is shownsetupName: The API name of the timeline setup recordcontext: A context object containing extra information for the actioneventType: The type of event being triggered
Plugin Structure
Basic Plugin Setup
A timeline plugin must extend LightningElement and implement the message channel subscription:
import {LightningElement, api, wire} from "lwc";
import {publish, subscribe, unsubscribe, MessageContext} from "lightning/messageService";
import timelineEvent from "@salesforce/messageChannel/afp__TimelineEvent__c";
export default class TimelinePlugin extends LightningElement {
@api recordId;
@api objectApiName;
@wire(MessageContext)
messageContext;
subscription = null;
connectedCallback() {
this.subscribeToMessageChannel();
this.setInitialContext();
}
disconnectedCallback() {
this.unsubscribeToMessageChannel();
}
subscribeToMessageChannel() {
if (!this.subscription) {
this.subscription = subscribe(this.messageContext, timelineEvent, (message) => this.handleMessage(message));
}
}
unsubscribeToMessageChannel() {
unsubscribe(this.subscription);
this.subscription = null;
}
handleMessage(message) {
// Handle incoming timeline events
console.log("Timeline action received:", message);
}
}
Component Metadata
The plugin component must be exposed in its metadata file:
<?xml version="1.0" encoding="UTF-8" ?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>62.0</apiVersion>
<isExposed>true</isExposed>
<masterLabel>Timeline Plugin</masterLabel>
<targets>
<target>lightning__RecordPage</target>
</targets>
</LightningComponentBundle>
Plugin Events
Outbound Events (Plugin → Timeline)
Plugins can publish messages to the Timeline component using the Lightning Message Channel:
setcontext Event
Sets context data for the plugin:
setInitialContext() {
publish(this.messageContext, timelineEvent, {
eventType: "tln__setContext",
context: {
pluginName: "ExamplePlugin",
initialized: new Date().toISOString(),
},
});
}
setfilter Event
Applies filters to the timeline:
applyFilter() {
const filterDetails = {
dateFilter: "all_time", // Options: "all_time", "last_7_days", "next_7_days", "last_30_days", "custom"
myItems: false, // Show only current user's items
minDate: null, // Start date for custom filter
maxDate: null, // End date for custom filter
allowedSetupItems: null, // Array of allowed setup item names
};
publish(this.messageContext, timelineEvent, {
eventType: "tln__setFilter",
context: filterDetails,
});
}
refresh Event
Triggers a timeline refresh:
refreshTimeline() {
publish(this.messageContext, timelineEvent, {
eventType: "tln__refresh",
});
}
Inbound Events (Timeline → Plugin)
The Timeline component publishes various events that plugins can subscribe to:
System Events (prefixed with tln__)
tln__initialized: Fired when the Timeline component is initialized
Action Events
Timeline items can trigger custom action events based on their configuration. These events contain:
eventType: The type of action (e.g., "view_record", "edit_record")recordId: The ID of the record being acted uponparentId: The ID of the parent recordsetupName: The timeline setup namecontext: Additional context data
Outbound events (Plugin → Timeline)
tln__refresh: Can be used to manually refresh the timelinetln__setFilter: Can be used to set the filter of the timelinetln__setContext: Can be used to set a context object that will be shared with actions
Filter Options
When applying filters through the tln__setFilter event, use the following message structure:
{
eventType: "tln__setFilter",
context: {
dateFilter: "custom",
myItems: false,
minDate: new Date(2025, 0, 1), // In javascript January is month 0
maxDate: null,
allowedSetupItems: ["MyProvider"],
},
}
You can use the following options:
Date Filters
"all_time": Show all timeline items regardless of date"last_7_days": Show items from the past 7 days"next_7_days": Show items for the next 7 days"last_30_days": Show items from the past 30 days"custom": Use custom date range withminDateandmaxDate
minDate and maxDate are ignored when dateFilter is not "custom".
User Filters
myItems: true: Show only items assigned to the current usermyItems: false: Show all items
Setup Item Filters
allowedSetupItems: ["Provider1", "Provider2"]: Show only items from specific providers. Use API names of TimelineSetupItems as values.allowedSetupItems: null: Show items from all providers
Example Implementation
Here's a complete example of a timeline plugin with filtering capabilities:
import {LightningElement, api, wire} from "lwc";
import {ShowToastEvent} from "lightning/platformShowToastEvent";
import {publish, subscribe, unsubscribe, MessageContext} from "lightning/messageService";
import timelineEvent from "@salesforce/messageChannel/afp__TimelineEvent__c";
export default class ExampleTimelinePlugin extends LightningElement {
@api recordId;
@api objectApiName;
@api setupName;
@wire(MessageContext)
messageContext;
subscription = null;
selectedFilter = "all";
filterOptions = [
{label: "All", value: "all"},
{label: "My Items", value: "my_items"},
{label: "Recent", value: "recent"},
];
connectedCallback() {
this.subscribeToMessageChannel();
this.setInitialContext();
}
disconnectedCallback() {
this.unsubscribeToMessageChannel();
}
subscribeToMessageChannel() {
if (!this.subscription) {
this.subscription = subscribe(this.messageContext, timelineEvent, (message) => this.handleMessage(message));
}
}
unsubscribeToMessageChannel() {
unsubscribe(this.subscription);
this.subscription = null;
}
setInitialContext() {
publish(this.messageContext, timelineEvent, {
eventType: "tln__setContext",
context: {
pluginName: "ExamplePlugin",
initialized: new Date().toISOString(),
},
});
}
handleFilterChange(event) {
this.selectedFilter = event.target.value;
this.applyFilter();
}
applyFilter() {
const filterDetails = {
dateFilter: "all_time",
myItems: false,
minDate: null,
maxDate: null,
allowedSetupItems: null,
};
if (this.selectedFilter === "my_items") {
filterDetails.myItems = true;
} else if (this.selectedFilter === "recent") {
filterDetails.dateFilter = "custom";
filterDetails.minDate = new Date(Date.now() - 864000000); // 10 days ago
}
publish(this.messageContext, timelineEvent, {
eventType: "tln__setFilter",
context: filterDetails,
});
}
refreshTimeline() {
publish(this.messageContext, timelineEvent, {
eventType: "tln__refresh",
});
this.showToast("Success", "Timeline refreshed", "success");
}
handleMessage(message) {
if (message.eventType === "tln__initialized") {
this.setInitialContext();
return;
}
console.log("Timeline action received:", message);
this.showToast("Timeline Action", `Action ${message.eventType} triggered for record ${message.recordId}`, "info");
}
showToast(title, message, variant) {
this.dispatchEvent(
new ShowToastEvent({
title,
message,
variant,
})
);
}
}
HTML Template Example
<template>
<div class="slds-card slds-var-p-around_small">
<div class="slds-card__header">
<h2 class="slds-card__header-title">Timeline Controls</h2>
</div>
<div class="slds-card__body">
<lightning-combobox
name="filter"
label="Filter Timeline"
value="{selectedFilter}"
placeholder="Select Filter"
options="{filterOptions}"
onchange="{handleFilterChange}"
>
</lightning-combobox>
<lightning-button
class="slds-var-m-top_small"
label="Refresh Timeline"
onclick="{refreshTimeline}"
variant="brand"
>
</lightning-button>
</div>
</div>
</template>
Deployment
To deploy a timeline plugin:
- Create the Lightning Web Component with the appropriate structure
- Ensure the component metadata exposes it for the Lightning Record Page
- Add the plugin component to your Lightning Record Page layout
- The plugin will automatically communicate with any Timeline components on the same page
Best Practices
- Always subscribe and unsubscribe: Use
connectedCallback()anddisconnectedCallback()to manage message channel subscriptions - Handle system events: Filter out system events (prefixed with
tln__) in your message handler - Set initial context: Since the order in which lightning web components are created on a page is not guaranteed, call
setInitialContext()inconnectedCallback()and after receivingtln__initialized. The first in case the timeline is initialized before the plugin and the second in case the plugin is initialized before the timeline.