diff --git a/public/app/features/logs/components/panel/InfiniteScroll.test.tsx b/public/app/features/logs/components/panel/InfiniteScroll.test.tsx
index 2507a3b5176..356ffe12dd5 100644
--- a/public/app/features/logs/components/panel/InfiniteScroll.test.tsx
+++ b/public/app/features/logs/components/panel/InfiniteScroll.test.tsx
@@ -1,5 +1,5 @@
import { act, render, screen } from '@testing-library/react';
-import { VariableSizeList } from 'react-window';
+import { List } from 'react-window';
import { createTheme, dateTimeForTimeZone, rangeUtil } from '@grafana/data';
import { LogsSortOrder } from '@grafana/schema';
@@ -89,19 +89,19 @@ function setup(
loadMore={loadMoreMock}
infiniteScrollMode={infiniteScrollMode}
>
- {({ getItemKey, itemCount, onItemsRendered, Renderer }) => (
- virtualization.getLineHeight()}
- itemKey={getItemKey}
- layout="vertical"
- onItemsRendered={onItemsRendered}
- style={{ overflow: 'scroll' }}
- width="100%"
- >
- {Renderer}
-
+ {({ itemCount, onItemsRendered, Renderer }) => (
+ virtualization.getLineHeight()}
+ onRowsRendered={onItemsRendered}
+ style={{
+ overflow: 'scroll',
+ height: 100,
+ width: '100%',
+ }}
+ />
)}
);
diff --git a/public/app/features/logs/components/panel/InfiniteScroll.tsx b/public/app/features/logs/components/panel/InfiniteScroll.tsx
index 3c7de64b6be..c662e49d1d8 100644
--- a/public/app/features/logs/components/panel/InfiniteScroll.tsx
+++ b/public/app/features/logs/components/panel/InfiniteScroll.tsx
@@ -1,6 +1,6 @@
-import { ReactNode, useCallback, useEffect, useRef, useState, MouseEvent } from 'react';
+import { ReactNode, useCallback, useEffect, useRef, useState, MouseEvent, type JSX } from 'react';
import { usePrevious } from 'react-use';
-import { ListChildComponentProps, ListOnItemsRenderedProps } from 'react-window';
+import { type RowComponentProps, type ListProps } from 'react-window';
import { AbsoluteTimeRange, LogsSortOrder, TimeRange } from '@grafana/data';
import { t } from '@grafana/i18n';
@@ -17,8 +17,8 @@ import { LogLineVirtualization } from './virtualization';
interface ChildrenProps {
itemCount: number;
getItemKey: (index: number) => string;
- onItemsRendered: (props: ListOnItemsRenderedProps) => void;
- Renderer: (props: ListChildComponentProps) => ReactNode;
+ onItemsRendered: ListProps<{}>['onRowsRendered'];
+ Renderer: (props: RowComponentProps) => JSX.Element;
}
export interface Props {
@@ -200,7 +200,7 @@ export const InfiniteScroll = ({
}, [onLoadMore]);
const Renderer = useCallback(
- ({ index, style }: ListChildComponentProps) => {
+ ({ index, style }: RowComponentProps) => {
if (!logs[index] && infiniteLoaderState !== 'idle') {
return (
{
+ const onItemsRendered = useCallback['onRowsRendered']>>(
+ (props) => {
if (!scrollElement) {
return;
}
- if (props.visibleStartIndex === 0) {
+ if (props.startIndex === 0) {
noScrollRef.current = scrollElement.scrollHeight <= scrollElement.clientHeight;
}
if (noScrollRef.current || infiniteLoaderState === 'loading' || infiniteLoaderState === 'out-of-bounds') {
@@ -261,9 +261,9 @@ export const InfiniteScroll = ({
}
const lastLogIndex = logs.length - 1;
const preScrollIndex = logs.length - 2;
- if (props.visibleStopIndex >= lastLogIndex) {
+ if (props.stopIndex >= lastLogIndex) {
setInfiniteLoaderState('pre-scroll-bottom');
- } else if (props.visibleStartIndex < preScrollIndex) {
+ } else if (props.startIndex < preScrollIndex) {
setInfiniteLoaderState('idle');
}
},
diff --git a/public/app/features/logs/components/panel/LogList.tsx b/public/app/features/logs/components/panel/LogList.tsx
index 8a8a9b085dc..35f7880544d 100644
--- a/public/app/features/logs/components/panel/LogList.tsx
+++ b/public/app/features/logs/components/panel/LogList.tsx
@@ -2,7 +2,7 @@ import { css } from '@emotion/css';
import { debounce } from 'lodash';
import { Grammar } from 'prismjs';
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, MouseEvent } from 'react';
-import { Align, VariableSizeList } from 'react-window';
+import { Align, List, ListImperativeAPI, useListRef } from 'react-window';
import {
CoreApp,
@@ -282,10 +282,9 @@ const LogListComponent = ({
const [processedLogs, setProcessedLogs] = useState([]);
const [listHeight, setListHeight] = useState(getListHeight(containerElement, app));
const theme = useTheme2();
- const listRef = useRef(null);
+ const listRef = useListRef(null);
const widthRef = useRef(containerElement.clientWidth);
const wrapperRef = useRef(null);
- const scrollRef = useRef(null);
const virtualization = useMemo(() => new LogLineVirtualization(theme, fontSize), [theme, fontSize]);
const dimensions = useMemo(
() =>
@@ -329,25 +328,29 @@ const LogListComponent = ({
// When log lines report size discrepancies, we debounce the calculation reset to give time to
// use the smallest log index to reset the heights.
- const debouncedResetAfterIndex = useMemo(() => {
- return debounce((index: number) => {
- listRef.current?.resetAfterIndex(index);
- overflowIndexRef.current = Infinity;
- }, 0);
- }, []);
+ // TODO do we need this?
+ // const debouncedResetAfterIndex = useMemo(() => {
+ // return debounce((index: number) => {
+ // listRef.current?.resetAfterIndex(index);
+ // overflowIndexRef.current = Infinity;
+ // }, 0);
+ // }, []);
const debouncedScrollToItem = useMemo(() => {
return debounce((index: number, align?: Align) => {
- listRef.current?.scrollToItem(index, align);
+ listRef.current?.scrollToRow({
+ index,
+ align,
+ });
}, 250);
- }, []);
+ }, [listRef]);
useEffect(() => {
const subscription = eventBus.subscribe(ScrollToLogsEvent, (e: ScrollToLogsEvent) =>
handleScrollToEvent(e, filteredLogs, listRef.current)
);
return () => subscription.unsubscribe();
- }, [eventBus, filteredLogs]);
+ }, [eventBus, filteredLogs, listRef]);
useEffect(() => {
setProcessedLogs(
@@ -366,11 +369,13 @@ const LogListComponent = ({
)
);
virtualization.resetLogLineSizes();
- listRef.current?.resetAfterIndex(0);
+ // TODO do we need this?
+ // listRef.current?.resetAfterIndex(0);
}, [forceEscape, getFieldLinks, grammar, logs, prettifyJSON, sortOrder, timeZone, virtualization, wrapLogMessage]);
useEffect(() => {
- listRef.current?.resetAfterIndex(0);
+ // TODO do we need this?
+ // listRef.current?.resetAfterIndex(0);
}, [wrapLogMessage, showDetails, displayedFields, dedupStrategy]);
useLayoutEffect(() => {
@@ -399,9 +404,10 @@ const LogListComponent = ({
return;
}
overflowIndexRef.current = index < overflowIndexRef.current ? index : overflowIndexRef.current;
- debouncedResetAfterIndex(overflowIndexRef.current);
+ // TODO do we need this?
+ // debouncedResetAfterIndex(overflowIndexRef.current);
},
- [debouncedResetAfterIndex, virtualization, widthContainer]
+ [virtualization, widthContainer]
);
const handleScrollPosition = useCallback(
@@ -410,13 +416,13 @@ const LogListComponent = ({
if (scrollToUID) {
const index = processedLogs.findIndex((log) => log.uid === scrollToUID);
if (index >= 0) {
- listRef.current?.scrollToItem(index, 'start');
+ listRef.current?.scrollToRow({ index, align: 'start' });
return;
}
}
- listRef.current?.scrollToItem(initialScrollPosition === 'top' ? 0 : processedLogs.length - 1);
+ listRef.current?.scrollToRow({ index: initialScrollPosition === 'top' ? 0 : processedLogs.length - 1 });
},
- [initialScrollPosition, permalinkedLogId, processedLogs]
+ [initialScrollPosition, permalinkedLogId, processedLogs, listRef]
);
const handleLogLineClick = useCallback(
@@ -503,7 +509,7 @@ const LogListComponent = ({
logs={filteredLogs}
loadMore={loadMore}
onClick={handleLogLineClick}
- scrollElement={scrollRef.current}
+ scrollElement={listRef.current?.element ?? null}
showTime={showTime}
sortOrder={sortOrder}
timeRange={timeRange}
@@ -512,12 +518,25 @@ const LogListComponent = ({
virtualization={virtualization}
wrapLogMessage={wrapLogMessage}
>
- {({ getItemKey, itemCount, onItemsRendered, Renderer }) => (
- (
+
- {Renderer}
-
+ listRef={listRef}
+ />
)}
@@ -586,16 +599,23 @@ function getStyles(
};
}
-function handleScrollToEvent(event: ScrollToLogsEvent, logs: LogListModel[], list: VariableSizeList | null) {
+function handleScrollToEvent(event: ScrollToLogsEvent, logs: LogListModel[], list: ListImperativeAPI | null) {
if (event.payload.scrollTo === 'top') {
- list?.scrollTo(0);
+ list?.scrollToRow({
+ index: 0,
+ });
} else if (event.payload.scrollTo === 'bottom') {
- list?.scrollToItem(logs.length - 1);
+ list?.scrollToRow({
+ index: logs.length - 1,
+ });
} else {
// uid
const index = logs.findIndex((log) => log.uid === event.payload.scrollTo);
if (index >= 0) {
- list?.scrollToItem(index, 'center');
+ list?.scrollToRow({
+ index,
+ align: 'center',
+ });
}
}
}
diff --git a/public/app/features/logs/components/panel/LogListSearch.tsx b/public/app/features/logs/components/panel/LogListSearch.tsx
index 3cdd1cbbe3d..353dd99cbe2 100644
--- a/public/app/features/logs/components/panel/LogListSearch.tsx
+++ b/public/app/features/logs/components/panel/LogListSearch.tsx
@@ -1,6 +1,6 @@
import { css } from '@emotion/css';
import { ChangeEvent, startTransition, useCallback, useEffect, useMemo, useRef, useState } from 'react';
-import { VariableSizeList } from 'react-window';
+import { type ListImperativeAPI } from 'react-window';
import { escapeRegex, GrafanaTheme2, shallowCompare } from '@grafana/data';
import { t } from '@grafana/i18n';
@@ -12,7 +12,7 @@ import { useLogListSearchContext } from './LogListSearchContext';
import { LogListModel } from './processing';
interface Props {
- listRef: VariableSizeList | null;
+ listRef: ListImperativeAPI | null;
logs: LogListModel[];
}
@@ -61,7 +61,10 @@ export const LogListSearch = ({ listRef, logs }: Props) => {
}
const prev = currentResult > 0 ? currentResult - 1 : matches.length - 1;
setCurrentResult(prev);
- listRef?.scrollToItem(logs.indexOf(matches[prev]), 'center');
+ listRef?.scrollToRow({
+ index: logs.indexOf(matches[prev]),
+ align: 'center',
+ });
}, [currentResult, listRef, logs, matches]);
const nextResult = useCallback(() => {
@@ -70,7 +73,10 @@ export const LogListSearch = ({ listRef, logs }: Props) => {
}
const next = currentResult < matches.length - 1 ? currentResult + 1 : 0;
setCurrentResult(next);
- listRef?.scrollToItem(logs.indexOf(matches[next]), 'center');
+ listRef?.scrollToRow({
+ index: logs.indexOf(matches[next]),
+ align: 'center',
+ });
}, [currentResult, listRef, logs, matches]);
useEffect(() => {
@@ -80,7 +86,10 @@ export const LogListSearch = ({ listRef, logs }: Props) => {
}
if (!currentResult) {
setCurrentResult(0);
- listRef?.scrollToItem(logs.indexOf(matches[0]), 'center');
+ listRef?.scrollToRow({
+ index: logs.indexOf(matches[0]),
+ align: 'center',
+ });
}
}, [currentResult, listRef, logs, matches]);