mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-07-13 00:13:33 +08:00
🐛 fix(query-editor): 修复结果集阻塞与未知总数分页误导
- 为查询结果定位元数据探测增加软超时降级,避免租户元数据卡死阻塞主查询结果渲染 - 将未知总数分页切换为顺序翻页模式,并修正文案仅在真实统计时显示正在统计中 - 补充查询结果与分页回归测试,覆盖元数据超时和 legacy 未知总数分页场景
This commit is contained in:
@@ -1902,6 +1902,45 @@ describe('DataGrid layout', () => {
|
|||||||
expect(markup).not.toContain('data-grid-pagination-jump="true"');
|
expect(markup).not.toContain('data-grid-pagination-jump="true"');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('keeps legacy unknown-total pagination in sequential mode instead of numbered pages', () => {
|
||||||
|
const previousUiVersion = mockStoreState.uiVersion;
|
||||||
|
mockStoreState.uiVersion = 'legacy';
|
||||||
|
|
||||||
|
try {
|
||||||
|
const markup = renderDataGridWithI18n(
|
||||||
|
<DataGrid
|
||||||
|
data={[
|
||||||
|
{
|
||||||
|
__gonavi_row_key__: 'row-1',
|
||||||
|
id: 1,
|
||||||
|
name: 'alpha',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
columnNames={['id', 'name']}
|
||||||
|
loading={false}
|
||||||
|
tableName="users"
|
||||||
|
dbName="main"
|
||||||
|
connectionId="conn-1"
|
||||||
|
readOnly
|
||||||
|
pagination={{
|
||||||
|
current: 3,
|
||||||
|
pageSize: 100,
|
||||||
|
total: 400,
|
||||||
|
totalKnown: false,
|
||||||
|
}}
|
||||||
|
onPageChange={() => {}}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(markup).toContain('第 3 页');
|
||||||
|
expect(markup).toContain('data-grid-pagination-sequential="true"');
|
||||||
|
expect(markup).not.toContain('class="ant-pagination');
|
||||||
|
expect(markup).not.toContain('data-grid-pagination-jump="true"');
|
||||||
|
} finally {
|
||||||
|
mockStoreState.uiVersion = previousUiVersion;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
it('renders the v2 DataGrid toolbar using the redesigned topbar hooks', () => {
|
it('renders the v2 DataGrid toolbar using the redesigned topbar hooks', () => {
|
||||||
const markup = renderDataGridWithI18n(
|
const markup = renderDataGridWithI18n(
|
||||||
<DataGrid
|
<DataGrid
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ const DataGridPaginationBar: React.FC<DataGridPaginationBarProps> = ({
|
|||||||
translate = defaultTranslate,
|
translate = defaultTranslate,
|
||||||
}) => {
|
}) => {
|
||||||
const [jumpPage, setJumpPage] = React.useState<number | null>(pagination?.current ?? null);
|
const [jumpPage, setJumpPage] = React.useState<number | null>(pagination?.current ?? null);
|
||||||
|
const showSequentialPagination = !showKnownPageCount;
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
setJumpPage(pagination?.current ?? null);
|
setJumpPage(pagination?.current ?? null);
|
||||||
@@ -93,6 +94,31 @@ const DataGridPaginationBar: React.FC<DataGridPaginationBarProps> = ({
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
) : null;
|
) : null;
|
||||||
|
const sequentialPaginationControl = (
|
||||||
|
<div
|
||||||
|
className="data-grid-pagination-sequential"
|
||||||
|
data-grid-pagination-sequential="true"
|
||||||
|
style={{ display: 'inline-flex', alignItems: 'center', gap: 8 }}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
data-grid-pagination-prev="true"
|
||||||
|
size="small"
|
||||||
|
icon={<LeftOutlined />}
|
||||||
|
disabled={!onPageChange || pagination.current <= 1}
|
||||||
|
onClick={() => onV2PageStep('previous')}
|
||||||
|
/>
|
||||||
|
<div className="data-grid-pagination-page-chip" data-grid-page-chip="true">
|
||||||
|
<span>{paginationPageText}</span>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
data-grid-pagination-next="true"
|
||||||
|
size="small"
|
||||||
|
icon={<RightOutlined />}
|
||||||
|
disabled={!onPageChange || pagination.current >= paginationTotalPages}
|
||||||
|
onClick={() => onV2PageStep('next')}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -146,24 +172,26 @@ const DataGridPaginationBar: React.FC<DataGridPaginationBarProps> = ({
|
|||||||
<span className="data-grid-pagination-kicker">{translate('data_grid.pagination.result_set')}</span>
|
<span className="data-grid-pagination-kicker">{translate('data_grid.pagination.result_set')}</span>
|
||||||
<span className="data-grid-pagination-summary-value">{paginationSummaryText}</span>
|
<span className="data-grid-pagination-summary-value">{paginationSummaryText}</span>
|
||||||
</div>
|
</div>
|
||||||
<Pagination
|
{showSequentialPagination ? sequentialPaginationControl : (
|
||||||
current={pagination.current}
|
<Pagination
|
||||||
pageSize={pagination.pageSize}
|
current={pagination.current}
|
||||||
total={paginationControlTotal}
|
pageSize={pagination.pageSize}
|
||||||
showSizeChanger={false}
|
total={paginationControlTotal}
|
||||||
onChange={onPageChange}
|
showSizeChanger={false}
|
||||||
showTitle={false}
|
onChange={onPageChange}
|
||||||
size="small"
|
showTitle={false}
|
||||||
itemRender={(_page, type, originalElement) => {
|
size="small"
|
||||||
if (type === 'prev') {
|
itemRender={(_page, type, originalElement) => {
|
||||||
return <span className="data-grid-pagination-nav-icon" aria-hidden="true"><LeftOutlined /></span>;
|
if (type === 'prev') {
|
||||||
}
|
return <span className="data-grid-pagination-nav-icon" aria-hidden="true"><LeftOutlined /></span>;
|
||||||
if (type === 'next') {
|
}
|
||||||
return <span className="data-grid-pagination-nav-icon" aria-hidden="true"><RightOutlined /></span>;
|
if (type === 'next') {
|
||||||
}
|
return <span className="data-grid-pagination-nav-icon" aria-hidden="true"><RightOutlined /></span>;
|
||||||
return originalElement;
|
}
|
||||||
}}
|
return originalElement;
|
||||||
/>
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{jumpPageControl}
|
{jumpPageControl}
|
||||||
<Select
|
<Select
|
||||||
size="small"
|
size="small"
|
||||||
|
|||||||
@@ -369,6 +369,7 @@ const DataViewer: React.FC<{ tab: TabData; isActive?: boolean }> = React.memo(({
|
|||||||
const duckdbApproxKeyRef = useRef<string>('');
|
const duckdbApproxKeyRef = useRef<string>('');
|
||||||
const oracleApproxSeqRef = useRef(0);
|
const oracleApproxSeqRef = useRef(0);
|
||||||
const oracleApproxKeyRef = useRef<string>('');
|
const oracleApproxKeyRef = useRef<string>('');
|
||||||
|
const autoCountKeyRef = useRef<string>('');
|
||||||
const manualCountSeqRef = useRef(0);
|
const manualCountSeqRef = useRef(0);
|
||||||
const manualCountKeyRef = useRef<string>('');
|
const manualCountKeyRef = useRef<string>('');
|
||||||
const pkSeqRef = useRef(0);
|
const pkSeqRef = useRef(0);
|
||||||
@@ -463,6 +464,7 @@ const DataViewer: React.FC<{ tab: TabData; isActive?: boolean }> = React.memo(({
|
|||||||
countKeyRef.current = '';
|
countKeyRef.current = '';
|
||||||
duckdbApproxKeyRef.current = '';
|
duckdbApproxKeyRef.current = '';
|
||||||
oracleApproxKeyRef.current = '';
|
oracleApproxKeyRef.current = '';
|
||||||
|
autoCountKeyRef.current = '';
|
||||||
manualCountKeyRef.current = '';
|
manualCountKeyRef.current = '';
|
||||||
duckdbSafeSelectCacheRef.current = {};
|
duckdbSafeSelectCacheRef.current = {};
|
||||||
latestConfigRef.current = null;
|
latestConfigRef.current = null;
|
||||||
@@ -923,7 +925,9 @@ const DataViewer: React.FC<{ tab: TabData; isActive?: boolean }> = React.memo(({
|
|||||||
return { ...prev, current: currentPage, pageSize: size };
|
return { ...prev, current: currentPage, pageSize: size };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const keepManualCounting = prev.totalCountLoading && manualCountKeyRef.current === countKey;
|
const keepTotalCounting = prev.totalCountLoading && (
|
||||||
|
manualCountKeyRef.current === countKey || autoCountKeyRef.current === countKey
|
||||||
|
);
|
||||||
const hasApproximateTotalForCurrentKey =
|
const hasApproximateTotalForCurrentKey =
|
||||||
prev.totalApprox &&
|
prev.totalApprox &&
|
||||||
(duckdbApproxKeyRef.current === countKey || oracleApproxKeyRef.current === countKey) &&
|
(duckdbApproxKeyRef.current === countKey || oracleApproxKeyRef.current === countKey) &&
|
||||||
@@ -938,7 +942,7 @@ const DataViewer: React.FC<{ tab: TabData; isActive?: boolean }> = React.memo(({
|
|||||||
totalKnown: false,
|
totalKnown: false,
|
||||||
totalApprox: true,
|
totalApprox: true,
|
||||||
approximateTotal: prev.approximateTotal,
|
approximateTotal: prev.approximateTotal,
|
||||||
totalCountLoading: keepManualCounting,
|
totalCountLoading: keepTotalCounting,
|
||||||
totalCountCancelled: false,
|
totalCountCancelled: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -950,8 +954,8 @@ const DataViewer: React.FC<{ tab: TabData; isActive?: boolean }> = React.memo(({
|
|||||||
totalKnown: false,
|
totalKnown: false,
|
||||||
totalApprox: false,
|
totalApprox: false,
|
||||||
approximateTotal: undefined,
|
approximateTotal: undefined,
|
||||||
totalCountLoading: keepManualCounting,
|
totalCountLoading: keepTotalCounting,
|
||||||
totalCountCancelled: keepManualCounting ? false : prev.totalCountCancelled,
|
totalCountCancelled: keepTotalCounting ? false : prev.totalCountCancelled,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -959,11 +963,13 @@ const DataViewer: React.FC<{ tab: TabData; isActive?: boolean }> = React.memo(({
|
|||||||
if (shouldRunAsyncCount) {
|
if (shouldRunAsyncCount) {
|
||||||
if (countKeyRef.current !== countKey) {
|
if (countKeyRef.current !== countKey) {
|
||||||
countKeyRef.current = countKey;
|
countKeyRef.current = countKey;
|
||||||
|
autoCountKeyRef.current = countKey;
|
||||||
const countSeq = ++countSeqRef.current;
|
const countSeq = ++countSeqRef.current;
|
||||||
const countStart = Date.now();
|
const countStart = Date.now();
|
||||||
// Large-table COUNT(*) can be slow and may delay later operations in some runtimes.
|
// Large-table COUNT(*) can be slow and may delay later operations in some runtimes.
|
||||||
// DuckDB large-file scenarios disable background COUNT because it can slow pagination significantly.
|
// DuckDB large-file scenarios disable background COUNT because it can slow pagination significantly.
|
||||||
const countConfig = buildRpcConnectionConfig(config, { timeout: 5 });
|
const countConfig = buildRpcConnectionConfig(config, { timeout: 5 });
|
||||||
|
setPagination(prev => ({ ...prev, totalCountLoading: true, totalCountCancelled: false }));
|
||||||
|
|
||||||
DBQuery(countConfig, dbName, countSql)
|
DBQuery(countConfig, dbName, countSql)
|
||||||
.then((resCount: any) => {
|
.then((resCount: any) => {
|
||||||
@@ -982,11 +988,21 @@ const DataViewer: React.FC<{ tab: TabData; isActive?: boolean }> = React.memo(({
|
|||||||
if (countSeqRef.current !== countSeq) return;
|
if (countSeqRef.current !== countSeq) return;
|
||||||
if (latestCountKeyRef.current !== countKey) return;
|
if (latestCountKeyRef.current !== countKey) return;
|
||||||
|
|
||||||
if (!resCount.success) return;
|
autoCountKeyRef.current = '';
|
||||||
if (!Array.isArray(resCount.data) || resCount.data.length === 0) return;
|
if (!resCount.success) {
|
||||||
|
setPagination(prev => ({ ...prev, totalCountLoading: false }));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!Array.isArray(resCount.data) || resCount.data.length === 0) {
|
||||||
|
setPagination(prev => ({ ...prev, totalCountLoading: false }));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const total = parseTotalFromCountRow(resCount.data[0]);
|
const total = parseTotalFromCountRow(resCount.data[0]);
|
||||||
if (total === null) return;
|
if (total === null) {
|
||||||
|
setPagination(prev => ({ ...prev, totalCountLoading: false }));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
setPagination(prev => ({
|
setPagination(prev => ({
|
||||||
...prev,
|
...prev,
|
||||||
@@ -1001,6 +1017,8 @@ const DataViewer: React.FC<{ tab: TabData; isActive?: boolean }> = React.memo(({
|
|||||||
.catch(() => {
|
.catch(() => {
|
||||||
if (countSeqRef.current !== countSeq) return;
|
if (countSeqRef.current !== countSeq) return;
|
||||||
if (countKeyRef.current !== countKey) return;
|
if (countKeyRef.current !== countKey) return;
|
||||||
|
autoCountKeyRef.current = '';
|
||||||
|
setPagination(prev => ({ ...prev, totalCountLoading: false }));
|
||||||
// Count failures do not block the main flow; details stay in the SQL log.
|
// Count failures do not block the main flow; details stay in the SQL log.
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5716,7 +5716,7 @@ describe('QueryEditor external SQL save', () => {
|
|||||||
await Promise.resolve();
|
await Promise.resolve();
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(textContent(renderer!.toJSON())).toContain('消息 2');
|
expect(textContent(renderer!.toJSON())).toContain('消息 1');
|
||||||
expect(queryResultMessageText(renderer!)).toContain("insert into c_dyscript(projectid,name) values (1,'demo')");
|
expect(queryResultMessageText(renderer!)).toContain("insert into c_dyscript(projectid,name) values (1,'demo')");
|
||||||
expect(textContent(renderer!.toJSON())).not.toContain('影响行数:0');
|
expect(textContent(renderer!.toJSON())).not.toContain('影响行数:0');
|
||||||
expect(dataGridState.latestProps).toBeNull();
|
expect(dataGridState.latestProps).toBeNull();
|
||||||
@@ -5748,7 +5748,7 @@ describe('QueryEditor external SQL save', () => {
|
|||||||
await Promise.resolve();
|
await Promise.resolve();
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(textContent(renderer!.toJSON())).toContain('消息 2');
|
expect(textContent(renderer!.toJSON())).toContain('消息 1');
|
||||||
expect(queryResultMessageText(renderer!)).toContain("insert into c_dyscript(projectid,name) values (1,'demo')");
|
expect(queryResultMessageText(renderer!)).toContain("insert into c_dyscript(projectid,name) values (1,'demo')");
|
||||||
expect(textContent(renderer!.toJSON())).not.toContain('影响行数:0');
|
expect(textContent(renderer!.toJSON())).not.toContain('影响行数:0');
|
||||||
expect(dataGridState.latestProps).toBeNull();
|
expect(dataGridState.latestProps).toBeNull();
|
||||||
|
|||||||
@@ -1306,6 +1306,49 @@ describe('QueryEditor external SQL save', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('falls back to read-only results when query locator metadata stalls', async () => {
|
||||||
|
vi.useFakeTimers();
|
||||||
|
backendApp.DBQueryMulti.mockResolvedValueOnce({
|
||||||
|
success: true,
|
||||||
|
data: [{ columns: ['NAME'], rows: [{ NAME: 'alpha' }] }],
|
||||||
|
});
|
||||||
|
backendApp.DBGetColumns.mockReturnValueOnce(new Promise(() => {}));
|
||||||
|
backendApp.DBGetIndexes.mockReturnValueOnce(new Promise(() => {}));
|
||||||
|
|
||||||
|
try {
|
||||||
|
let renderer!: ReactTestRenderer;
|
||||||
|
await act(async () => {
|
||||||
|
renderer = create(<QueryEditor tab={createTab({ dbName: 'main', query: 'SELECT NAME FROM users' })} />);
|
||||||
|
});
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
findButton(renderer!, '运行').props.onClick();
|
||||||
|
await Promise.resolve();
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(backendApp.DBQueryMulti).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
vi.advanceTimersByTime(2000);
|
||||||
|
await Promise.resolve();
|
||||||
|
await Promise.resolve();
|
||||||
|
await Promise.resolve();
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(backendApp.DBQueryMulti).toHaveBeenCalledWith(
|
||||||
|
expect.anything(),
|
||||||
|
'main',
|
||||||
|
'SELECT NAME FROM users LIMIT 5000',
|
||||||
|
'query-1',
|
||||||
|
);
|
||||||
|
expect(dataGridState.latestProps?.data?.[0]).toMatchObject({ NAME: 'alpha' });
|
||||||
|
expect(dataGridState.latestProps?.tableName).toBe('users');
|
||||||
|
expect(dataGridState.latestProps?.readOnly).toBe(true);
|
||||||
|
} finally {
|
||||||
|
vi.useRealTimers();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
it('keeps MySQL information_schema routine results read-only without a locator warning', async () => {
|
it('keeps MySQL information_schema routine results read-only without a locator warning', async () => {
|
||||||
const sql = [
|
const sql = [
|
||||||
'SELECT ROUTINE_SCHEMA, ROUTINE_NAME, DEFINER, SECURITY_TYPE',
|
'SELECT ROUTINE_SCHEMA, ROUTINE_NAME, DEFINER, SECURITY_TYPE',
|
||||||
|
|||||||
@@ -25,8 +25,30 @@ export type CompletionTriggerMeta = {dbName: string, triggerName: string, tableN
|
|||||||
export type CompletionRoutineMeta = {dbName: string, routineName: string, routineType: string, schemaName?: string};
|
export type CompletionRoutineMeta = {dbName: string, routineName: string, routineType: string, schemaName?: string};
|
||||||
|
|
||||||
export const QUERY_LOCATOR_ALIAS_PREFIX = '__gonavi_locator_';
|
export const QUERY_LOCATOR_ALIAS_PREFIX = '__gonavi_locator_';
|
||||||
|
const QUERY_LOCATOR_METADATA_TIMEOUT_MS = 1500;
|
||||||
const SQLSERVER_MESSAGE_PREFIX_RE = /^\s*mssql:/i;
|
const SQLSERVER_MESSAGE_PREFIX_RE = /^\s*mssql:/i;
|
||||||
|
|
||||||
|
const withSoftTimeout = <T,>(promise: Promise<T>, fallback: () => T, timeoutMs = QUERY_LOCATOR_METADATA_TIMEOUT_MS): Promise<T> => {
|
||||||
|
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0 || typeof globalThis.setTimeout !== 'function') {
|
||||||
|
return promise.catch(() => fallback());
|
||||||
|
}
|
||||||
|
return new Promise<T>((resolve) => {
|
||||||
|
let settled = false;
|
||||||
|
const finish = (value: T) => {
|
||||||
|
if (settled) return;
|
||||||
|
settled = true;
|
||||||
|
globalThis.clearTimeout(timerId);
|
||||||
|
resolve(value);
|
||||||
|
};
|
||||||
|
const timerId = globalThis.setTimeout(() => {
|
||||||
|
finish(fallback());
|
||||||
|
}, timeoutMs);
|
||||||
|
promise
|
||||||
|
.then((value) => finish(value))
|
||||||
|
.catch(() => finish(fallback()));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const trimBoundaryBlankEntries = (entries: string[]): string[] => {
|
const trimBoundaryBlankEntries = (entries: string[]): string[] => {
|
||||||
let start = 0;
|
let start = 0;
|
||||||
let end = entries.length;
|
let end = entries.length;
|
||||||
@@ -2059,9 +2081,15 @@ export const resolveQueryLocatorPlan = async ({
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const [resCols, resIndexes] = await Promise.all([
|
const [resCols, resIndexes] = await Promise.all([
|
||||||
DBGetColumns(buildRpcConnectionConfig(config) as any, tableRef.metadataDbName, tableRef.metadataTableName),
|
withSoftTimeout(
|
||||||
DBGetIndexes(buildRpcConnectionConfig(config) as any, tableRef.metadataDbName, tableRef.metadataTableName)
|
DBGetColumns(buildRpcConnectionConfig(config) as any, tableRef.metadataDbName, tableRef.metadataTableName),
|
||||||
.catch((error: any) => ({ success: false, message: String(error?.message || error || 'Failed to load indexes'), data: [] })),
|
() => ({ success: false, message: 'Timed out while loading columns', data: [] }),
|
||||||
|
),
|
||||||
|
withSoftTimeout(
|
||||||
|
DBGetIndexes(buildRpcConnectionConfig(config) as any, tableRef.metadataDbName, tableRef.metadataTableName)
|
||||||
|
.catch((error: any) => ({ success: false, message: String(error?.message || error || 'Failed to load indexes'), data: [] })),
|
||||||
|
() => ({ success: false, message: 'Timed out while loading indexes', data: [] }),
|
||||||
|
),
|
||||||
]);
|
]);
|
||||||
if (!resCols?.success || !Array.isArray(resCols.data)) {
|
if (!resCols?.success || !Array.isArray(resCols.data)) {
|
||||||
const reason = translate('query_editor.message.read_only_table_locator_metadata_unavailable', {
|
const reason = translate('query_editor.message.read_only_table_locator_metadata_unavailable', {
|
||||||
|
|||||||
@@ -47,6 +47,13 @@ describe('dataGridPagination', () => {
|
|||||||
translate: keyEchoTranslate,
|
translate: keyEchoTranslate,
|
||||||
})).toBe('data_grid.pagination.summary.counting {"current":1}');
|
})).toBe('data_grid.pagination.summary.counting {"current":1}');
|
||||||
|
|
||||||
|
expect(resolvePaginationSummaryText({
|
||||||
|
pagination: { ...pagination, totalKnown: false },
|
||||||
|
prefersManualTotalCount: false,
|
||||||
|
supportsApproximateTableCount: false,
|
||||||
|
translate: keyEchoTranslate,
|
||||||
|
})).toBe('data_grid.pagination.summary.not_counted {"current":1}');
|
||||||
|
|
||||||
expect(resolvePaginationSummaryText({
|
expect(resolvePaginationSummaryText({
|
||||||
pagination: {
|
pagination: {
|
||||||
...pagination,
|
...pagination,
|
||||||
|
|||||||
@@ -46,15 +46,16 @@ export const resolvePaginationSummaryText = (params: {
|
|||||||
const approximateTotal = resolveApproximateTotal(pagination);
|
const approximateTotal = resolveApproximateTotal(pagination);
|
||||||
|
|
||||||
if (pagination.totalKnown === false) {
|
if (pagination.totalKnown === false) {
|
||||||
if (prefersManualTotalCount) {
|
if (pagination.totalCountLoading) {
|
||||||
if (pagination.totalCountLoading) return translate('data_grid.pagination.summary.counting_exact', { current: currentCount });
|
return prefersManualTotalCount
|
||||||
if (supportsApproximateTableCount && approximateTotal !== null) {
|
? translate('data_grid.pagination.summary.counting_exact', { current: currentCount })
|
||||||
return translate('data_grid.pagination.summary.approximate', { current: currentCount, total: approximateTotal });
|
: translate('data_grid.pagination.summary.counting', { current: currentCount });
|
||||||
}
|
|
||||||
if (pagination.totalCountCancelled) return translate('data_grid.pagination.summary.cancelled', { current: currentCount });
|
|
||||||
return translate('data_grid.pagination.summary.not_counted', { current: currentCount });
|
|
||||||
}
|
}
|
||||||
return translate('data_grid.pagination.summary.counting', { current: currentCount });
|
if (supportsApproximateTableCount && approximateTotal !== null) {
|
||||||
|
return translate('data_grid.pagination.summary.approximate', { current: currentCount, total: approximateTotal });
|
||||||
|
}
|
||||||
|
if (pagination.totalCountCancelled) return translate('data_grid.pagination.summary.cancelled', { current: currentCount });
|
||||||
|
return translate('data_grid.pagination.summary.not_counted', { current: currentCount });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Number.isFinite(total) || total <= 0) {
|
if (!Number.isFinite(total) || total <= 0) {
|
||||||
|
|||||||
Reference in New Issue
Block a user