From 499581ef6637834ac8092494f51154032b24eb70 Mon Sep 17 00:00:00 2001 From: breken Date: Wed, 9 Sep 2026 16:39:42 -0700 Subject: [PATCH] fix(alert-timeline): make timeline scrollable with mouse wheel/trackpad react-chrono renders nested overflow containers with zero scroll extent inside the timeline. Chrome latches wheel gestures onto the one under the cursor and never chains them to the clipped scroll container, so the timeline cannot be scrolled. Move max-h/overflow-y off the tremor Card onto a plain inner div and attach a non-passive wheel listener that scrolls it directly, calling preventDefault only when the scroll position actually changed so overscroll still chains to the page. --- .../ui/alert-timeline.tsx | 33 +++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/keep-ui/features/alerts/alert-detail-sidebar/ui/alert-timeline.tsx b/keep-ui/features/alerts/alert-detail-sidebar/ui/alert-timeline.tsx index ea24ebc4ff..d69484853a 100644 --- a/keep-ui/features/alerts/alert-detail-sidebar/ui/alert-timeline.tsx +++ b/keep-ui/features/alerts/alert-detail-sidebar/ui/alert-timeline.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useEffect, useRef } from "react"; import { Subtitle, Button, Card, Title } from "@tremor/react"; import { Chrono } from "react-chrono"; import { ArrowPathIcon } from "@heroicons/react/24/outline"; @@ -26,6 +26,33 @@ export const AlertTimeline: React.FC = ({ isLoading, onRefresh, }) => { + const timelineScrollRef = useRef(null); + + useEffect(() => { + const scroller = timelineScrollRef.current; + if (!scroller) { + return; + } + // react-chrono renders nested overflow containers with zero scroll + // extent. Chrome latches wheel gestures onto the one under the cursor + // and never chains them to the real scroll container, so the clipped + // timeline cannot be scrolled. Scroll it ourselves; preventDefault + // only when the scroll position actually changed, so overscroll still + // chains to the page once the container reaches either end. + const onWheel = (e: WheelEvent) => { + if (scroller.scrollHeight <= scroller.clientHeight) { + return; + } + const before = scroller.scrollTop; + scroller.scrollTop += e.deltaY; + if (scroller.scrollTop !== before) { + e.preventDefault(); + } + }; + scroller.addEventListener("wheel", onWheel, { passive: false }); + return () => scroller.removeEventListener("wheel", onWheel); + }, []); + // Default audit event if no audit data is available const defaultAuditEvent = alert ? [ @@ -86,7 +113,8 @@ export const AlertTimeline: React.FC = ({ title="Refresh" /> - + +
{isLoading ? (

Loading...

@@ -129,6 +157,7 @@ export const AlertTimeline: React.FC = ({
)} +
);