diff --git a/src/components/AutoRefreshControl/AutoRefreshControl.tsx b/src/components/AutoRefreshControl/AutoRefreshControl.tsx
index 270d6f395..ce24725ae 100644
--- a/src/components/AutoRefreshControl/AutoRefreshControl.tsx
+++ b/src/components/AutoRefreshControl/AutoRefreshControl.tsx
@@ -37,6 +37,7 @@ export function AutoRefreshControl({className}: AutoRefreshControlProps) {
setAutoRefreshInterval(Number(v));
}}
width={85}
+ qa="ydb-autorefresh-select"
>
{i18n('None')}
{i18n('15 sec')}
diff --git a/src/components/PaginatedTable/PaginatedTable.tsx b/src/components/PaginatedTable/PaginatedTable.tsx
index ad794127f..6e9c4cf45 100644
--- a/src/components/PaginatedTable/PaginatedTable.tsx
+++ b/src/components/PaginatedTable/PaginatedTable.tsx
@@ -4,9 +4,7 @@ import {TableWithControlsLayout} from '../TableWithControlsLayout/TableWithContr
import {TableChunk} from './TableChunk';
import {TableHead} from './TableHead';
-import {EmptyTableRow} from './TableRow';
import {DEFAULT_TABLE_ROW_HEIGHT} from './constants';
-import i18n from './i18n';
import {b} from './shared';
import type {
Column,
@@ -75,10 +73,14 @@ export const PaginatedTable = ({
chunkSize,
});
- const lastChunkSize = React.useMemo(
- () => foundEntities % chunkSize || chunkSize,
- [foundEntities, chunkSize],
- );
+ const lastChunkSize = React.useMemo(() => {
+ // If foundEntities = 0, there will only first chunk
+ // Display it with 1 row, to display empty data message
+ if (!foundEntities) {
+ return 1;
+ }
+ return foundEntities % chunkSize || chunkSize;
+ }, [foundEntities, chunkSize]);
const handleDataFetched = React.useCallback((total: number, found: number) => {
setTotalEntities(total);
@@ -97,16 +99,6 @@ export const PaginatedTable = ({
}, [filters, initialFound, initialTotal, parentRef]);
const renderChunks = () => {
- if (!isInitialLoad && foundEntities === 0) {
- return (
-
-
- {renderEmptyDataMessage ? renderEmptyDataMessage() : i18n('empty')}
-
-
- );
- }
-
return activeChunks.map((isActive, index) => (
key={index}
@@ -121,6 +113,7 @@ export const PaginatedTable = ({
sortParams={sortParams}
getRowClassName={getRowClassName}
renderErrorMessage={renderErrorMessage}
+ renderEmptyDataMessage={renderEmptyDataMessage}
onDataFetched={handleDataFetched}
isActive={isActive}
/>
diff --git a/src/components/PaginatedTable/TableChunk.tsx b/src/components/PaginatedTable/TableChunk.tsx
index 347c11025..4702106fb 100644
--- a/src/components/PaginatedTable/TableChunk.tsx
+++ b/src/components/PaginatedTable/TableChunk.tsx
@@ -7,7 +7,15 @@ import {useAutoRefreshInterval} from '../../utils/hooks';
import {ResponseError} from '../Errors/ResponseError';
import {EmptyTableRow, LoadingTableRow, TableRow} from './TableRow';
-import type {Column, FetchData, GetRowClassName, SortParams} from './types';
+import i18n from './i18n';
+import type {
+ Column,
+ FetchData,
+ GetRowClassName,
+ RenderEmptyDataMessage,
+ RenderErrorMessage,
+ SortParams,
+} from './types';
import {typedMemo} from './utils';
const DEBOUNCE_TIMEOUT = 200;
@@ -25,7 +33,8 @@ interface TableChunkProps {
fetchData: FetchData;
getRowClassName?: GetRowClassName;
- renderErrorMessage?: (error: IResponseError) => React.ReactNode;
+ renderErrorMessage?: RenderErrorMessage;
+ renderEmptyDataMessage?: RenderEmptyDataMessage;
onDataFetched: (total: number, found: number) => void;
}
@@ -42,6 +51,7 @@ export const TableChunk = typedMemo(function TableChunk({
sortParams,
getRowClassName,
renderErrorMessage,
+ renderEmptyDataMessage,
onDataFetched,
isActive,
}: TableChunkProps) {
@@ -119,6 +129,15 @@ export const TableChunk = typedMemo(function TableChunk({
}
}
+ // Data is loaded, but there are no entities in the chunk
+ if (!currentData.data?.length) {
+ return (
+
+ {renderEmptyDataMessage ? renderEmptyDataMessage() : i18n('empty')}
+
+ );
+ }
+
return currentData.data.map((rowData, index) => (
{
expect(uptimeValues.length).toBeGreaterThan(0);
expect(hostValues.length).toBe(uptimeValues.length);
});
+
+ test('Table displays empty data message when no entities', async ({page}) => {
+ const paginatedTable = new PaginatedTable(page);
+
+ await paginatedTable.waitForTableToLoad();
+ await paginatedTable.waitForTableData();
+
+ await paginatedTable.search('Some Invalid search string !%#@[]');
+
+ await paginatedTable.waitForTableData();
+
+ const emptyDataMessage = await paginatedTable.getEmptyDataMessageLocator();
+ await expect(emptyDataMessage).toContainText('No such nodes');
+ });
+
+ test('Autorefresh updates data when initially empty data', async ({page}) => {
+ const paginatedTable = new PaginatedTable(page);
+
+ const emptyRequest = page.route(`${backend}/viewer/json/nodes?*`, async (route) => {
+ await route.fulfill({json: {FoundNodes: 0, TotalNodes: 0, Nodes: []}});
+ });
+ await paginatedTable.clickRefreshButton();
+
+ await emptyRequest;
+
+ const emptyDataMessage = await paginatedTable.getEmptyDataMessageLocator();
+ await expect(emptyDataMessage).toContainText('No such nodes');
+
+ await paginatedTable.setRefreshInterval('15 sec');
+
+ const requestWithData = page.route(`${backend}/viewer/json/nodes?*`, async (route) => {
+ await route.continue();
+ });
+
+ await page.waitForTimeout(15_000); // Wait for autorefresh
+
+ await requestWithData;
+ await paginatedTable.waitForTableData();
+
+ await expect(emptyDataMessage).toBeHidden();
+
+ const hostValues = await paginatedTable.getColumnValues('Host');
+ expect(hostValues.length).toBeGreaterThan(0);
+ });
});
diff --git a/tests/suites/paginatedTable/paginatedTable.ts b/tests/suites/paginatedTable/paginatedTable.ts
index 79ef716a1..5b2abfa6e 100644
--- a/tests/suites/paginatedTable/paginatedTable.ts
+++ b/tests/suites/paginatedTable/paginatedTable.ts
@@ -7,6 +7,7 @@ export class PaginatedTable {
private radioButtons: Locator;
private countLabel: Locator;
private tableRows: Locator;
+ private emptyTableRows: Locator;
private refreshButton: Locator;
private refreshIntervalSelect: Locator;
private headCells: Locator;
@@ -19,8 +20,9 @@ export class PaginatedTable {
this.countLabel = this.tableSelector.locator('.ydb-entities-count .g-label__content');
this.headCells = this.tableSelector.locator('.ydb-paginated-table__head-cell');
this.tableRows = this.tableSelector.locator('.ydb-paginated-table__row');
+ this.emptyTableRows = this.tableSelector.locator('.ydb-paginated-table__row_empty');
this.refreshButton = page.locator('.auto-refresh-control button[aria-label="Refresh"]');
- this.refreshIntervalSelect = page.locator('.cluster__auto-refresh-select');
+ this.refreshIntervalSelect = page.getByTestId('ydb-autorefresh-select');
}
async search(searchTerm: string) {
@@ -70,6 +72,10 @@ export class PaginatedTable {
return this.tableRows.count();
}
+ async getEmptyDataMessageLocator() {
+ return this.emptyTableRows.nth(0);
+ }
+
async waitForTableToLoad() {
await this.tableSelector.waitFor({state: 'visible'});
}
@@ -97,7 +103,7 @@ export class PaginatedTable {
async setRefreshInterval(interval: string) {
await this.refreshIntervalSelect.click();
- await this.page.locator('.g-select-popup__option', {hasText: interval}).click();
+ await this.page.locator('.g-select-list__option', {hasText: interval}).click();
}
async getRefreshInterval(): Promise {