Infinite scroll: share loader with log context + small fixes (#81067)

* Loading indicator: refactor

* Infinite scroll: use ref to keep the last scroll

* Infinite scroll: share loading indicator with log context

* Infinite scrolling: override state

* Infinite scroll: clean up imports and dependency array

* Logs: disable order while loading

* Infinite scroll: update ref before calling shouldLoadMore

* Loading indicator: remove unused export

* Logs order: move disabled prop to inline field

* Formatting

* Update betterer file
This commit is contained in:
Matias Chomicki 2024-01-23 17:52:38 +01:00 committed by GitHub
parent aa07c4a6b3
commit e07417dca3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 27 additions and 30 deletions

View File

@ -3691,6 +3691,9 @@ exports[`better eslint`] = {
[0, 0, 0, "Do not use any type assertions.", "3"],
[0, 0, 0, "Do not use any type assertions.", "4"]
],
"public/app/features/logs/components/LoadingIndicator.tsx:5381": [
[0, 0, 0, "Styles should be written using objects.", "0"]
],
"public/app/features/logs/components/LogDetailsRow.tsx:5381": [
[0, 0, 0, "Styles should be written using objects.", "0"],
[0, 0, 0, "Styles should be written using objects.", "1"],
@ -3763,9 +3766,6 @@ exports[`better eslint`] = {
[0, 0, 0, "Styles should be written using objects.", "34"],
[0, 0, 0, "Styles should be written using objects.", "35"]
],
"public/app/features/logs/components/log-context/LoadingIndicator.tsx:5381": [
[0, 0, 0, "Styles should be written using objects.", "0"]
],
"public/app/features/logs/components/log-context/LogRowContextModal.tsx:5381": [
[0, 0, 0, "Styles should be written using objects.", "0"],
[0, 0, 0, "Styles should be written using objects.", "1"],

View File

@ -754,9 +754,13 @@ class UnthemedLogs extends PureComponent<Props, State> {
</InlineFieldRow>
<div>
<InlineField label="Display results" className={styles.horizontalInlineLabel} transparent>
<InlineField
label="Display results"
className={styles.horizontalInlineLabel}
transparent
disabled={isFlipping || loading}
>
<RadioButtonGroup
disabled={isFlipping}
options={[
{
label: 'Newest first',

View File

@ -760,7 +760,8 @@ export const runLoadMoreLogsQueries = createAsyncThunk<void, RunLoadMoreLogsQuer
mergeMap(([data, correlations]) => {
// For query splitting, otherwise duplicates results
if (data.state !== LoadingState.Done) {
return of(queryResponse);
// While loading, return the previous response and override state, otherwise it's set to Done
return of({ ...queryResponse, state: LoadingState.Loading });
}
return decorateData(
combinePanelData(queryResponse, data),

View File

@ -1,11 +1,12 @@
import { css } from '@emotion/css';
import React, { ReactNode, useEffect, useState } from 'react';
import React, { ReactNode, useEffect, useRef, useState } from 'react';
import { AbsoluteTimeRange, LogRowModel, TimeRange } from '@grafana/data';
import { convertRawToRange, isRelativeTime, isRelativeTimeRange } from '@grafana/data/src/datetime/rangeutil';
import { reportInteraction } from '@grafana/runtime';
import { LogsSortOrder, TimeZone } from '@grafana/schema';
import { Spinner } from '@grafana/ui';
import { LoadingIndicator } from './LoadingIndicator';
export type Props = {
children: ReactNode;
@ -32,7 +33,7 @@ export const InfiniteScroll = ({
const [lowerOutOfRange, setLowerOutOfRange] = useState(false);
const [upperLoading, setUpperLoading] = useState(false);
const [lowerLoading, setLowerLoading] = useState(false);
const [lastScroll, setLastScroll] = useState(scrollElement?.scrollTop || 0);
const lastScroll = useRef<number>(scrollElement?.scrollTop || 0);
useEffect(() => {
setUpperOutOfRange(false);
@ -56,8 +57,8 @@ export const InfiniteScroll = ({
return;
}
event.stopImmediatePropagation();
setLastScroll(scrollElement.scrollTop);
const scrollDirection = shouldLoadMore(event, scrollElement, lastScroll);
const scrollDirection = shouldLoadMore(event, scrollElement, lastScroll.current);
lastScroll.current = scrollElement.scrollTop;
if (scrollDirection === ScrollDirection.NoScroll) {
return;
} else if (scrollDirection === ScrollDirection.Top) {
@ -110,7 +111,7 @@ export const InfiniteScroll = ({
scrollElement.removeEventListener('scroll', handleScroll);
scrollElement.removeEventListener('wheel', handleScroll);
};
}, [lastScroll, loadMoreLogs, loading, range, rows, scrollElement, sortOrder, timeZone]);
}, [loadMoreLogs, loading, range, rows, scrollElement, sortOrder, timeZone]);
// We allow "now" to move when using relative time, so we hide the message so it doesn't flash.
const hideTopMessage = sortOrder === LogsSortOrder.Descending && isRelativeTime(range.raw.to);
@ -118,11 +119,11 @@ export const InfiniteScroll = ({
return (
<>
{upperLoading && loadingMessage}
{upperLoading && <LoadingIndicator adjective={sortOrder === LogsSortOrder.Descending ? 'newer' : 'older'} />}
{!hideTopMessage && upperOutOfRange && outOfRangeMessage}
{children}
{!hideBottomMessage && lowerOutOfRange && outOfRangeMessage}
{lowerLoading && loadingMessage}
{lowerLoading && <LoadingIndicator adjective={sortOrder === LogsSortOrder.Descending ? 'older' : 'newer'} />}
</>
);
};
@ -139,11 +140,6 @@ const outOfRangeMessage = (
End of the selected time range.
</div>
);
const loadingMessage = (
<div className={styles.messageContainer}>
<Spinner />
</div>
);
enum ScrollDirection {
Top = -1,

View File

@ -3,17 +3,14 @@ import React from 'react';
import { Spinner } from '@grafana/ui';
import { Place } from './types';
// ideally we'd use `@grafana/ui/LoadingPlaceholder`, but that
// one has a large margin-bottom.
type Props = {
place: Place;
adjective?: string;
};
export const LoadingIndicator = ({ place }: Props) => {
const text = place === 'above' ? 'Loading newer logs...' : 'Loading older logs...';
export const LoadingIndicator = ({ adjective = 'newer' }: Props) => {
const text = `Loading ${adjective} logs...`;
return (
<div className={loadingIndicatorStyles}>
<div>

View File

@ -26,11 +26,10 @@ import { useDispatch } from 'app/types';
import { dataFrameToLogsModel } from '../../logsModel';
import { sortLogRows } from '../../utils';
import { LoadingIndicator } from '../LoadingIndicator';
import { LogRows } from '../LogRows';
import { LoadingIndicator } from './LoadingIndicator';
import { LogContextButtons } from './LogContextButtons';
import { Place } from './types';
const getStyles = (theme: GrafanaTheme2) => {
return {
@ -143,6 +142,7 @@ type Section = {
loadingState: LoadingState;
rows: LogRowModel[];
};
type Place = 'above' | 'below';
type Context = Record<Place, Section>;
const makeEmptyContext = (): Context => ({
@ -518,7 +518,7 @@ export const LogRowContextModal: React.FunctionComponent<LogRowContextModalProps
<td className={styles.loadingCell}>
{loadingStateAbove !== LoadingState.Done && loadingStateAbove !== LoadingState.Error && (
<div ref={aboveLoadingElement}>
<LoadingIndicator place="above" />
<LoadingIndicator adjective="newer" />
</div>
)}
{loadingStateAbove === LoadingState.Error && <div>Error loading log more logs.</div>}
@ -587,7 +587,7 @@ export const LogRowContextModal: React.FunctionComponent<LogRowContextModalProps
<td className={styles.loadingCell}>
{loadingStateBelow !== LoadingState.Done && loadingStateBelow !== LoadingState.Error && (
<div ref={belowLoadingElement}>
<LoadingIndicator place="below" />
<LoadingIndicator adjective="older" />
</div>
)}
{loadingStateBelow === LoadingState.Error && <div>Error loading log more logs.</div>}

View File

@ -1 +0,0 @@
export type Place = 'above' | 'below';