Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions src/app/components/notifications/NotificationBell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ import { useNotificationStore } from '@/app/store/notificationStore';
import NotificationCenter from '@/app/components/notifications/NotificationCenter';

export default function NotificationBell() {
const { notifications } = useNotificationStore();
const unread = notifications.filter((n) => !n.read).length;
const unreadCount = useNotificationStore((s) => s.notifications.filter((n) => !n.read).length);
const [open, setOpen] = useState(false);
const rootRef = useRef<HTMLDivElement>(null);

Expand Down Expand Up @@ -38,9 +37,9 @@ export default function NotificationBell() {
className="relative inline-flex items-center justify-center w-9 h-9 rounded-full border bg-white hover:bg-gray-50"
>
<Bell size={18} className="text-gray-700" />
{unread > 0 && (
{unreadCount > 0 && (
<span className="absolute -top-1 -right-1 inline-flex items-center justify-center px-1.5 py-0.5 text-[10px] leading-none rounded-full bg-red-600 text-white">
{unread}
{unreadCount}
</span>
)}
</button>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import React from 'react';
import { render, act } from '@testing-library/react';
import { describe, it, expect, beforeEach, vi } from 'vitest';
import NotificationBell from '../NotificationBell';
import { useNotificationStore } from '@/app/store/notificationStore';
import { Bell } from 'lucide-react';

// Mock lucide-react to spy on Bell rendering
vi.mock('lucide-react', async (importOriginal) => {
const original = await importOriginal<typeof import('lucide-react')>();
return {
...original,
Bell: vi.fn((props) => <original.Bell {...props} />),
};
});

describe('NotificationBell', () => {
beforeEach(() => {
useNotificationStore.setState({ notifications: [] });
vi.clearAllMocks();
});

it('renders correctly and has 0 unread initially', () => {
const { queryByText } = render(<NotificationBell />);
expect(queryByText('1')).toBeNull();
});

it('renders only once (no extra re-render) when a read notification is added', () => {
const { queryByText } = render(<NotificationBell />);

// Check initial render count of Bell
expect(Bell).toHaveBeenCalledTimes(1);

// Add a read notification to the store
act(() => {
useNotificationStore.getState().addNotification({
id: '1',
message: 'Read notification',
type: 'info',
read: true,
title: 'Info',
});
});

// The unread count badge should not be present
expect(queryByText('1')).toBeNull();

// Since the notification was already read, the unread count did not change
// Therefore, the NotificationBell should NOT have re-rendered
expect(Bell).toHaveBeenCalledTimes(1);
});

it('re-renders when an unread notification is added (unread count changes)', () => {
const { getByText } = render(<NotificationBell />);
expect(Bell).toHaveBeenCalledTimes(1);

// Add an unread notification to the store
act(() => {
useNotificationStore.getState().addNotification({
id: '2',
message: 'Unread notification',
type: 'info',
read: false,
title: 'Info',
});
});

// The unread count badge should display '1'
expect(getByText('1')).toBeInTheDocument();

// Since the unread count changed, the component should re-render
expect(Bell).toHaveBeenCalledTimes(2);
});
});
Loading