Skip to content

Commit 8dd0678

Browse files
committed
fix: design review fixes
1 parent e790c68 commit 8dd0678

File tree

5 files changed

+148
-30
lines changed

5 files changed

+148
-30
lines changed

src/components/FixedHeightQuery/FixedHeightQuery.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
height: 100% !important;
2525
margin: 0 !important;
2626
padding: var(--g-spacing-2) !important;
27+
padding-left: 0 !important;
2728

2829
white-space: pre-wrap !important;
2930
text-overflow: ellipsis !important;

src/containers/Tenant/Diagnostics/Diagnostics.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import {setDiagnosticsTab} from '../../../store/reducers/tenant/tenant';
1616
import type {AdditionalNodesProps, AdditionalTenantsProps} from '../../../types/additionalProps';
1717
import {uiFactory} from '../../../uiFactory/uiFactory';
1818
import {cn} from '../../../utils/cn';
19-
import {useTypedDispatch, useTypedSelector} from '../../../utils/hooks';
19+
import {useScrollPosition, useTypedDispatch, useTypedSelector} from '../../../utils/hooks';
2020
import {Heatmap} from '../../Heatmap';
2121
import {Nodes} from '../../Nodes/Nodes';
2222
import {Operations} from '../../Operations';
@@ -200,6 +200,12 @@ function Diagnostics(props: DiagnosticsProps) {
200200
);
201201
};
202202

