diff --git a/src/components/molecules/ExecutionDetails/TestExecutionDetails/TestExecutionDetailsTabs.tsx b/src/components/molecules/ExecutionDetails/TestExecutionDetails/TestExecutionDetailsTabs.tsx index e19c467d0..a06142236 100644 --- a/src/components/molecules/ExecutionDetails/TestExecutionDetails/TestExecutionDetailsTabs.tsx +++ b/src/components/molecules/ExecutionDetails/TestExecutionDetails/TestExecutionDetailsTabs.tsx @@ -1,9 +1,7 @@ -import {useEffect, useRef, useState} from 'react'; +import {useEffect} from 'react'; import {Tabs} from 'antd'; -import debounce from 'lodash.debounce'; - import useIsRunning from '@hooks/useIsRunning'; import {Execution} from '@models/execution'; @@ -30,11 +28,6 @@ const TestExecutionDetailsTabs: React.FC = () => { const executorsFeaturesMap = useAppSelector(selectExecutorsFeaturesMap); - const ref = useRef(null); - - const [oldScroll, setOldScroll] = useState(0); - const [isAutoScrolled, setAutoScrolledState] = useState(false); - const execution = data as Execution; const { @@ -57,46 +50,12 @@ const TestExecutionDetailsTabs: React.FC = () => { setTestExecutionTabsData({execution, test: details}); }, [execution, details]); - useEffect(() => { - if (ref && ref.current) { - ref.current.onscroll = debounce(() => { - setOldScroll(prev => { - if (ref && ref.current) { - if (prev > ref.current?.scrollTop) { - setAutoScrolledState(false); - } else { - setAutoScrolledState(true); - } - - return ref.current?.scrollTop; - } - - return prev; - }); - }, 50); - } - }, [oldScroll]); - - useEffect(() => { - setTimeout(() => { - setAutoScrolledState(true); - }, 500); - }, [id]); - - useEffect(() => { - if (isRunning) { - setAutoScrolledState(true); - } - }, [isRunning, id]); - const defaultExecutionDetailsTabs = [ { value: { key: 'LogOutputPane', label: 'Log Output', - children: ( - - ), + children: , }, metadata: { order: Infinity, @@ -145,11 +104,7 @@ const TestExecutionDetailsTabs: React.FC = () => { const items = usePluginSlotList('testExecutionTabs', defaultExecutionDetailsTabs); - return ( -
- -
- ); + return ; }; export default TestExecutionDetailsTabs; diff --git a/src/components/molecules/LogOutput/LogOutput.tsx b/src/components/molecules/LogOutput/LogOutput.tsx index 6c215af03..0a5a8890c 100644 --- a/src/components/molecules/LogOutput/LogOutput.tsx +++ b/src/components/molecules/LogOutput/LogOutput.tsx @@ -4,6 +4,8 @@ import useWebSocket from 'react-use-websocket'; import Ansi from 'ansi-to-react'; +import {useScrolledToBottom} from '@hooks/useScrolledToBottom'; + import {LogAction} from '@models/log'; import {useAppDispatch, useAppSelector} from '@redux/hooks'; @@ -23,7 +25,6 @@ export type LogOutputProps = { actions?: LogAction[]; isRunning?: boolean; title?: string; - isAutoScrolled?: boolean; initialLines?: number; }; @@ -36,12 +37,12 @@ const LogOutput: React.FC = props => { actions = ['copy', 'fullscreen'], isRunning, title, - isAutoScrolled, initialLines = 300, } = props; const bottomRef = useRef(null); const containerRef = useRef(null); + const isScrolledToBottom = useScrolledToBottom(containerRef.current); const wsRoot = useWsEndpoint(); @@ -52,27 +53,13 @@ const LogOutput: React.FC = props => { const [expanded, setExpanded] = useState(false); const lines = useCountLines(logs); - const visibleLogs = useLastLines(logs, expanded ? Infinity : initialLines); - - const scrollToBottom: (behavior?: ScrollBehavior) => void = (behavior = 'smooth') => { - if (bottomRef && bottomRef.current) { - bottomRef.current.scrollIntoView({behavior, block: 'end'}); - } - }; + const visibleLogs = useLastLines(logs, expanded || isRunning ? Infinity : initialLines); const onExpand = useCallback((event: MouseEvent) => { event.preventDefault(); setExpanded(true); }, []); - const smoothScrollIfAutoscroll = useCallback(() => { - if (!isAutoScrolled) { - return; - } - - scrollToBottom(); - }, [isAutoScrolled]); - // TODO: Consider getting token different way than using the one from RTK const {value: token, loading: tokenLoading} = useAsync(getRtkIdToken); useWebSocket( @@ -133,12 +120,14 @@ const LogOutput: React.FC = props => { }, [logs, isFullScreenLogOutput]); useEffect(() => { - smoothScrollIfAutoscroll(); + if (containerRef.current && isScrolledToBottom) { + containerRef.current.scrollTop = containerRef.current.scrollHeight - containerRef.current.clientHeight; + } }, [logs]); useEffect(() => { setTimeout(() => { - scrollToBottom('auto'); + bottomRef?.current?.scrollIntoView({behavior: 'auto', block: 'end'}); }, 100); }, [executionId]); diff --git a/src/hooks/useScrolledToBottom.ts b/src/hooks/useScrolledToBottom.ts new file mode 100644 index 000000000..512bc4449 --- /dev/null +++ b/src/hooks/useScrolledToBottom.ts @@ -0,0 +1,7 @@ +import {useMemo} from 'react'; + +export const useScrolledToBottom = (element?: Element | null, offset = 25, defaults = true) => + useMemo( + () => (element ? Math.abs(element.scrollHeight - element.scrollTop - element.clientHeight) < offset : defaults), + [element?.clientHeight, element?.scrollHeight, element?.scrollTop, offset, defaults] + );