Implement Tab Change Events In Embedded Dashboards
Overview
This article discusses the implementation of tab change event publishing for embedded dashboards. This feature allows parent applications to detect when users switch between dashboard tabs, providing valuable insights into user behavior and enabling enhanced integration capabilities. By implementing this feature, we aim to provide developers with the tools they need to create more engaging and dynamic user experiences.
Background
Embedding dashboards into external applications is a powerful way to provide users with data-driven insights within their existing workflows. However, to fully leverage embedded dashboards, it's crucial to understand how users interact with them. Embedding customers need to know when tab changes occur for several key reasons:
- Update External Navigation or Breadcrumbs: When a user switches tabs within an embedded dashboard, the parent application may need to update its navigation or breadcrumbs to reflect the user's current context. This ensures a seamless and consistent user experience across the entire application.
- Track User Engagement Across Different Dashboard Sections: Understanding which tabs users are engaging with the most can provide valuable insights into their interests and priorities. This information can be used to optimize dashboard design, content, and functionality.
- Sync Tab State with Their Application Routing: By tracking tab changes, parent applications can synchronize the dashboard state with their own routing mechanisms. This allows users to easily navigate back to specific tabs or share links to specific dashboard views.
- Show/Hide Relevant Contextual Information Based on Active Tab: Different tabs may contain different types of data or visualizations. By knowing which tab is active, parent applications can display relevant contextual information or tools to the user, enhancing their ability to analyze and interpret the data.
- Analytics Tracking of Tab Usage Patterns: Monitoring tab usage patterns can help identify trends and areas for improvement. This data can be used to optimize dashboard performance, content, and user experience.
To effectively address these needs, implementing tab change event publishing is essential. This will provide parent applications with the necessary information to create more integrated and user-friendly experiences.
Technical Details
To implement tab change event publishing, we need to modify the existing dashboard component to emit events whenever a user switches between tabs. This involves updating the tab change handler and defining the structure of the event payload.
Files to Modify
The primary file that needs modification is:
packages/frontend/src/ee/features/embed/EmbedDashboard/components/EmbedDashboard.tsx
- Modify the tab change handler (around line 329-334)
- Add event emission when the active tab changes
- Include previous tab information for context
This file contains the logic for rendering the dashboard and handling tab changes. We will need to update this file to emit events whenever the active tab changes.
Current Code Context
The existing tab change logic in EmbedDashboard.tsx
looks like this:
// Lines 329-334
onTabChange={(e) => {
const tab = sortedTabs.find((t) => t.uuid === e);
if (tab) {
setActiveTab(tab);
}
}}
This code snippet simply updates the active tab state when a user clicks on a new tab. We need to enhance this logic to emit an event whenever the active tab changes.
Implementation Requirements
To implement tab change event publishing, we need to:
- Enhance the tab change handler to emit an event.
- Define the structure of the event payload.
- Handle edge cases such as initial tab selection and no previous tab.
Tab Change Event Logic
The following code snippet demonstrates how to enhance the tab change handler to emit an event:
// In EmbedDashboard.tsx
import { useLocation } from 'react-router-dom';
import { LightdashUiEvent, LightdashEventType } from '../events/LightdashUiEvent';
const EmbedDashboard: FC<Props> = ({ containerStyles }) => {
const { pathname } = useLocation();
// ... existing code
// Enhanced tab change handler
const handleTabChange = useCallback((tabUuid: string) => {
const tab = sortedTabs.find((t) => t.uuid === tabUuid);
if (tab && dashboard && projectUuid) {
const previousTab = activeTab;
// Update state first
setActiveTab(tab);
// Then emit event
LightdashUiEvent.dispatch(LightdashEventType.TabChanged, {
tabUuid: tab.uuid,
tabName: tab.name,
tabIndex: sortedTabs.indexOf(tab),
previousTabUuid: previousTab?.uuid,
previousTabName: previousTab?.name,
dashboardUuid: dashboard.uuid,
projectUuid,
timestamp: Date.now()
}, pathname);
}
}, [sortedTabs, activeTab, dashboard?.uuid, projectUuid, pathname]);
// Use in the Tabs component
<
value={activeTab?.uuid}
onTabChange={handleTabChange}
// ... other props
>
};
This code snippet introduces the handleTabChange
function, which is called whenever a user clicks on a new tab. This function first updates the active tab state and then emits a TabChanged
event using the LightdashUiEvent.dispatch
method.
Event Payload Structure
The TabChanged
event will include the following information:
interface TabChangedDetail {
tabUuid: string; // UUID of the newly selected tab
tabName: string; // Display name of the tab
tabIndex: number; // Zero-based index in sorted tab order
previousTabUuid?: string; // UUID of previously selected tab (if any)
previousTabName?: string; // Display name of previous tab (if any)
dashboardUuid: string; // Dashboard identifier
projectUuid: string; // Project identifier
timestamp: number; // When the change occurred
}
This payload provides comprehensive information about the tab change, including the UUID, name, and index of the new tab, as well as the UUID and name of the previous tab (if any). It also includes the dashboard and project UUIDs, as well as a timestamp of when the change occurred.
Edge Cases to Handle
We need to consider several edge cases when implementing tab change event publishing:
- Initial Tab Selection: The first tab selection on dashboard load should emit an event.
- No Previous Tab: We need to handle the case where no tab was previously selected.
- Same Tab Click: We need to decide whether to emit an event if the user clicks on the current tab.
- Programmatic Changes: We need to distinguish between user clicks and programmatic tab changes.
- Tab Order Changes: We need to handle dynamic tab reordering.
These edge cases need to be carefully considered to ensure that the tab change event publishing mechanism works correctly in all scenarios.
Current Dashboard Tab Structure
Based on the existing code:
- Tabs are stored in
dashboard.tabs
- Sorted by
order
property:sortedTabs = dashboard.tabs.sort((a, b) => a.order - b.order)
- Active tab tracked in
activeTab
state - Tab properties include:
uuid
,name
,order
This information is important for understanding how the tab change event publishing mechanism interacts with the existing dashboard structure.
Usage Examples
To illustrate how tab change events can be used, here are a couple of examples:
Iframe Embedding
window.addEventListener('message', (event) => {
if (event.data.source === 'lightdash' &&
event.data.type === 'lightdash:dashboard:tabChanged') {
const {
tabName,
tabIndex,
previousTabName,
dashboardUuid
} = event.data.detail;
console.log(`Tab changed from "${previousTabName}" to "${tabName}"`);
console.log(`Now on tab ${tabIndex + 1}`);
// Update parent app navigation
updateBreadcrumb(`Dashboard > ${tabName}`);
// Track analytics
analytics.track('dashboard_tab_changed', {
dashboard_id: dashboardUuid,
tab_name: tabName,
tab_index: tabIndex
});
}
});
This example demonstrates how to listen for tab change events in an iframe and use the event data to update the parent application's navigation, track analytics, and more.
React SDK
import { Dashboard } from '@lightdash/sdk';
import type { TabChangedDetail } from '@lightdash/sdk';
<
token={token}
instanceUrl={url}
onTabChange={(detail: TabChangedDetail) => {
console.log(`Switched to tab: ${detail.tabName}`);
// Update application state
setCurrentTab(detail.tabName);
// Update URL or navigation
router.push(`/analytics/${detail.tabUuid}`);
// Show contextual help for specific tabs
if (detail.tabName === 'Revenue Analysis') {
showRevenueHelp();
}
}}
/>
This example shows how to use the React SDK to listen for tab change events and use the event data to update the application state, navigation, and display contextual help.
Integration with Existing Tab Logic
The implementation needs to work with:
- Tab State Management (lines 214-229):
activeTab
statesetActiveTab
function- Initial tab selection logic
- Tab Filtering (lines 232-260):
filteredTiles
based on active tab- Tab-specific tile visibility
- Tab Rendering (lines 326-379):
- Mantine
Tabs
component - Tab list and panels
- Tab styling logic
- Mantine
It's crucial to ensure that the new tab change event publishing mechanism integrates seamlessly with the existing tab logic and doesn't introduce any conflicts or regressions.
Testing Requirements
To ensure the quality and reliability of the tab change event publishing mechanism, we need to implement thorough testing.
- Unit Tests
- Tab change event emission
- Event payload validation
- Previous tab tracking
- Edge cases (no previous tab, same tab click)
- Integration Tests
- Tab state synchronization
- Multiple tab changes in sequence
- Tab change with filter interactions
- E2E Tests
- User clicking tabs triggers events
- Parent application receiving events
- Tab navigation in multi-tab dashboards
Comprehensive testing is essential to ensure that the tab change event publishing mechanism works correctly in all scenarios and doesn't introduce any regressions.
Current Code References
EmbedDashboard.tsx:214-229
Tab state management and initial selection:
const [activeTab, setActiveTab] = useState<DashboardTab | undefined>();
// Set active tab to first tab if no active tab is set
useEffect(() => {
if (sortedTabs.length > 0 && !activeTab) {
setActiveTab(sortedTabs[0]);
}
}, [sortedTabs, activeTab]);
EmbedDashboard.tsx:326-334
Current tab change handler that needs enhancement:
<
value={activeTab?.uuid}
onTabChange={(e) => {
const tab = sortedTabs.find((t) => t.uuid === e);
if (tab) {
setActiveTab(tab);
}
}}
// ...
>
Definition of Done
The following criteria must be met for the tab change event publishing mechanism to be considered complete:
- [x] Tab change events emit when users switch tabs
- [x] Events include complete tab context (current and previous)
- [x] Initial tab selection triggers event
- [x] Tab index information included for ordering context
- [x] Events work for both iframe and React SDK scenarios
- [x] Unit tests cover all tab change scenarios
- [x] Integration tests verify event timing and payload
- [x] Documentation includes usage examples
- [x] No impact on existing tab functionality
Dependencies
- Prerequisite: Phase 1 (Core Event System) must be completed
- Imports: React Router for pathname detection
- Context: Dashboard and project UUID from dashboard context
Related Issues
- Depends on: Phase 1 Core Event System
- Relates to: Phase 6 Events SDK Module (for iframe usage)
- May relate to: Filter events (tabs may reset filters)
This comprehensive guide outlines the implementation of tab change event publishing for embedded dashboards. By following these guidelines, developers can create more engaging and dynamic user experiences by leveraging the power of tab change events. This enhancement will enable a deeper understanding of user interactions within dashboards, ultimately leading to more informed decision-making and improved user satisfaction.