Skip to content

fix: table is not scrolled to top on sorting #2347

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jun 3, 2025
Merged
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
74 changes: 58 additions & 16 deletions src/components/PaginatedTable/PaginatedTableWithLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,37 +1,79 @@
import React from 'react';

import {TableWithControlsLayout} from '../TableWithControlsLayout/TableWithControlsLayout';
import type {TableProps} from '../TableWithControlsLayout/TableWithControlsLayout';
import type {TableWrapperProps} from '../TableWithControlsLayout/TableWithControlsLayout';

import {PaginatedTableProvider} from './PaginatedTableContext';
import {PaginatedTableProvider, usePaginatedTableState} from './PaginatedTableContext';
import type {PaginatedTableState} from './types';

export interface PaginatedTableWithLayoutProps {
controls: React.ReactNode;
controls?: React.ReactNode;
table: React.ReactNode;
tableProps?: TableProps;
tableWrapperProps?: Omit<TableWrapperProps, 'children'>;
error?: React.ReactNode;
initialState?: Partial<PaginatedTableState>;
fullHeight?: boolean;
noBatching?: boolean;
}

const TableWrapper = ({
table,
tableWrapperProps,
}: {
table: React.ReactNode;
tableWrapperProps?: Omit<TableWrapperProps, 'children'>;
}) => {
const {tableState} = usePaginatedTableState();
const {sortParams} = tableState;

const enhancedTableWrapperProps = React.useMemo(() => {
const existingScrollDependencies = tableWrapperProps?.scrollDependencies || [];

return {
...tableWrapperProps,
scrollDependencies: [...existingScrollDependencies, sortParams],
};
}, [tableWrapperProps, sortParams]);

return (
<TableWithControlsLayout.Table {...enhancedTableWrapperProps}>
{table}
</TableWithControlsLayout.Table>
);
};

const ControlsSection = ({controls}: {controls?: React.ReactNode}) => {
if (!controls) {
return null;
}

return <TableWithControlsLayout.Controls>{controls}</TableWithControlsLayout.Controls>;
};

const ErrorSection = ({error}: {error?: React.ReactNode}) => {
if (!error) {
return null;
}

return <React.Fragment>{error}</React.Fragment>;
};

export const PaginatedTableWithLayout = ({
controls,
table,
tableProps,
tableWrapperProps,
error,
initialState,
noBatching,
fullHeight = true,
}: PaginatedTableWithLayoutProps) => (
<PaginatedTableProvider initialState={initialState} noBatching={noBatching}>
<TableWithControlsLayout fullHeight={fullHeight}>
<TableWithControlsLayout.Controls>{controls}</TableWithControlsLayout.Controls>
{error}
<TableWithControlsLayout.Table {...(tableProps || {})}>
{table}
</TableWithControlsLayout.Table>
</TableWithControlsLayout>
</PaginatedTableProvider>
);
}: PaginatedTableWithLayoutProps) => {
return (
<PaginatedTableProvider initialState={initialState} noBatching={noBatching}>
<TableWithControlsLayout fullHeight={fullHeight}>
<ControlsSection controls={controls} />
<ErrorSection error={error} />
<TableWrapper table={table} tableWrapperProps={tableWrapperProps} />
</TableWithControlsLayout>
</PaginatedTableProvider>
);
};
16 changes: 15 additions & 1 deletion src/components/ResizeableDataTable/ResizeableDataTable.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import type {Column, DataTableProps, Settings} from '@gravity-ui/react-data-table';
import React from 'react';

import type {Column, DataTableProps, Settings, SortOrder} from '@gravity-ui/react-data-table';
import DataTable, {updateColumnsWidth} from '@gravity-ui/react-data-table';
import {Skeleton} from '@gravity-ui/uikit';

Expand All @@ -13,6 +15,7 @@ export interface ResizeableDataTableProps<T> extends Omit<DataTableProps<T>, 'th
columnsWidthLSKey?: string;
wrapperClassName?: string;
loading?: boolean;
onSortChange?: (params: SortOrder | SortOrder[] | undefined) => void;
}

