Skip to content

Commit

Permalink
vtadmin: enable sorting in all tables
Browse files Browse the repository at this point in the history
Signed-off-by: c-r-dev <[email protected]>
  • Loading branch information
c-r-dev committed Jan 6, 2025
1 parent 549a8c4 commit d95149c
Show file tree
Hide file tree
Showing 2 changed files with 153 additions and 9 deletions.
142 changes: 142 additions & 0 deletions web/vtadmin/src/components/dataTable/SortedDataTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/**
* Copyright 2025 The Vitess Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import * as React from 'react';
import { useLocation } from 'react-router-dom';

import { useURLPagination } from '../../hooks/useURLPagination';
import { useURLQuery } from '../../hooks/useURLQuery';
import { stringify } from '../../util/queryString';
import { PaginationNav } from './PaginationNav';
import { useCallback, useMemo, useState } from 'react';

export interface ColumnProps {
// Coulmn display name string | JSX.Element
display: string| JSX.Element,
// Column data accessor
accessor: string
}

interface Props<T> {
// When passing a JSX.Element, note that the column element
// will be rendered *inside* a <th> tag. (Note: I don't love this
// abstraction + we'll likely want to revisit this when we add
// table sorting.)
columns: Array<ColumnProps>;
data: T[];
pageSize?: number;
renderRows: (rows: T[]) => JSX.Element[];
title?: string;
// Pass a unique `pageKey` for each DataTable, in case multiple
// DataTables access the same URL. This will be used to
// access page number from the URL.
pageKey?: string;
}

// Generally, page sizes of ~100 rows are fine in terms of performance,
// but anything over ~50 feels unwieldy in terms of UX.
const DEFAULT_PAGE_SIZE = 50;

export const SortedDataTable = <T extends object>({
columns,
data,
pageSize = DEFAULT_PAGE_SIZE,
renderRows,
title,
pageKey = '',
}: Props<T>) => {
const { pathname } = useLocation();
const urlQuery = useURLQuery();

const pageQueryKey = `${pageKey}page`;

const totalPages = Math.ceil(data.length / pageSize);
const { page } = useURLPagination({ totalPages, pageQueryKey });

const startIndex = (page - 1) * pageSize;
const endIndex = startIndex + pageSize;

const startRow = startIndex + 1;
const lastRow = Math.min(data.length, startIndex + pageSize);

const formatPageLink = (p: number) => ({
pathname,
search: stringify({ ...urlQuery.query, [pageQueryKey]: p === 1 ? undefined : p }),
});

const [sortColumn, setSortColumn] = useState(null);
const [sortOrder, setSortOrder] = useState('asc');

const handleSort = useCallback((column: any) => {
if (sortColumn === column) {
setSortOrder(sortOrder === 'asc' ? 'desc' : 'asc');
} else {
setSortColumn(column);
setSortOrder('asc');
}
}, [sortColumn, sortOrder]);

const sortedData = useMemo(() => {
if (!sortColumn) return data;

const compare = (a: { [x: string]: any; }, b: { [x: string]: any; }) => {
const valueA = a[sortColumn];
const valueB = b[sortColumn];

if (valueA < valueB) {
return sortOrder === 'asc' ? -1 : 1;
} else if (valueA > valueB) {
return sortOrder === 'asc' ? 1 : -1;
} else {
return 0;
}
};

return [...data].sort(compare);
}, [data, sortColumn, sortOrder]);

const dataPage = sortedData.slice(startIndex, endIndex);


return (
<div>
<table>
{title && <caption>{title}</caption>}
<thead>
<tr>
{columns.map((col, cdx) => (
<th key={cdx} onClick={()=> handleSort(col.accessor)}>
<div style={{ display: 'flex' }}>
{col.display}
{sortColumn === col.accessor && (
<span>{sortOrder === 'asc' ? '▲' : '▼'}</span>
)}
</div>
</th>
))}
</tr>
</thead>
<tbody>{renderRows(dataPage)}</tbody>
</table>

<PaginationNav currentPage={page} formatLink={formatPageLink} totalPages={totalPages} />
{!!data.length && (
<p className="text-secondary">
Showing {startRow} {lastRow > startRow ? `- ${lastRow}` : null} of {data.length}
</p>
)}
</div>
);
};
20 changes: 11 additions & 9 deletions web/vtadmin/src/components/routes/Schemas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,18 @@ import { formatBytes } from '../../util/formatBytes';
import { getTableDefinitions } from '../../util/tableDefinitions';
import { DataCell } from '../dataTable/DataCell';
import { DataFilter } from '../dataTable/DataFilter';
import { DataTable } from '../dataTable/DataTable';
import { ColumnProps, SortedDataTable } from '../dataTable/SortedDataTable';
import { ContentContainer } from '../layout/ContentContainer';
import { WorkspaceHeader } from '../layout/WorkspaceHeader';
import { WorkspaceTitle } from '../layout/WorkspaceTitle';
import { KeyspaceLink } from '../links/KeyspaceLink';
import { QueryLoadingPlaceholder } from '../placeholders/QueryLoadingPlaceholder';
import { HelpTooltip } from '../tooltip/HelpTooltip';

const TABLE_COLUMNS = [
'Keyspace',
'Table',
<div className="text-right">
const TABLE_COLUMNS : Array<ColumnProps> = [
{display: 'Keyspace', accessor : 'keyspace'},
{display: 'Table' , accessor : 'table'},
{display : <div className="text-right">
Approx. Size{' '}
<HelpTooltip
text={
Expand All @@ -45,8 +45,8 @@ const TABLE_COLUMNS = [
</span>
}
/>
</div>,
<div className="text-right">
</div>, accessor : '_tableSize'},
{display: <div className="text-right">
Approx. Rows{' '}
<HelpTooltip
text={
Expand All @@ -57,7 +57,7 @@ const TABLE_COLUMNS = [
</span>
}
/>
</div>,
</div>, accessor : '_tableRowCount'},
];

export const Schemas = () => {
Expand All @@ -74,6 +74,8 @@ export const Schemas = () => {
clusterID: d.cluster?.id,
keyspace: d.keyspace,
table: d.tableDefinition?.name,
_tableSize: d.tableSize?.data_length || 0,
_tableRowCount: d.tableSize?.row_count || 0,
_raw: d,
}));

Expand Down Expand Up @@ -120,7 +122,7 @@ export const Schemas = () => {
placeholder="Filter schemas"
value={filter || ''}
/>
<DataTable columns={TABLE_COLUMNS} data={filteredData} renderRows={renderRows} />
<SortedDataTable columns={TABLE_COLUMNS} data={filteredData} renderRows={renderRows} />
<QueryLoadingPlaceholder query={schemasQuery} />
</ContentContainer>
</div>
Expand Down

0 comments on commit d95149c

Please sign in to comment.