@edusight/notification-widget
v1.0.46
Published
React notification center widget for EduSight Notification Service, aligned with Novu's React UI and functionalities
Maintainers
Readme
EduSight Notification Widget
A modern, accessible React notification center widget built with clean architecture principles, featuring real-time updates, comprehensive notification management, and SDK dependency injection.
Version 1.0.35 Features
- Refactored Architecture - Clean architecture with dependency injection and separation of concerns
- SDK Integration - Direct integration with @edusight/notification-sdk using dependency injection pattern
- Performance Optimized - Code splitting, memoization, and optimized bundle size (<25KB gzipped)
- Enhanced Accessibility - WCAG 2.1 AA compliance with improved keyboard navigation
- Dynamic Theming - Runtime theme switching with dark mode support
- Responsive Design - Mobile-first approach with responsive breakpoints
- Error Boundaries - Comprehensive error handling with fallback components
- Real-time Sync - WebSocket integration with fallback polling and cross-tab synchronization
- Comprehensive Testing - Unit, integration, and accessibility tests with >90% coverage
Features
- Real-time Updates - WebSocket integration with automatic reconnection and fallback polling
- Responsive Design - Works seamlessly on desktop, tablet, and mobile
- Full CRUD Operations - Mark as read/unread, archive, delete with optimistic updates
- Customizable UI - Dynamic theme support with CSS variables
- SDK Integration - Direct integration with @edusight/notification-sdk using dependency injection
- Accessibility - Full WCAG 2.1 compliance with keyboard navigation
- Performance - Lazy loading, memoization, and optimized re-renders
- Multi-tenant - Built-in tenant and environment scoping
- Error Boundaries - Component-level error boundaries with fallback UI
- Live Preferences - Real-time preference management with debounced saving
- Cross-tab Sync - Synchronization across multiple browser tabs
- Clean Architecture - Separation of concerns with dependency injection pattern
Installation
npm install @edusight/notification-widgetPeer Dependencies
The widget requires React 18+ as a peer dependency:
npm install react@^18.2.0 react-dom@^18.2.0Styles
Import the widget styles in your application:
import '@edusight/notification-widget/styles';
// or
import '@edusight/notification-widget/styles.css';Custom Selectors
Each widget section exposes mx-widget-* hooks so host apps can override colors or spacing without touching the bundled CSS:
| Attribute | Target |
| --- | --- |
| data-mx-widget="root" | Widget shell/bell container |
| data-mx-widget="popover" | Notifications popover root |
| data-mx-widget="tabs" | Tab navigation bar |
| data-mx-widget="item" | Individual notification item |
| data-mx-widget="action" | Primary/secondary action buttons |
| data-mx-widget="bell" | Bell button itself |
Use these selectors in your global CSS to align the widget with your design system (for example .mx-widget-root { background-color: #fff; }). No need to import the widget CSS if you only rely on the shared CSS variables.
Quick Start
Basic Setup with NotificationWidget
The main component that provides a complete notification center experience:
import { NotificationWidget } from '@edusight/notification-widget';
import '@edusight/notification-widget/styles';
function App() {
return (
<NotificationWidget
sdkConfig={{
apiKey: 'your-api-key',
baseUrl: 'https://api.example.com',
subscriberId: 'user-123',
tenantId: 'your-tenant',
environmentId: 'production',
environment: 'production',
}}
position="right"
size="medium"
theme="light"
onError={(error) => console.error('Widget error:', error)}
/>
);
}Development Setup
For development, you can use the playground environment:
cd playground
npm install
npm run devThe playground provides a complete testing environment with:
- Mock data for offline development
- Real-time WebSocket simulation
- All widget features and configurations
- Debug logging and status monitoring
Core Concepts
NotificationWidget
The main widget component that provides a complete notification center with SDK dependency injection:
import { NotificationWidget } from '@edusight/notification-widget';
<NotificationWidget
sdkConfig={{
apiKey: 'your-api-key',
baseUrl: 'https://api.example.com',
subscriberId: 'user-123',
websocketUrl: 'wss://api.example.com',
environment: 'production',
}}
position="right"
size="medium"
theme="light"
className="custom-widget-class"
onError={(error) => console.error('Widget error:', error)}
/>Props:
sdkConfig:SDKConfiguration- SDK configuration for dependency injectionposition:'left' | 'right'- Popover position relative to bell (default: 'right')size:'small' | 'medium' | 'large'- Widget size (default: 'medium')theme:'light' | 'dark'- Theme mode (default: 'light')className:string- Additional CSS classesonError:(error: Error) => void- Error handler callback
SDK Configuration
The widget uses dependency injection to integrate with the EduSight Notification SDK:
interface SDKConfiguration {
apiKey: string; // API key for authentication
baseUrl: string; // Base URL for the notification service
subscriberId: string; // Subscriber ID for the current user
tenantId: string; // Tenant ID for multi-tenant support
environmentId: string; // Environment ID for environment scoping
environment: 'development' | 'staging' | 'production'; // Environment setting
}Note: WebSocket URL is automatically derived from baseUrl by the SDK. The SDK converts HTTP URLs to WebSocket URLs automatically (http → ws, https → wss).
useSDK Hook
Access the SDK instance from within the widget context:
import { useSDK } from '@edusight/notification-widget';
function CustomComponent() {
const { client, isInitialized, error } = useSDK();
if (!isInitialized) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
// Use the SDK client for custom operations
const handleCustomAction = async () => {
await client.notifications.markAsRead('notification-id');
};
return <button onClick={handleCustomAction}>Custom Action</button>;
}Components
NotificationWidget
Main notification center component with complete functionality:
import { NotificationWidget } from '@edusight/notification-widget';
<NotificationWidget
sdkConfig={sdkConfig}
position="right"
size="medium"
theme="light"
onError={handleError}
/>Features:
- Bell icon with unread count badge
- Popover with notification list
- Real-time WebSocket updates with fallback polling
- Live preferences management
- Error boundaries with fallback UI
- Cross-tab synchronization
- Optimistic updates
BellComponent
Notification bell icon with unread badge (used internally by NotificationWidget):
import { BellComponent } from '@edusight/notification-widget';
<BellComponent
unreadCount={5}
onClick={handleClick}
size="medium"
disabled={false}
/>InboxPopover
Notification popover content (used internally by NotificationWidget):
import { InboxPopover } from '@edusight/notification-widget';
<InboxPopover
isOpen={true}
onClose={handleClose}
position="right"
currentView="notifications"
onViewChange={handleViewChange}
notifications={notifications}
onNotificationAction={handleAction}
preferences={preferences}
onPreferenceChange={handlePreferenceChange}
/>NotificationItem
Individual notification item component:
import { NotificationItem } from '@edusight/notification-widget';
<NotificationItem
notification={notification}
onAction={handleAction}
isSelected={false}
onSelect={handleSelect}
/>PreferencesView
Notification preference management component:
import { PreferencesView } from '@edusight/notification-widget';
<PreferencesView
preferences={preferences}
onChange={handleChange}
isLoading={false}
/>Error Boundaries
The widget includes comprehensive error boundaries:
import {
NotificationWidgetErrorBoundary,
ComponentErrorBoundary
} from '@edusight/notification-widget';
// Main widget error boundary
<NotificationWidgetErrorBoundary onError={handleError}>
<YourComponent />
</NotificationWidgetErrorBoundary>
// Component-level error boundary
<ComponentErrorBoundary componentName="CustomComponent">
<CustomComponent />
</ComponentErrorBoundary>Hooks
useLivePreferences
Manage notification preferences with real-time saving:
import { useLivePreferences } from '@edusight/notification-widget';
const {
updatePreference,
isSaving,
error
} = useLivePreferences({
preferences: currentPreferences,
onPreferencesChange: handleChange,
onError: handleError,
});
// Update a preference (automatically debounced and saved)
updatePreference('channels.email', true);useSDK
Access the SDK instance within the widget context:
import { useSDK } from '@edusight/notification-widget';
const { client, isInitialized, error } = useSDK();
if (!isInitialized) return <div>Loading...</div>;
// Use SDK methods directly
const markAsRead = async (id: string) => {
await client.notifications.markAsRead(id);
};Custom Hooks Integration
The widget is designed to work with custom hooks that use the SDK:
// Custom hook using the SDK
function useCustomNotifications() {
const { client } = useSDK();
const [notifications, setNotifications] = useState([]);
useEffect(() => {
if (client) {
client.notifications.list().then(setNotifications);
}
}, [client]);
return notifications;
}Configuration
SDKConfiguration
interface SDKConfiguration {
apiKey: string; // API key for authentication
baseUrl: string; // Base URL for the notification service
subscriberId: string; // Subscriber ID for the current user
tenantId: string; // Tenant ID for multi-tenant support
environmentId: string; // Environment ID for environment scoping
environment: 'development' | 'staging' | 'production'; // Environment setting
}NotificationWidgetProps
interface NotificationWidgetProps {
sdkConfig: SDKConfiguration; // SDK configuration for dependency injection
position?: 'left' | 'right'; // Popover position (default: 'right')
size?: 'small' | 'medium' | 'large'; // Widget size (default: 'medium')
theme?: 'light' | 'dark'; // Theme mode (default: 'light')
className?: string; // Additional CSS classes
onError?: (error: Error) => void; // Error handler callback
}Widget State Management
The widget uses a reducer pattern for state management:
interface WidgetState {
notifications: Notification[]; // Current notifications
unreadCount: number; // Unread notification count
preferences: NotificationPreferences; // User preferences
ui: {
isOpen: boolean; // Popover open state
currentView: 'notifications' | 'preferences'; // Current view
selectedNotifications: string[]; // Selected notification IDs
isLoading: boolean; // Loading state
error: Error | null; // Error state
};
websocket: {
connected: boolean; // WebSocket connection status
reconnecting: boolean; // Reconnection state
};
}Styling & Theming
Tailwind CSS Integration
The widget is built with Tailwind CSS and follows clean design principles. All components use Tailwind utility classes for consistent styling.
Theme Support
The widget supports light and dark themes:
// Light theme (default)
<NotificationWidget theme="light" sdkConfig={config} />
// Dark theme
<NotificationWidget theme="dark" sdkConfig={config} />Custom Styling
Override widget styles using CSS classes:
<NotificationWidget
className="custom-notification-widget"
sdkConfig={config}
/>.custom-notification-widget {
/* Custom styles for the widget container */
}
.custom-notification-widget .notification-bell {
/* Custom styles for the bell component */
}
.custom-notification-widget .notification-popover {
/* Custom styles for the popover */
}CSS Variables
The widget uses CSS variables for theming:
:root {
--notification-primary: #3b82f6;
--notification-primary-hover: #2563eb;
--notification-background: #ffffff;
--notification-text: #1f2937;
--notification-border: #e5e7eb;
}
[data-theme='dark'] {
--notification-primary: #60a5fa;
--notification-primary-hover: #3b82f6;
--notification-background: #1f2937;
--notification-text: #f9fafb;
--notification-border: #374151;
}Performance Optimizations
Architecture Benefits
The refactored architecture provides several performance benefits:
- Dependency Injection: SDK is initialized once and reused across components
- Error Boundaries: Component-level error isolation prevents cascading failures
- Optimistic Updates: UI updates immediately while API calls happen in background
- Debounced Preferences: Preference changes are debounced to reduce API calls
- Fallback Polling: Automatic fallback to polling when WebSocket fails
Code Splitting
The widget includes optimized code splitting:
// Main widget bundle: ~17KB gzipped
import { NotificationWidget } from '@edusight/notification-widget';
// Components are automatically code-split
// - Bell component: Loaded immediately
// - Popover content: Loaded on first open
// - Preferences view: Loaded when accessedMemory Management
The widget includes proper cleanup:
- WebSocket connections are properly closed
- Event listeners are removed on unmount
- Timers and intervals are cleared
- Component state is reset on errors
Bundle Size
Optimized bundle sizes:
- Main widget: ~17KB gzipped
- SDK integration: ~5KB additional
- Styles: ~25KB (includes Tailwind utilities)
- Total: ~47KB gzipped (complete package)
Accessibility
The widget includes:
- ✅ WCAG 2.1 Level AA compliance
- ✅ Keyboard navigation (Tab, Enter, Escape)
- ✅ ARIA labels and roles
- ✅ Screen reader support
- ✅ High contrast mode support
- ✅ Focus management
Use the AccessibilityProvider for custom accessibility options:
import { AccessibilityProvider } from '@edusight/notification-widget';
<AccessibilityProvider options={{ highContrast: true }}>
<YourApp />
</AccessibilityProvider>Playground Development Environment
A comprehensive development environment is included for testing and debugging:
cd playground
npm install
npm run devThe playground demonstrates:
- Complete NotificationWidget integration
- SDK dependency injection pattern
- Real-time WebSocket simulation
- All notification operations (CRUD)
- Live preferences management
- Error boundary testing
- Cross-tab synchronization
- Responsive design testing
- Debug logging and monitoring
Playground Features
- Configuration Panel: Test different SDK configurations
- Debug Console: Real-time logging and status monitoring
- Connection Status: API and WebSocket connection indicators
- Feature Testing: All widget features in one place
- Error Simulation: Test error boundaries and fallbacks
See the playground README for detailed development documentation.
Build Status
| Component | Status | Bundle Size | Features | |-----------|--------|-------------|----------| | Widget | ✅ Success | ~17 KB (gzipped) | Complete notification center | | SDK Integration | ✅ Success | ~5 KB (gzipped) | Dependency injection | | Styles | ✅ Success | ~25 KB (gzipped) | Tailwind utilities | | Playground | ✅ Success | ~330 KB (gzipped) | Development environment |
Development
Build
npm run buildGenerates:
dist/index.esm.js- ES modules builddist/index.cjs.js- CommonJS builddist/index.d.ts- TypeScript declarationsdist/index.css- Compiled styles
Test
npm test # Run all tests
npm run test:watch # Watch mode
npm run test:coverage # Coverage reportTest coverage: >90% across all components and hooks
Lint
npm run lint # Check for issues
npm run lint:fix # Fix auto-fixable issuesType Check
npm run type-check # TypeScript type checkingIntegration Workflow
- Install the widget:
npm install @edusight/notification-widget - Import styles:
import '@edusight/notification-widget/styles' - Configure SDK: Set up your SDK configuration object
- Add NotificationWidget: Place the widget component in your app
- Handle errors: Implement error handling for production use
- Test with playground: Use the included playground for development
Example Integration
import React from 'react';
import { NotificationWidget } from '@edusight/notification-widget';
import '@edusight/notification-widget/styles';
function App() {
const sdkConfig = {
apiKey: process.env.REACT_APP_NOTIFICATION_API_KEY!,
baseUrl: process.env.REACT_APP_NOTIFICATION_API_URL!,
subscriberId: getCurrentUserId(),
tenantId: getCurrentTenantId(),
environmentId: getCurrentEnvironmentId(),
environment: process.env.NODE_ENV as 'development' | 'staging' | 'production',
};
const handleError = (error: Error) => {
console.error('Notification widget error:', error);
// Send to error tracking service
errorTracker.captureException(error);
};
return (
<div className="app">
<header className="app-header">
<h1>My Application</h1>
<NotificationWidget
sdkConfig={sdkConfig}
position="right"
size="medium"
theme="light"
onError={handleError}
/>
</header>
<main>
{/* Your app content */}
</main>
</div>
);
}
export default App;Troubleshooting
"useSDK must be used within a NotificationWidget"
Ensure your component is used within the NotificationWidget context:
// ❌ Wrong - useSDK outside widget context
function MyComponent() {
const { client } = useSDK(); // This will throw an error
return <div>...</div>;
}
// ✅ Correct - Custom component within widget
<NotificationWidget sdkConfig={config}>
{/* Custom components can use useSDK here */}
</NotificationWidget>SDK Configuration Issues
- Check required fields: Ensure all required SDK config fields are provided
- Verify URLs: Check that
baseUrlandwebsocketUrlare correct - Environment mismatch: Ensure
environmentmatches your deployment - API key validation: Verify the API key is valid and has proper permissions
WebSocket Connection Issues
- URL format: Ensure WebSocket URL uses
ws://orwss://protocol - CORS settings: Check CORS configuration on the backend
- Firewall/proxy: Verify WebSocket connections aren't blocked
- Fallback polling: The widget automatically falls back to polling if WebSocket fails
Performance Issues
- Bundle size: Check if you're importing the entire widget when you only need parts
- Re-renders: Use React DevTools to identify unnecessary re-renders
- Memory leaks: Ensure proper cleanup of event listeners and timers
- Error boundaries: Check if error boundaries are catching and handling errors properly
Styling Issues
- CSS conflicts: Ensure your CSS doesn't conflict with widget styles
- Tailwind purging: Make sure Tailwind isn't purging widget classes
- Theme application: Verify theme prop is being applied correctly
- Custom classes: Check that custom className prop is being used
API Reference
Exported Components
export {
NotificationWidget, // Main widget component
BellComponent, // Bell icon with badge
InboxPopover, // Popover content
NotificationItem, // Individual notification
PreferencesView, // Preferences management
NotificationWidgetErrorBoundary, // Main error boundary
ComponentErrorBoundary, // Component-level error boundary
};Exported Hooks
export {
useSDK, // Access SDK instance
useLivePreferences, // Live preferences management
};Exported Types
export type {
NotificationWidgetProps, // Main widget props
SDKConfiguration, // SDK config interface
WidgetState, // Widget state interface
WidgetAction, // State action types
Notification, // Core notification type
NotificationPreferences, // Preferences interface
WidgetError, // Error interface
};Re-exported SDK Types
export type {
Notification as SDKNotification,
NotificationFilters,
} from '@edusight/notification-sdk';Architecture
Clean Architecture Principles
The widget follows clean architecture principles:
- Separation of Concerns: UI, business logic, and data access are separated
- Dependency Injection: SDK is injected rather than directly imported
- Error Boundaries: Component-level error isolation
- State Management: Centralized state with reducer pattern
- Event Handling: Proper event cleanup and memory management
Component Hierarchy
NotificationWidget
├── SDKProvider (Dependency Injection)
├── NotificationWidgetErrorBoundary
└── NotificationWidgetInternal
├── ComponentErrorBoundary (BellComponent)
│ └── BellComponent
└── ComponentErrorBoundary (InboxPopover)
└── InboxPopover
├── NotificationsList
│ └── NotificationItem[]
└── PreferencesViewState Flow
- SDK Initialization: SDKProvider initializes and provides SDK instance
- State Management: useReducer manages widget state centrally
- Real-time Updates: WebSocket events update state via reducer actions
- Optimistic Updates: UI updates immediately, API calls happen async
- Error Handling: Errors are caught by boundaries and handled gracefully
License
MIT
