diff --git a/src/components/QueryResultTable/QueryResultTable.tsx b/src/components/QueryResultTable/QueryResultTable.tsx
index 3623e959ae..8319e46cb2 100644
--- a/src/components/QueryResultTable/QueryResultTable.tsx
+++ b/src/components/QueryResultTable/QueryResultTable.tsx
@@ -6,6 +6,7 @@ import type {Column, Settings} from '@gravity-ui/react-data-table';
import type {ColumnType, KeyValueRow} from '../../types/api/query';
import {cn} from '../../utils/cn';
import {DEFAULT_TABLE_SETTINGS} from '../../utils/constants';
+import {getColumnWidth} from '../../utils/getColumnWidth';
import {getColumnType, prepareQueryResponse} from '../../utils/query';
import {isNumeric} from '../../utils/utils';
import type {ResizeableDataTableProps} from '../ResizeableDataTable/ResizeableDataTable';
@@ -13,7 +14,6 @@ import {ResizeableDataTable} from '../ResizeableDataTable/ResizeableDataTable';
import {Cell} from './Cell';
import i18n from './i18n';
-import {getColumnWidth} from './utils/getColumnWidth';
import './QueryResultTable.scss';
diff --git a/src/components/QueryResultTable/utils/getColumnWidth.test.ts b/src/components/QueryResultTable/utils/getColumnWidth.test.ts
deleted file mode 100644
index 71cc1e6b41..0000000000
--- a/src/components/QueryResultTable/utils/getColumnWidth.test.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-import {HEADER_PADDING, MAX_COLUMN_WIDTH, getColumnWidth} from './getColumnWidth';
-
-describe('getColumnWidth', () => {
- it('returns minimum width for empty data', () => {
- const result = getColumnWidth({data: [], name: 'test'});
- expect(result).toBe(HEADER_PADDING + 'test'.length * 10);
- });
-
- it('calculates correct width for string columns', () => {
- const data = [{test: 'short'}, {test: 'medium length'}, {test: 'this is a longer string'}];
- const result = getColumnWidth({data, name: 'test'});
- expect(result).toBe(HEADER_PADDING + 'this is a longer string'.length * 10);
- });
-
- it('returns MAX_COLUMN_WIDTH when calculated width exceeds it', () => {
- const data = [{test: 'a'.repeat(100)}];
- const result = getColumnWidth({data, name: 'test'});
- expect(result).toBe(MAX_COLUMN_WIDTH);
- });
-
- it('handles undefined data correctly', () => {
- const result = getColumnWidth({name: 'test'});
- expect(result).toBe(HEADER_PADDING + 'test'.length * 10);
- });
-
- it('handles missing values in data correctly', () => {
- const data = [{test: 'short'}, {}, {test: 'longer string'}];
- const result = getColumnWidth({data, name: 'test'});
- expect(result).toBe(HEADER_PADDING + 'longer string'.length * 10);
- });
-
- it('uses column name length when all values are shorter', () => {
- const data = [{longColumnName: 'a'}, {longColumnName: 'bb'}];
- const result = getColumnWidth({data, name: 'longColumnName'});
- expect(result).toBe(HEADER_PADDING + 'longColumnName'.length * 10);
- });
-});
diff --git a/src/components/QueryResultTable/utils/getColumnWidth.ts b/src/components/QueryResultTable/utils/getColumnWidth.ts
deleted file mode 100644
index 9a8d23971c..0000000000
--- a/src/components/QueryResultTable/utils/getColumnWidth.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-import type {KeyValueRow} from '../../../types/api/query';
-
-export const MAX_COLUMN_WIDTH = 600;
-export const HEADER_PADDING = 20;
-
-export const getColumnWidth = ({data, name}: {data?: KeyValueRow[]; name: string}) => {
- let maxColumnContentLength = name.length;
-
- if (data) {
- for (const row of data) {
- const cellLength = row[name] ? String(row[name]).length : 0;
- maxColumnContentLength = Math.max(maxColumnContentLength, cellLength);
-
- if (maxColumnContentLength * 10 + HEADER_PADDING >= MAX_COLUMN_WIDTH) {
- return MAX_COLUMN_WIDTH;
- }
- }
- }
-
- return maxColumnContentLength * 10 + HEADER_PADDING;
-};
diff --git a/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx b/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx
index b12a1d83ad..5361e04bfb 100644
--- a/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx
+++ b/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx
@@ -71,20 +71,20 @@ export const SchemaViewer = ({type, path, tenantName, extended = false}: SchemaV
const columns = React.useMemo(() => {
if (isViewType(type)) {
- return getViewColumns();
+ return getViewColumns(tableData);
}
if (isExternalTableType(type)) {
- return getExternalTableColumns();
+ return getExternalTableColumns(tableData);
}
if (isColumnEntityType(type)) {
- return getColumnTableColumns();
+ return getColumnTableColumns(tableData);
}
if (isRowTableType(type)) {
- return getRowTableColumns(extended, hasAutoIncrement, hasDefaultValue);
+ return getRowTableColumns(tableData, extended, hasAutoIncrement, hasDefaultValue);
}
return [];
- }, [type, extended, hasAutoIncrement, hasDefaultValue]);
+ }, [type, extended, hasAutoIncrement, hasDefaultValue, tableData]);
if (loading || isViewSchemaLoading) {
return ;
diff --git a/src/containers/Tenant/Schema/SchemaViewer/columns.tsx b/src/containers/Tenant/Schema/SchemaViewer/columns.tsx
index 210a8a6a98..ec87df93e3 100644
--- a/src/containers/Tenant/Schema/SchemaViewer/columns.tsx
+++ b/src/containers/Tenant/Schema/SchemaViewer/columns.tsx
@@ -1,5 +1,7 @@
import DataTable from '@gravity-ui/react-data-table';
+import {getColumnWidth} from '../../../../utils/getColumnWidth';
+
import i18n from './i18n';
import type {SchemaColumn, SchemaData} from './types';
@@ -108,16 +110,37 @@ const compressionColumn: SchemaColumn = {
render: ({row}) => row.columnCodec,
};
-export function getViewColumns(): SchemaColumn[] {
- return [nameColumn, typeColumn];
+const WIDTH_PREDICTION_ROWS_COUNT = 100;
+
+function normalizeColumns(columns: SchemaColumn[], data?: SchemaData[]) {
+ if (!data) {
+ return columns;
+ }
+ const dataSlice = data.slice(0, WIDTH_PREDICTION_ROWS_COUNT);
+ return columns.map((column) => {
+ return {
+ ...column,
+ width: getColumnWidth({
+ data: dataSlice,
+ name: column.name,
+ header: typeof column.header === 'string' ? column.header : undefined,
+ sortable: column.sortable || column.sortable === undefined,
+ }),
+ };
+ });
+}
+
+export function getViewColumns(data?: SchemaData[]): SchemaColumn[] {
+ return normalizeColumns([nameColumn, typeColumn], data);
}
-export function getExternalTableColumns(): SchemaColumn[] {
- return [idColumn, nameColumn, typeColumn, notNullColumn];
+export function getExternalTableColumns(data?: SchemaData[]): SchemaColumn[] {
+ return normalizeColumns([idColumn, nameColumn, typeColumn, notNullColumn], data);
}
-export function getColumnTableColumns(): SchemaColumn[] {
- return [idColumn, nameColumn, typeColumn, notNullColumn];
+export function getColumnTableColumns(data?: SchemaData[]): SchemaColumn[] {
+ return normalizeColumns([idColumn, nameColumn, typeColumn, notNullColumn], data);
}
export function getRowTableColumns(
+ data: SchemaData[] | undefined,
extended: boolean,
hasAutoIncrement: boolean,
hasDefaultValue: boolean,
@@ -136,5 +159,5 @@ export function getRowTableColumns(
rowTableColumns.push(autoIncrementColumn);
}
- return rowTableColumns;
+ return normalizeColumns(rowTableColumns, data);
}
diff --git a/src/containers/Tenant/Schema/SchemaViewer/types.ts b/src/containers/Tenant/Schema/SchemaViewer/types.ts
index 3ed84ffff9..9a77d19591 100644
--- a/src/containers/Tenant/Schema/SchemaViewer/types.ts
+++ b/src/containers/Tenant/Schema/SchemaViewer/types.ts
@@ -1,6 +1,6 @@
import type {Column} from '@gravity-ui/react-data-table';
-export interface SchemaData {
+export type SchemaData = {
id?: number;
name?: string;
keyColumnIndex?: number;
@@ -12,7 +12,7 @@ export interface SchemaData {
prefferedPoolKind?: string;
columnCodec?: string;
defaultValue?: string | number | boolean;
-}
+};
export interface SchemaColumn extends Column {
name: keyof SchemaData;
diff --git a/src/utils/__test__/getColumnWidth.test.ts b/src/utils/__test__/getColumnWidth.test.ts
new file mode 100644
index 0000000000..26bf8387fb
--- /dev/null
+++ b/src/utils/__test__/getColumnWidth.test.ts
@@ -0,0 +1,100 @@
+import {
+ HEADER_PADDING,
+ MAX_COLUMN_WIDTH,
+ PIXELS_PER_CHARACTER,
+ SORT_ICON_TO_CHARACTERS,
+ getColumnWidth,
+} from '../getColumnWidth';
+
+describe('getColumnWidth', () => {
+ it('returns minimum width for empty data', () => {
+ const result = getColumnWidth({data: [], name: 'test'});
+ expect(result).toBe(HEADER_PADDING + 'test'.length * PIXELS_PER_CHARACTER);
+ });
+
+ it('calculates correct width for string columns', () => {
+ const data = [{test: 'short'}, {test: 'medium length'}, {test: 'this is a longer string'}];
+ const result = getColumnWidth({data, name: 'test'});
+ expect(result).toBe(
+ HEADER_PADDING + 'this is a longer string'.length * PIXELS_PER_CHARACTER,
+ );
+ });
+
+ it('calculates correct width for columns with sorting', () => {
+ const result = getColumnWidth({data: [], name: 'test', sortable: true});
+ expect(result).toBe(
+ HEADER_PADDING + ('test'.length + SORT_ICON_TO_CHARACTERS) * PIXELS_PER_CHARACTER,
+ );
+ });
+ it('calculates correct width for columns with sorting and column name wider than header', () => {
+ const data = [{test: 'this is a longer string'}];
+ const result = getColumnWidth({data, name: 'test', sortable: true});
+ expect(result).toBe(
+ HEADER_PADDING + 'this is a longer string'.length * PIXELS_PER_CHARACTER,
+ );
+ });
+
+ it('calculates correct width for columns with header', () => {
+ const result = getColumnWidth({data: [], name: 'test', header: 'a'});
+ expect(result).toBe(HEADER_PADDING + 'a'.length * PIXELS_PER_CHARACTER);
+ });
+
+ it('returns MAX_COLUMN_WIDTH when calculated width exceeds it', () => {
+ const data = [{test: 'a'.repeat(100)}];
+ const result = getColumnWidth({data, name: 'test'});
+ expect(result).toBe(MAX_COLUMN_WIDTH);
+ });
+
+ it('handles undefined data correctly', () => {
+ const result = getColumnWidth({name: 'test'});
+ expect(result).toBe(HEADER_PADDING + 'test'.length * PIXELS_PER_CHARACTER);
+ });
+
+ it('handles missing values in data correctly', () => {
+ const data = [{test: 'short'}, {}, {test: 'longer string'}];
+ const result = getColumnWidth({data, name: 'test'});
+ expect(result).toBe(HEADER_PADDING + 'longer string'.length * PIXELS_PER_CHARACTER);
+ });
+
+ it('uses column name length when all values are shorter', () => {
+ const data = [{longColumnName: 'a'}, {longColumnName: 'bb'}];
+ const result = getColumnWidth({data, name: 'longColumnName'});
+ expect(result).toBe(HEADER_PADDING + 'longColumnName'.length * PIXELS_PER_CHARACTER);
+ });
+
+ it('handles null values in data correctly', () => {
+ const data = [{test: 'a'}, {test: null}];
+ const result = getColumnWidth({data, name: 'test'});
+ expect(result).toBe(HEADER_PADDING + 'test'.length * PIXELS_PER_CHARACTER);
+ });
+
+ it('handles undefined values in data correctly', () => {
+ const data = [{test: 'a'}, {test: undefined}];
+ const result = getColumnWidth({data, name: 'test'});
+ expect(result).toBe(HEADER_PADDING + 'test'.length * PIXELS_PER_CHARACTER);
+ });
+
+ it('handles empty string values in data correctly', () => {
+ const data = [{test: 'short'}, {test: ''}, {test: 'longer string'}];
+ const result = getColumnWidth({data, name: 'test'});
+ expect(result).toBe(HEADER_PADDING + 'longer string'.length * PIXELS_PER_CHARACTER);
+ });
+
+ it('handles an array of numbers correctly', () => {
+ const data = [{test: 1}, {test: 123}, {test: 12345}];
+ const result = getColumnWidth({data, name: 'test'});
+ expect(result).toBe(HEADER_PADDING + '12345'.length * PIXELS_PER_CHARACTER);
+ });
+
+ it('handles an array of mixed data types correctly', () => {
+ const data = [{test: 'short'}, {test: 123}, {test: null}, {test: 'longer string'}];
+ const result = getColumnWidth({data, name: 'test'});
+ expect(result).toBe(HEADER_PADDING + 'longer string'.length * PIXELS_PER_CHARACTER);
+ });
+
+ it('handles empty name correctly', () => {
+ const data = [{test: 'test'}];
+ const result = getColumnWidth({data, name: ''});
+ expect(result).toBe(HEADER_PADDING);
+ });
+});
diff --git a/src/utils/getColumnWidth.ts b/src/utils/getColumnWidth.ts
new file mode 100644
index 0000000000..ae2dea5e5c
--- /dev/null
+++ b/src/utils/getColumnWidth.ts
@@ -0,0 +1,42 @@
+export const MAX_COLUMN_WIDTH = 600;
+export const HEADER_PADDING = 20;
+export const SORT_ICON_TO_CHARACTERS = 2;
+export const PIXELS_PER_CHARACTER = 10;
+
+export function getColumnWidth({
+ data,
+ name,
+ header,
+ sortable,
+}: {
+ data?: Record[];
+ name: string;
+ header?: string;
+ sortable?: boolean;
+}) {
+ const headerContentLength = typeof header === 'string' ? header.length : name.length;
+
+ let maxColumnContentLength = sortable
+ ? headerContentLength + SORT_ICON_TO_CHARACTERS
+ : headerContentLength;
+
+ if (data) {
+ for (const row of data) {
+ let cellLength = 0;
+ if (row[name]) {
+ cellLength = String(row[name]).length;
+ }
+
+ maxColumnContentLength = Math.max(maxColumnContentLength, cellLength);
+
+ if (
+ maxColumnContentLength * PIXELS_PER_CHARACTER + HEADER_PADDING >=
+ MAX_COLUMN_WIDTH
+ ) {
+ return MAX_COLUMN_WIDTH;
+ }
+ }
+ }
+
+ return maxColumnContentLength * PIXELS_PER_CHARACTER + HEADER_PADDING;
+}