203+
useScrollPosition(
204+
containerRef,
205+
`tenant-diagnostics-${tenantName}-${activeTab?.id}`,
206+
activeTab?.id === TENANT_DIAGNOSTICS_TABS_IDS.overview,
207+
);
208+
203209
return (
204210
<div className={b()}>
205211
{activeTab ? (

src/containers/Tenant/Diagnostics/TopQueries/columns/columns.tsx

Lines changed: 20 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import DataTable from '@gravity-ui/react-data-table';
22
import type {Column, OrderType} from '@gravity-ui/react-data-table';
33

44
import {FixedHeightQuery} from '../../../../../components/FixedHeightQuery/FixedHeightQuery';
5-
import {YDBSyntaxHighlighter} from '../../../../../components/SyntaxHighlighter/YDBSyntaxHighlighter';
65
import type {KeyValueRow} from '../../../../../types/api/query';
76
import {cn} from '../../../../../utils/cn';
87
import {formatDateTime, formatNumber} from '../../../../../utils/dataFormatters/dataFormatters';
@@ -28,15 +27,22 @@ const cpuTimeUsColumn: Column<KeyValueRow> = {
2827
align: DataTable.RIGHT,
2928
};
3029

31-
const queryTextColumn: Column<KeyValueRow> = {
32-
name: QUERIES_COLUMNS_IDS.QueryText,
33-
header: QUERIES_COLUMNS_TITLES.QueryText,
34-
render: ({row}) => (
35-
<div className={b('query')}>
36-
<FixedHeightQuery value={row.QueryText?.toString()} lines={3} hasClipboardButton />
37-
</div>
38-
),
39-
width: 500,
30+
const getQueryTextColumn = (lines = 3) => {
31+
const queryTextColumn: Column<KeyValueRow> = {
32+
name: QUERIES_COLUMNS_IDS.QueryText,
33+
header: QUERIES_COLUMNS_TITLES.QueryText,
34+
render: ({row}) => (
35+
<div className={b('query')}>
36+
<FixedHeightQuery
37+
value={row.QueryText?.toString()}
38+
lines={lines}
39+
hasClipboardButton
40+
/>
41+
</div>
42+
),
43+
width: 500,
44+
};
45+
return queryTextColumn;
4046
};
4147

4248
const endTimeColumn: Column<KeyValueRow> = {
@@ -71,25 +77,10 @@ const userSIDColumn: Column<KeyValueRow> = {
7177
width: 120,
7278
};
7379

74-
const oneLineQueryTextColumn: Column<KeyValueRow> = {
75-
name: QUERIES_COLUMNS_IDS.OneLineQueryText,
76-
header: QUERIES_COLUMNS_TITLES.OneLineQueryText,
77-
render: ({row}) => (
78-
<YDBSyntaxHighlighter
79-
language="yql"
80-
text={row.QueryText?.toString() || ''}
81-
withClipboardButton={{
82-
withLabel: false,
83-
}}
84-
/>
85-
),
86-
width: 500,
87-
};
88-
8980
const queryHashColumn: Column<KeyValueRow> = {
9081
name: QUERIES_COLUMNS_IDS.QueryHash,
9182
header: QUERIES_COLUMNS_TITLES.QueryHash,
92-
render: ({row}) => generateHash(String(row.QueryText)),
83+
render: ({row}) => <div className={b('query-hash')}>{generateHash(String(row.QueryText))}</div>,
9384
width: 130,
9485
};
9586

@@ -130,7 +121,7 @@ export function getTopQueriesColumns() {
130121
durationColumn,
131122
readBytesColumn,
132123
requestUnitsColumn,
133-
queryTextColumn,
124+
getQueryTextColumn(),
134125
endTimeColumn,
135126
readRowsColumn,
136127
userSIDColumn,
@@ -144,14 +135,14 @@ export function getTopQueriesColumns() {
144135
}
145136

146137
export function getTenantOverviewTopQueriesColumns() {
147-
return [queryHashColumn, oneLineQueryTextColumn, cpuTimeUsColumn];
138+
return [queryHashColumn, getQueryTextColumn(6), cpuTimeUsColumn];
148139
}
149140
export function getRunningQueriesColumns() {
150141
const columns = [
151142
queryHashColumn,
152143
userSIDColumn,
153144
queryStartColumn,
154-
queryTextColumn,
145+
getQueryTextColumn(),
155146
applicationColumn,
156147
];
157148

src/utils/hooks/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ export * from './useAutoRefreshInterval';
99
export * from './useEventHandler';
1010
export * from './useDelayed';
1111
export * from './useAclSyntax';
12+
export * from './useScrollPosition';
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
import React from 'react';
2+
3+
import {debounce} from 'lodash';
4+
import {useHistory} from 'react-router-dom';
5+
6+
const DEBAUNCE_DELAY = 100;
7+
8+
/**
9+
* Hook for saving and restoring scroll position on navigation
10+
* Uses sessionStorage to persist scroll position across browser history navigation
11+
* Only restores position when navigating via browser back/forward buttons
12+
*/
13+
export function useScrollPosition(
14+
elementRef: React.RefObject<HTMLElement>,
15+
key: string,
16+
shouldRestore: boolean,
17+
) {
18+
const history = useHistory();
19+
20+
// Save scroll position to sessionStorage
21+
const saveScrollPosition = React.useCallback(
22+
(scrollTop: number) => {
23+
try {
24+
sessionStorage.setItem(`scroll-${key}`, String(scrollTop));
25+
} catch (error) {
26+
// Ignore sessionStorage errors (e.g., in private mode)
27+
console.warn('Failed to save scroll position:', error);
28+
}
29+
},
30+
[key],
31+
);
32+
33+
// Clear scroll position from sessionStorage
34+
const clearScrollPosition = React.useCallback(() => {
35+
try {
36+
sessionStorage.removeItem(`scroll-${key}`);
37+
} catch (error) {
38+
// Ignore sessionStorage errors
39+
console.warn('Failed to clear scroll position:', error);
40+
}
41+
}, [key]);
42+
43+
// Restore scroll position from sessionStorage
44+
const restoreScrollPosition = React.useCallback(() => {
45+
if (!elementRef.current) {
46+
return;
47+
}
48+
49+
try {
50+
const savedPosition = sessionStorage.getItem(`scroll-${key}`);
51+
if (savedPosition !== null) {
52+
const scrollTop = parseInt(savedPosition, 10);
53+
if (!isNaN(scrollTop)) {
54+
elementRef.current.scrollTop = scrollTop;
55+
}
56+
}
57+
} catch (error) {
58+
// Ignore sessionStorage errors
59+
console.warn('Failed to restore scroll position:', error);
60+
}
61+
}, [elementRef, key]);
62+
63+
// Create debounced save function
64+
const debouncedSaveScrollPosition = React.useMemo(
65+
() =>
66+
debounce((scrollTop: number) => {
67+
saveScrollPosition(scrollTop);
68+
}, DEBAUNCE_DELAY),
69+
[saveScrollPosition],
70+
);
71+
72+
// Handle scroll events with debouncing
73+
const handleScroll = React.useCallback(() => {
74+
if (!elementRef.current) {
75+
return;
76+
}
77+
78+
debouncedSaveScrollPosition(elementRef.current.scrollTop);
79+
}, [elementRef, debouncedSaveScrollPosition]);
80+
81+
// Detect if navigation is via browser back/forward
82+
const isHistoryNavigation = React.useMemo(() => {
83+
// Check if this is a browser back/forward navigation
84+
// React Router v5 doesn't provide direct way to detect this,
85+
// but we can use history.action to determine the type of navigation
86+
return history.action === 'POP';
87+
}, [history.action]);
88+
89+
// Handle location changes
90+
React.useLayoutEffect(() => {
91+
if (isHistoryNavigation && shouldRestore) {
92+
// This is a browser back/forward navigation - restore position with timeout to ensure that content rendered
93+
const timeoutId = setTimeout(restoreScrollPosition, 100);
94+
return () => clearTimeout(timeoutId);
95+
} else {
96+
// This is a regular navigation (tab click, page reload) - clear position
97+
clearScrollPosition();
98+
}
99+
}, [
100+
isHistoryNavigation,
101+
restoreScrollPosition,
102+
clearScrollPosition,
103+
elementRef,
104+
shouldRestore,
105+
]);
106+
107+
React.useEffect(() => {
108+
const element = elementRef.current;
109+
if (!element) {
110+
return;
111+
}
112+
113+
element.addEventListener('scroll', handleScroll, {passive: true});
114+
return () => {
115+
element.removeEventListener('scroll', handleScroll);
116+
debouncedSaveScrollPosition.cancel();
117+
};
118+
}, [handleScroll, debouncedSaveScrollPosition, elementRef]);
119+
}

0 commit comments

Comments
 (0)