Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/components/PaginatedTable/PaginatedTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export interface PaginatedTableProps<T, F> {
containerClassName?: string;
onDataFetched?: (data: PaginatedTableData<T>) => void;
keepCache?: boolean;
useColumnsIdsInRequest?: boolean;
}

const DEFAULT_PAGINATION_LIMIT = 20;
Expand All @@ -53,6 +54,7 @@ export const PaginatedTable = <T, F>({
containerClassName,
onDataFetched,
keepCache = true,
useColumnsIdsInRequest = true,
}: PaginatedTableProps<T, F>) => {
// Get state and setters from context
const {tableState, setSortParams, setTotalEntities, setFoundEntities, setIsInitialLoad} =
Expand Down Expand Up @@ -122,6 +124,7 @@ export const PaginatedTable = <T, F>({
renderEmptyDataMessage={renderEmptyDataMessage}
onDataFetched={handleDataFetched}
keepCache={keepCache}
useColumnsIdsInRequest={useColumnsIdsInRequest}
/>
</tbody>
</table>
Expand Down
10 changes: 8 additions & 2 deletions src/components/PaginatedTable/TableChunk.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ interface TableChunkProps<T, F> {
onDataFetched: (data?: PaginatedTableData<T>) => void;

keepCache?: boolean;
useColumnsIdsInRequest?: boolean;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest to name this prop something like "preserveColsOrderInRequest"

useColumnsIdsInRequest is

looks like hook name
doesnt explicitly state how they are actually used

}

// Memoisation prevents chunks rerenders that could cause perfomance issues on big tables
Expand All @@ -61,13 +62,18 @@ export const TableChunk = typedMemo(function TableChunk<T, F>({
shouldFetch,
shouldRender,
keepCache,
useColumnsIdsInRequest,
}: TableChunkProps<T, F>) {
const [isTimeoutActive, setIsTimeoutActive] = React.useState(true);
const [autoRefreshInterval] = useAutoRefreshInterval();
const {noBatching} = usePaginatedTableState();

//sort ids to prevent refetch if only order was changed
const columnsIds = columns.map((column) => column.name).toSorted();
const columnsIds = React.useMemo(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

may be if !useColumnsIdsInRequest, columnsIds will be []? To avoid nullish coalescing in getNodes фтв getGroups

() =>
//sort ids to prevent refetch if only order was changed
useColumnsIdsInRequest ? columns.map((column) => column.name).toSorted() : [],
[columns, useColumnsIdsInRequest],
);

const queryParams = {
offset: id * chunkSize,
Expand Down
4 changes: 4 additions & 0 deletions src/components/PaginatedTable/TableChunksRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export interface TableChunksRendererProps<T, F> {
renderEmptyDataMessage?: RenderEmptyDataMessage;
onDataFetched: (data?: PaginatedTableData<T>) => void;
keepCache: boolean;
useColumnsIdsInRequest?: boolean;
}

export const TableChunksRenderer = <T, F>({
Expand All @@ -47,6 +48,7 @@ export const TableChunksRenderer = <T, F>({
renderEmptyDataMessage,
onDataFetched,
keepCache,
useColumnsIdsInRequest,
}: TableChunksRendererProps<T, F>) => {
const chunkStates = useScrollBasedChunks({
scrollContainerRef,
Expand Down Expand Up @@ -125,6 +127,7 @@ export const TableChunksRenderer = <T, F>({
shouldFetch={chunkState.shouldFetch}
shouldRender={chunkState.shouldRender}
keepCache={keepCache}
useColumnsIdsInRequest={useColumnsIdsInRequest}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently we pass useColumnsIdsInRequest down the components tree to determine fetch options

The question is - do we use the same entities table with and without useColumnsIdsInRequest simultenously?

If not - I suggest to move this to fetching logics

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understood you correctly, you suggest not to pass the useColumnsIdsInRequest prop down the component tree, but instead decide whether to include columnsIds in the fetch options inside the fetching logic itself.
In my case most tables always use column IDs in the request, and the peers table never uses them. So I can do something like:
const shouldUseColumnsIds = tableName !== 'node-peers'; (or use a map of table names) directly in the fetching logic instead of passing a prop from the UI.

Is that what you meant?

/>
);
},
Expand All @@ -143,6 +146,7 @@ export const TableChunksRenderer = <T, F>({
rowHeight,
sortParams,
tableName,
useColumnsIdsInRequest,
],
);

Expand Down
2 changes: 1 addition & 1 deletion src/components/nodesColumns/__test__/utils.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {UNBREAKABLE_GAP} from '../../../utils/utils';
import {UNBREAKABLE_GAP} from '../../../utils/constants';
import {prepareClockSkewValue, preparePingTimeValue} from '../utils';

describe('preparePingTimeValue', () => {
Expand Down
1 change: 1 addition & 0 deletions src/containers/Node/NodeNetwork/NodeNetworkTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export function NodeNetworkTable({
fetchData={getNodePeers}
filters={filters}
tableName={i18n('table_node-peers')}
useColumnsIdsInRequest={false}
renderErrorMessage={renderPaginatedTableErrorMessage}
renderEmptyDataMessage={renderEmptyDataMessage}
onDataFetched={onDataFetched}
Expand Down
16 changes: 11 additions & 5 deletions src/containers/Node/NodeNetwork/columns.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ import {
} from '../../../components/nodesColumns/columns';
import type {GetNodesColumnsParams} from '../../../components/nodesColumns/types';
import {EMPTY_DATA_PLACEHOLDER} from '../../../lib';
import {formatBytes} from '../../../utils/bytesParsers';
import {formatDateTime} from '../../../utils/dataFormatters/dataFormatters';
import type {Column} from '../../../utils/tableUtils/types';
import {bytesToMB, isNumeric} from '../../../utils/utils';

import {
NODE_NETWORK_COLUMNS_IDS,
Expand All @@ -35,15 +35,22 @@ function getPeerConnectTimeColumn<T extends {ConnectTime?: string}>(): Column<T>
};
}

function renderSent(bytes?: number | string) {
return formatBytes({
value: bytes,
size: 'mb',
withSizeLabel: true,
});
}
Comment on lines +38 to +44
Copy link

Copilot AI Dec 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function name renderSent is misleading as it's used for both sent and received bytes. Consider renaming it to something more generic like renderBytes or formatBytesColumn to better reflect its actual usage.

Copilot uses AI. Check for mistakes.

function getPeerSentBytesColumn<T extends {BytesSend?: string | number}>(): Column<T> {
return {
name: NODE_NETWORK_COLUMNS_IDS.BytesSend,
header: NODE_NETWORK_COLUMNS_TITLES.BytesSend,
align: DataTable.RIGHT,
width: 140,
resizeMinWidth: 120,
render: ({row}) =>
isNumeric(row.BytesSend) ? bytesToMB(row.BytesSend, 0) : EMPTY_DATA_PLACEHOLDER,
render: ({row}) => renderSent(row.BytesSend) || EMPTY_DATA_PLACEHOLDER,
};
}

Expand All @@ -54,8 +61,7 @@ function getPeerReceivedBytesColumn<T extends {BytesReceived?: string | number}>
align: DataTable.RIGHT,
width: 160,
resizeMinWidth: 130,
render: ({row}) =>
isNumeric(row.BytesReceived) ? bytesToMB(row.BytesReceived, 0) : EMPTY_DATA_PLACEHOLDER,
render: ({row}) => renderSent(row.BytesReceived) || EMPTY_DATA_PLACEHOLDER,
};
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {cn} from '../../../../../../utils/cn';
import {NON_BREAKING_SPACE} from '../../../../../../utils/constants';
import {UNBREAKABLE_GAP} from '../../../../../../utils/constants';
import {ServerlessTabCard} from '../../TabCard/ServerlessTabCard';

import '../MetricsTabs.scss';
Expand All @@ -11,10 +11,10 @@ export function PlaceholderTab() {
<div className={b('link-container', {placeholder: true})}>
<div className={b('link')}>
<ServerlessTabCard
text={NON_BREAKING_SPACE}
text={UNBREAKABLE_GAP}
active={false}
helpText={undefined}
subtitle={NON_BREAKING_SPACE}
subtitle={UNBREAKABLE_GAP}
/>
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/utils/bytesParsers/__test__/formatBytes.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {UNBREAKABLE_GAP} from '../../utils';
import {UNBREAKABLE_GAP} from '../../constants';
import {formatBytes} from '../formatBytes';

describe('formatBytes', () => {
Expand Down
4 changes: 2 additions & 2 deletions src/utils/bytesParsers/formatBytes.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {GIGABYTE, KILOBYTE, MEGABYTE, TERABYTE} from '../constants';
import {GIGABYTE, KILOBYTE, MEGABYTE, TERABYTE, UNBREAKABLE_GAP} from '../constants';
import type {FormatToSizeArgs, FormatValuesArgs} from '../dataFormatters/common';
import {formatNumber, roundToPrecision} from '../dataFormatters/dataFormatters';
import {UNBREAKABLE_GAP, isNumeric} from '../utils';
import {isNumeric} from '../utils';

import i18n from './i18n';

Expand Down
2 changes: 1 addition & 1 deletion src/utils/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export const SECTION_IDS = {
export const TENANT_OVERVIEW_TABLES_LIMIT = 3;

export const EMPTY_DATA_PLACEHOLDER = '—';
export const NON_BREAKING_SPACE = '\u00A0';
export const UNBREAKABLE_GAP = '\u00A0';

export const QUERY_TECHNICAL_MARK = '/*UI-QUERY-EXCLUDE*/';

Expand Down
2 changes: 1 addition & 1 deletion src/utils/dataFormatters/__test__/formatNumbers.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {UNBREAKABLE_GAP} from '../../utils';
import {UNBREAKABLE_GAP} from '../../constants';
import {formatNumericValues} from '../dataFormatters';

describe('formatNumericValues', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {UNBREAKABLE_GAP} from '../../utils';
import {UNBREAKABLE_GAP} from '../../constants';
import {formatStorageValues} from '../dataFormatters';

describe('formatStorageValues', () => {
Expand Down
2 changes: 1 addition & 1 deletion src/utils/dataFormatters/__test__/formatUptime.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {UNBREAKABLE_GAP} from '../../utils';
import {UNBREAKABLE_GAP} from '../../constants';
import {
formatUptimeInSeconds,
getDowntimeFromDateFormatted,
Expand Down
4 changes: 2 additions & 2 deletions src/utils/dataFormatters/dataFormatters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import {dateTimeParse, duration} from '@gravity-ui/date-utils';
import type {TVDiskID, TVSlotId} from '../../types/api/vdisk';
import {formatBytes as formatBytesCustom, getBytesSizeUnit} from '../bytesParsers/formatBytes';
import type {BytesSizes} from '../bytesParsers/formatBytes';
import {HOUR_IN_SECONDS} from '../constants';
import {HOUR_IN_SECONDS, UNBREAKABLE_GAP} from '../constants';
import {configuredNumeral} from '../numeral';
import {UNBREAKABLE_GAP, isNumeric} from '../utils';
import {isNumeric} from '../utils';

import {formatValues} from './common';
import {formatNumberWithDigits, getNumberSizeUnit} from './formatNumber';
Expand Down
3 changes: 2 additions & 1 deletion src/utils/dataFormatters/formatNumber.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import i18n from '../bytesParsers/i18n';
import {UNBREAKABLE_GAP, isNumeric} from '../utils';
import {UNBREAKABLE_GAP} from '../constants';
import {isNumeric} from '../utils';

import type {FormatToSizeArgs, FormatValuesArgs} from './common';
import {formatNumber, roundToPrecision} from './dataFormatters';
Expand Down
2 changes: 1 addition & 1 deletion src/utils/numeral.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import numeral from 'numeral';
import 'numeral/locales'; // Without this numeral will throw an error when using not 'en' locale

import {UNBREAKABLE_GAP} from './constants';
import {Lang, i18n} from './i18n';
import {UNBREAKABLE_GAP} from './utils';

// Set space delimiter for all locales possible in project
Object.values(Lang).forEach((value) => {
Expand Down
2 changes: 0 additions & 2 deletions src/utils/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,6 @@ export function toExponential(value: number, precision?: number) {
return Number(value).toExponential(precision);
}

export const UNBREAKABLE_GAP = '\xa0';

// Numeric values expected, not numeric value should be displayd as 0
export function safeParseNumber(value: unknown, defaultValue = 0): number {
if (isNumeric(value)) {
Expand Down
Loading