Skip to content

Commit 0be62cc

Browse files
committed
fix: Afina review
1 parent d512182 commit 0be62cc

File tree

10 files changed

+170
-360
lines changed

10 files changed

+170
-360
lines changed

src/components/PaginatedTable/PaginatedTable.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ export interface PaginatedTableProps<T, F> {
3636
renderEmptyDataMessage?: RenderEmptyDataMessage;
3737
renderErrorMessage?: RenderErrorMessage;
3838
containerClassName?: string;
39+
startOffset?: number;
3940
}
4041

4142
export const PaginatedTable = <T, F>({
@@ -54,6 +55,7 @@ export const PaginatedTable = <T, F>({
5455
renderErrorMessage,
5556
renderEmptyDataMessage,
5657
containerClassName,
58+
startOffset = 0,
5759
}: PaginatedTableProps<T, F>) => {
5860
const initialTotal = initialEntitiesCount || 0;
5961
const initialFound = initialEntitiesCount || 1;
@@ -123,6 +125,7 @@ export const PaginatedTable = <T, F>({
123125
renderEmptyDataMessage={renderEmptyDataMessage}
124126
onDataFetched={handleDataFetched}
125127
isActive={isActive}
128+
startOffset={startOffset}
126129
/>
127130
));
128131
};

src/components/PaginatedTable/TableChunk.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ interface TableChunkProps<T, F> {
3030
sortParams?: SortParams;
3131
isActive: boolean;
3232
tableName: string;
33+
startOffset: number;
3334

3435
fetchData: FetchData<T, F>;
3536
getRowClassName?: GetRowClassName<T>;
@@ -53,6 +54,7 @@ export const TableChunk = typedMemo(function TableChunk<T, F>({
5354
renderErrorMessage,
5455
renderEmptyDataMessage,
5556
onDataFetched,
57+
startOffset,
5658
isActive,
5759
}: TableChunkProps<T, F>) {
5860
const [isTimeoutActive, setIsTimeoutActive] = React.useState(true);
@@ -61,7 +63,7 @@ export const TableChunk = typedMemo(function TableChunk<T, F>({
6163
const columnsIds = columns.map((column) => column.name);
6264

6365
const queryParams = {
64-
offset: id * chunkSize,
66+
offset: startOffset + id * chunkSize,
6567
limit: chunkSize,
6668
fetchData: fetchData as FetchData<T, unknown>,
6769
filters,

src/components/PaginatedTable/useScrollBasedChunks.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,18 @@ export const useScrollBasedChunks = ({
5151
Math.floor(visibleEnd / rowHeight / chunkSize) + overscanCount,
5252
Math.max(chunksCount - 1, 0),
5353
);
54-
5554
return {start, end};
5655
}, [parentRef, tableRef, rowHeight, chunkSize, overscanCount, chunksCount]);
5756

57+
React.useEffect(() => {
58+
const newRange = calculateVisibleRange();
59+
60+
if (newRange) {
61+
setStartChunk(newRange.start);
62+
setEndChunk(newRange.end);
63+
}
64+
}, [chunksCount, calculateVisibleRange]);
65+
5866
const handleScroll = React.useCallback(() => {
5967
const newRange = calculateVisibleRange();
6068
if (newRange) {
@@ -84,7 +92,7 @@ export const useScrollBasedChunks = ({
8492
return React.useMemo(() => {
8593
// boolean array that represents active chunks
8694
const activeChunks = Array(chunksCount).fill(false);
87-
for (let i = startChunk; i <= endChunk; i++) {
95+
for (let i = startChunk; i <= Math.min(endChunk, chunksCount); i++) {
8896
activeChunks[i] = true;
8997
}
9098
return activeChunks;

src/containers/Tenant/Diagnostics/TopicData/TopicData.tsx

Lines changed: 98 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
import React from 'react';
22

33
import {NoSearchResults} from '@gravity-ui/illustrations';
4+
import {skipToken} from '@reduxjs/toolkit/query';
5+
import {isNil} from 'lodash';
46

57
import type {RenderControls} from '../../../../components/PaginatedTable';
68
import {ResizeablePaginatedTable} from '../../../../components/PaginatedTable';
79
import {partitionsApi} from '../../../../store/reducers/partitions/partitions';
8-
import type {TopicMessageMetadataItem} from '../../../../types/api/topic';
10+
import {topicApi} from '../../../../store/reducers/topic';
11+
import type {TopicDataRequest, TopicMessageMetadataItem} from '../../../../types/api/topic';
912
import {useAutoRefreshInterval} from '../../../../utils/hooks';
1013
import {useSelectedColumns} from '../../../../utils/hooks/useSelectedColumns';
1114
import {renderPaginatedTableErrorMessage} from '../../../../utils/renderPaginatedTableErrorMessage';
@@ -38,8 +41,8 @@ interface TopicDataProps {
3841

3942
export function TopicData({parentRef, path, database}: TopicDataProps) {
4043
const [autoRefreshInterval] = useAutoRefreshInterval();
41-
const [startOffset, setStartOffset] = React.useState<number | undefined>(undefined);
42-
const [endOffset, setEndOffset] = React.useState<number | undefined>(undefined);
44+
const [startOffset, setStartOffset] = React.useState(0);
45+
const [endOffset, setEndOffset] = React.useState(0);
4346
const [fullValue, setFullValue] = React.useState<
4447
string | TopicMessageMetadataItem[] | undefined
4548
>(undefined);
@@ -52,8 +55,24 @@ export function TopicData({parentRef, path, database}: TopicDataProps) {
5255
topicDataFilter,
5356
handleSelectedOffsetChange,
5457
handleStartTimestampChange,
58+
handleSelectedPartitionChange,
5559
} = useTopicDataQueryParams();
5660

61+
const queryParams = React.useMemo(() => {
62+
if (isNil(selectedPartition)) {
63+
return skipToken;
64+
}
65+
const params: TopicDataRequest = {database, path, partition: selectedPartition, limit: 1};
66+
if (startTimestamp) {
67+
params.read_timestamp = startTimestamp;
68+
} else {
69+
params.offset = selectedOffset ?? 0;
70+
}
71+
return params;
72+
}, [selectedPartition, selectedOffset, startTimestamp, database, path]);
73+
74+
const {currentData, isFetching} = topicApi.useGetTopicDataQuery(queryParams);
75+
5776
const {
5877
data: partitions,
5978
isLoading: partitionsLoading,
@@ -74,16 +93,19 @@ export function TopicData({parentRef, path, database}: TopicDataProps) {
7493
}
7594
}, [selectedPartition, partitions]);
7695

77-
const tableFilters = React.useMemo(() => {
78-
return {
79-
path,
80-
database,
81-
partition: selectedPartition ?? '',
82-
selectedOffset: safeParseNumber(selectedOffset),
83-
startTimestamp: safeParseNumber(startTimestamp),
84-
topicDataFilter,
85-
};
86-
}, [path, database, selectedPartition, selectedOffset, startTimestamp, topicDataFilter]);
96+
React.useEffect(() => {
97+
if (partitions && partitions.length && isNil(selectedPartition)) {
98+
handleSelectedPartitionChange(partitions[0].partitionId);
99+
handleSelectedOffsetChange(undefined);
100+
handleStartTimestampChange(undefined);
101+
}
102+
}, [
103+
partitions,
104+
selectedPartition,
105+
handleSelectedPartitionChange,
106+
handleSelectedOffsetChange,
107+
handleStartTimestampChange,
108+
]);
87109

88110
const {columnsToShow, columnsToSelect, setColumns} = useSelectedColumns(
89111
getAllColumns(setFullValue),
@@ -93,6 +115,59 @@ export function TopicData({parentRef, path, database}: TopicDataProps) {
93115
REQUIRED_TOPIC_DATA_COLUMNS,
94116
);
95117

118+
const emptyData = React.useMemo(() => !currentData?.Messages?.length, [currentData]);
119+
120+
const tableFilters = React.useMemo(() => {
121+
return {
122+
path,
123+
database,
124+
partition: selectedPartition ?? '',
125+
isEmpty: emptyData,
126+
};
127+
}, [path, database, selectedPartition, emptyData]);
128+
129+
const scrollToOffset = React.useCallback(
130+
(newOffset: number) => {
131+
const scrollTop = (newOffset - (startOffset ?? 0)) * 41;
132+
const normalizedScrollTop = Math.max(0, scrollTop);
133+
parentRef.current?.scrollTo({
134+
top: normalizedScrollTop,
135+
behavior: 'instant',
136+
});
137+
},
138+
[startOffset, parentRef],
139+
);
140+
141+
React.useEffect(() => {
142+
if (isFetching) {
143+
return;
144+
}
145+
const messages = currentData?.Messages;
146+
if (messages?.length) {
147+
const messageOffset = safeParseNumber(messages[0].Offset);
148+
//scroll when table is already rendered and calculated it's state
149+
setTimeout(() => {
150+
scrollToOffset(messageOffset);
151+
}, 0);
152+
}
153+
}, [currentData, isFetching, scrollToOffset]);
154+
155+
const scrollToStartOffset = React.useCallback(() => {
156+
parentRef.current?.scrollTo({
157+
top: 0,
158+
behavior: 'smooth',
159+
});
160+
}, [parentRef]);
161+
162+
const scrollToEndOffset = React.useCallback(() => {
163+
if (parentRef.current) {
164+
parentRef.current.scrollTo({
165+
top: parentRef.current.scrollHeight - parentRef.current.clientHeight,
166+
behavior: 'smooth',
167+
});
168+
}
169+
}, [parentRef]);
170+
96171
const renderControls: RenderControls = () => {
97172
return (
98173
<TopicDataControls
@@ -105,6 +180,8 @@ export function TopicData({parentRef, path, database}: TopicDataProps) {
105180
partitionsError={partitionsError}
106181
initialOffset={startOffset}
107182
endOffset={endOffset}
183+
scrollToStartOffset={scrollToStartOffset}
184+
scrollToEndOffset={scrollToEndOffset}
108185
/>
109186
);
110187
};
@@ -132,19 +209,22 @@ export function TopicData({parentRef, path, database}: TopicDataProps) {
132209
);
133210
};
134211

212+
const getTopicData = React.useMemo(
213+
() => generateTopicDataGetter({setEndOffset, setStartOffset}),
214+
[],
215+
);
216+
135217
return (
136218
<React.Fragment>
137219
<FullValue value={fullValue} onClose={() => setFullValue(undefined)} />
138220
<ResizeablePaginatedTable
139221
columnsWidthLSKey={TOPIC_DATA_COLUMNS_WIDTH_LS_KEY}
140222
parentRef={parentRef}
141223
columns={columnsToShow}
142-
fetchData={generateTopicDataGetter({
143-
setStartOffset,
144-
setEndOffset,
145-
initialOffset: startOffset,
146-
})}
224+
fetchData={getTopicData}
225+
initialEntitiesCount={endOffset - startOffset}
147226
limit={50}
227+
startOffset={startOffset}
148228
renderControls={renderControls}
149229
renderErrorMessage={renderPaginatedTableErrorMessage}
150230
renderEmptyDataMessage={renderEmptyDataMessage}

src/containers/Tenant/Diagnostics/TopicData/TopicDataControls/TopicDataControls.tsx

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,17 @@ import React from 'react';
33
import type {Value} from '@gravity-ui/date-components';
44
import {RelativeDatePicker} from '@gravity-ui/date-components';
55
import {dateTimeParse} from '@gravity-ui/date-utils';
6+
import {ArrowDownToLine, ArrowUpToLine} from '@gravity-ui/icons';
67
import type {TableColumnSetupItem} from '@gravity-ui/uikit';
7-
import {RadioButton, Select, TableColumnSetup} from '@gravity-ui/uikit';
8-
import {isNil} from 'lodash';
8+
import {
9+
ActionTooltip,
10+
Button,
11+
Flex,
12+
Icon,
13+
RadioButton,
14+
Select,
15+
TableColumnSetup,
16+
} from '@gravity-ui/uikit';
917

1018
import {DebouncedInput} from '../../../../../components/DebouncedInput/DebouncedInput';
1119
import {EntitiesCount} from '../../../../../components/EntitiesCount';
@@ -29,6 +37,8 @@ interface TopicDataControlsProps {
2937

3038
initialOffset?: number;
3139
endOffset?: number;
40+
scrollToStartOffset: VoidFunction;
41+
scrollToEndOffset: VoidFunction;
3242
}
3343

3444
export function TopicDataControls({
@@ -40,6 +50,8 @@ export function TopicDataControls({
4050
partitions,
4151
partitionsLoading,
4252
partitionsError,
53+
scrollToStartOffset,
54+
scrollToEndOffset,
4355
}: TopicDataControlsProps) {
4456
const {
4557
selectedPartition,
@@ -66,12 +78,6 @@ export function TopicDataControls({
6678
],
6779
);
6880

69-
React.useEffect(() => {
70-
if (partitions && partitions.length && isNil(selectedPartition)) {
71-
handleSelectedPartitionChange([partitions[0].partitionId]);
72-
}
73-
}, [partitions, selectedPartition, handleSelectedPartitionChange]);
74-
7581
return (
7682
<React.Fragment>
7783
<Select
@@ -99,6 +105,18 @@ export function TopicDataControls({
99105
label={i18n('label_offset')}
100106
current={`${formatNumber(initialOffset)}${formatNumber(endOffset - 1)}`}
101107
/>
108+
<Flex gap={1}>
109+
<ActionTooltip title={i18n('action_scroll-down')}>
110+
<Button onClick={scrollToEndOffset}>
111+
<Icon size={14} data={ArrowDownToLine} />
112+
</Button>
113+
</ActionTooltip>
114+
<ActionTooltip title={i18n('action_scroll-up')}>
115+
<Button onClick={scrollToStartOffset}>
116+
<Icon size={14} data={ArrowUpToLine} />
117+
</Button>
118+
</ActionTooltip>
119+
</Flex>
102120
</React.Fragment>
103121
);
104122
}
@@ -115,9 +133,14 @@ function TopicDataStartControls() {
115133

116134
const onFilterChange = React.useCallback(
117135
(value: TopicDataFilterValue) => {
136+
if (value === 'TIMESTAMP') {
137+
handleSelectedOffsetChange(undefined);
138+
} else {
139+
handleStartTimestampChange(undefined);
140+
}
118141
handleTopicDataFilterChange(value);
119142
},
120-
[handleTopicDataFilterChange],
143+
[handleTopicDataFilterChange, handleSelectedOffsetChange, handleStartTimestampChange],
121144
);
122145
const onStartOffsetChange = React.useCallback(
123146
(value: string) => {

0 commit comments

Comments
 (0)