diff --git a/dashboard/src/components/EventList.tsx b/dashboard/src/components/EventList.tsx
index 989112c..afb2d2a 100644
--- a/dashboard/src/components/EventList.tsx
+++ b/dashboard/src/components/EventList.tsx
@@ -42,7 +42,9 @@ export const EventList = memo(function EventList({ events }: EventListProps) {
const windowState = useMemo(() => {
const visibleCount = Math.ceil(viewportHeight / ROW_HEIGHT) + OVERSCAN;
- const startIndex = Math.max(0, Math.floor(scrollTop / ROW_HEIGHT) - OVERSCAN);
+ const maxScrollTop = Math.max(0, events.length * ROW_HEIGHT - viewportHeight);
+ const clampedScrollTop = Math.min(scrollTop, maxScrollTop);
+ const startIndex = Math.max(0, Math.floor(clampedScrollTop / ROW_HEIGHT) - OVERSCAN);
const endIndex = Math.min(events.length, startIndex + visibleCount + OVERSCAN);
return {
diff --git a/dashboard/src/store/eventStore.test.tsx b/dashboard/src/store/eventStore.test.tsx
index f9dc377..5f51174 100644
--- a/dashboard/src/store/eventStore.test.tsx
+++ b/dashboard/src/store/eventStore.test.tsx
@@ -1,8 +1,9 @@
-import { render, screen } from '@testing-library/react';
+import { render, screen, act } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { describe, expect, it } from '@jest/globals';
import { EventFiltersBar } from '../components/EventFiltersBar';
import { EventListPanel } from '../components/EventListPanel';
+import { EventList } from '../components/EventList';
import { useEventStore } from '../store/eventStore';
import { generateMockEvents } from '../utils/eventData';
@@ -32,3 +33,54 @@ describe('event store selective subscriptions', () => {
expect(filteredRows[0].textContent).toContain('TaskCreated');
});
});
+
+describe('pagination + filter interaction', () => {
+ it('applying a filter while scrolled does not blank the list', () => {
+ const events = generateMockEvents(200);
+ useEventStore.setState({
+ events,
+ filters: { search: '', contractAddress: 'all', eventType: 'all' },
+ isLoading: false,
+ error: null,
+ });
+
+ // Render just the list with a large scroll offset to simulate being deep in the list
+ const { rerender } = render(