export function ResizeableDataTable<T>({
Expand All @@ -21,10 +24,20 @@ export function ResizeableDataTable<T>({
settings,
wrapperClassName,
loading,
onSort,
onSortChange,
...props
}: ResizeableDataTableProps<T>) {
const [tableColumnsWidth, setTableColumnsWidth] = useTableResize(columnsWidthLSKey);

const handleSort = React.useCallback(
(params: SortOrder | SortOrder[] | undefined) => {
onSort?.(params); // Original onSort if provided
onSortChange?.(params); // Expose sort params to parent
},
[onSort, onSortChange],
);

// If loading is true, override the render method of each column to display a Skeleton
const processedColumns = loading
? columns.map((column: Column<T>) => ({
Expand All @@ -46,6 +59,7 @@ export function ResizeableDataTable<T>({
theme="yandex-cloud"
columns={updatedColumns}
onResize={setTableColumnsWidth}
onSort={handleSort}
settings={newSettings}
{...props}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,17 @@ import './TableWithControlsLayout.scss';
const b = cn('ydb-table-with-controls-layout');

interface TableWithControlsLayoutItemProps {
children?: React.ReactNode;
children: React.ReactNode;
renderExtraControls?: () => React.ReactNode;
className?: string;
fullHeight?: boolean;
}

export interface TableProps extends TableWithControlsLayoutItemProps {
export interface TableWrapperProps extends Omit<TableWithControlsLayoutItemProps, 'children'> {
loading?: boolean;
scrollContainerRef?: React.RefObject<HTMLElement>;
scrollDependencies?: any[];
children: React.ReactNode;
}

export const TableWithControlsLayout = ({
Expand Down Expand Up @@ -56,7 +57,7 @@ TableWithControlsLayout.Table = function Table({
className,
scrollContainerRef,
scrollDependencies = [],
}: TableProps) {
}: TableWrapperProps) {
// Create an internal ref for the table container
const tableContainerRef = React.useRef<HTMLDivElement>(null);

Expand Down
35 changes: 21 additions & 14 deletions src/containers/Nodes/PaginatedNodes/GroupedNodesComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,18 +55,26 @@ const NodeGroup = React.memo(function NodeGroup({
expanded={isExpanded}
onIsExpandedChange={onIsExpandedChange}
>
<NodesTable
path={path}
database={database}
searchValue={searchValue}
problemFilter={'All'}
uptimeFilter={NodesUptimeFilterValues.All}
peerRoleFilter={peerRoleFilter}
filterGroup={name}
filterGroupBy={groupByParam}
initialEntitiesCount={count}
columns={columns}
scrollContainerRef={scrollContainerRef}
<PaginatedTableWithLayout
initialState={{sortParams: undefined}}
table={
<NodesTable
path={path}
database={database}
searchValue={searchValue}
problemFilter={'All'}
uptimeFilter={NodesUptimeFilterValues.All}
peerRoleFilter={peerRoleFilter}
filterGroup={name}
filterGroupBy={groupByParam}
initialEntitiesCount={count}
columns={columns}
scrollContainerRef={scrollContainerRef}
/>
}
tableWrapperProps={{
scrollContainerRef: scrollContainerRef,
}}
/>
</TableGroup>
);
Expand Down Expand Up @@ -184,13 +192,12 @@ export function GroupedNodesComponent({
}
error={error ? <ResponseError error={error} /> : null}
table={renderGroups()}
tableProps={{
tableWrapperProps={{
scrollContainerRef,
scrollDependencies: [searchValue, groupByParam, tableGroups, peerRoleFilter],
loading: isLoading,
className: b('groups-wrapper'),
}}
fullHeight
/>
);
}
3 changes: 1 addition & 2 deletions src/containers/Nodes/PaginatedNodes/NodesComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,10 @@ export function NodesComponent({
scrollContainerRef={scrollContainerRef}
/>
}
tableProps={{
tableWrapperProps={{
scrollContainerRef,
scrollDependencies: [searchValue, problemFilter, uptimeFilter, peerRoleFilter],
}}
fullHeight
/>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,20 +59,28 @@ export const StorageGroupGroup = React.memo(function StorageGroupGroup({
expanded={isExpanded}
onIsExpandedChange={onIsExpandedChange}
>
<PaginatedStorageGroupsTable
database={database}
scrollContainerRef={scrollContainerRef}
nodeId={nodeId}
groupId={groupId}
pDiskId={pDiskId}
filterGroup={name}
filterGroupBy={filterGroupBy}
searchValue={searchValue}
visibleEntities={'all'}
onShowAll={handleShowAllGroups}
renderErrorMessage={renderPaginatedTableErrorMessage}
columns={columns}
initialEntitiesCount={count}
<PaginatedTableWithLayout
initialState={{sortParams: undefined}}
table={
<PaginatedStorageGroupsTable
database={database}
scrollContainerRef={scrollContainerRef}
nodeId={nodeId}
groupId={groupId}
pDiskId={pDiskId}
filterGroup={name}
filterGroupBy={filterGroupBy}
searchValue={searchValue}
visibleEntities={'all'}
onShowAll={handleShowAllGroups}
renderErrorMessage={renderPaginatedTableErrorMessage}
columns={columns}
initialEntitiesCount={count}
/>
}
tableWrapperProps={{
scrollContainerRef: scrollContainerRef,
}}
/>
</TableGroup>
);
Expand Down Expand Up @@ -173,7 +181,7 @@ export function GroupedStorageGroupsComponent({
error={error ? <ResponseError error={error} /> : null}
table={renderGroups()}
initialState={initialState}
tableProps={{
tableWrapperProps={{
scrollContainerRef,
scrollDependencies: [searchValue, storageGroupsGroupByParam, tableGroups],
loading: isLoading,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,10 @@ export function StorageGroupsComponent({
initialEntitiesCount={initialEntitiesCount}
/>
}
tableProps={{
tableWrapperProps={{
scrollContainerRef,
scrollDependencies: [searchValue, visibleEntities],
}}
fullHeight
/>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,21 +60,29 @@ export const StorageNodeGroup = React.memo(function StorageNodeGroup({
expanded={isExpanded}
onIsExpandedChange={onIsExpandedChange}
>
<PaginatedStorageNodesTable
database={database}
scrollContainerRef={scrollContainerRef}
nodeId={nodeId}
groupId={groupId}
filterGroup={name}
filterGroupBy={filterGroupBy}
searchValue={searchValue}
visibleEntities={'all'}
nodesUptimeFilter={NodesUptimeFilterValues.All}
onShowAll={handleShowAllNodes}
renderErrorMessage={renderPaginatedTableErrorMessage}
columns={columns}
initialEntitiesCount={count}
onDataFetched={onDataFetched}
<PaginatedTableWithLayout
initialState={{sortParams: undefined}}
table={
<PaginatedStorageNodesTable
database={database}
scrollContainerRef={scrollContainerRef}
nodeId={nodeId}
groupId={groupId}
filterGroup={name}
filterGroupBy={filterGroupBy}
searchValue={searchValue}
visibleEntities={'all'}
nodesUptimeFilter={NodesUptimeFilterValues.All}
onShowAll={handleShowAllNodes}
renderErrorMessage={renderPaginatedTableErrorMessage}
columns={columns}
initialEntitiesCount={count}
onDataFetched={onDataFetched}
/>
}
tableWrapperProps={{
scrollContainerRef: scrollContainerRef,
}}
/>
</TableGroup>
);
Expand Down Expand Up @@ -174,7 +182,7 @@ export function GroupedStorageNodesComponent({
error={error ? <ResponseError error={error} /> : null}
table={renderGroups()}
initialState={initialState}
tableProps={{
tableWrapperProps={{
scrollContainerRef,
scrollDependencies: [searchValue, storageNodesGroupByParam, tableGroups],
loading: isLoading,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,10 @@ export function StorageNodesComponent({
onDataFetched={handleDataFetched}
/>
}
tableProps={{
tableWrapperProps={{
scrollContainerRef,
scrollDependencies: [searchValue, visibleEntities, nodesUptimeFilter],
}}
fullHeight
/>
);
}
Loading
Loading