Dashboard versions: disable show more when last page (#102655)

---------

Co-authored-by: Dominik Prokop <dominik.prokop@grafana.com>
This commit is contained in:
Stephanie Hingtgen
2025-03-25 08:53:13 -05:00
committed by GitHub
co-authored by Dominik Prokop
parent df8c69bdba
commit b792c31edd
4 changed files with 148 additions and 16 deletions
@@ -1,4 +1,5 @@
import { SceneTimeRange } from '@grafana/scenes';
import { config } from '@grafana/runtime';
import { DashboardScene } from '../scene/DashboardScene';
import { activateFullSceneTree } from '../utils/test-utils';
@@ -108,6 +109,92 @@ describe('VersionsEditView', () => {
expect(versionsView.state.isNewLatest).toBe(true);
});
it('should correctly identify last page when partial page is returned without version 1', async () => {
jest.mocked(historySrv.getHistoryList).mockResolvedValueOnce({
continueToken: '',
versions: [
{
id: 4,
dashboardId: 1,
dashboardUID: '_U4zObQMz',
parentVersion: 3,
restoredFrom: 0,
version: 4,
created: '2017-02-22T17:43:01-08:00',
createdBy: 'admin',
message: '',
checked: false,
},
{
id: 3,
dashboardId: 1,
dashboardUID: '_U4zObQMz',
parentVersion: 1,
restoredFrom: 1,
version: 3,
created: '2017-02-22T17:43:01-08:00',
createdBy: 'admin',
message: '',
checked: false,
},
],
});
versionsView.reset();
versionsView.fetchVersions();
await new Promise(process.nextTick);
expect(versionsView.versions.length).toBeLessThan(VERSIONS_FETCH_LIMIT);
expect(versionsView.versions.find((rev) => rev.version === 1)).toBeUndefined();
});
it('should correctly identify last page when kubernetesClientDashboardsFolders is enabled and continueToken is empty', async () => {
// @ts-ignore
config.featureToggles.kubernetesClientDashboardsFolders = true;
jest.mocked(historySrv.getHistoryList).mockResolvedValueOnce({
continueToken: '',
versions: [
{
id: 4,
dashboardId: 1,
dashboardUID: '_U4zObQMz',
parentVersion: 3,
restoredFrom: 0,
version: 4,
created: '2017-02-22T17:43:01-08:00',
createdBy: 'admin',
message: '',
checked: false,
},
{
id: 3,
dashboardId: 1,
dashboardUID: '_U4zObQMz',
parentVersion: 1,
restoredFrom: 1,
version: 3,
created: '2017-02-22T17:43:01-08:00',
createdBy: 'admin',
message: '',
checked: false,
},
],
});
versionsView.reset();
versionsView.fetchVersions();
await new Promise(process.nextTick);
expect(versionsView.versions.length).toBeLessThan(VERSIONS_FETCH_LIMIT);
expect(versionsView.versions.find((rev) => rev.version === 1)).toBeUndefined();
expect(versionsView.continueToken).toBe('');
// reset feature flag
// @ts-ignore
config.featureToggles.kubernetesClientDashboardsFolders = false;
});
});
});
@@ -83,6 +83,10 @@ export class VersionsEditView extends SceneObjectBase<VersionsEditViewState> imp
return this._start;
}
public get continueToken(): string {
return this._continueToken;
}
public getUrlKey(): string {
return 'versions';
}
@@ -202,7 +206,11 @@ function VersionsEditorSettingsListView({ model }: SceneComponentProps<VersionsE
const canCompare = model.versions.filter((version) => version.checked).length === 2;
const showButtons = model.versions.length > 1;
const hasMore = model.versions.length >= model.limit;
const isLastPage = model.versions.find((rev) => rev.version === 1);
// older versions may have been cleaned up in the db, so also check if the last page is less than the limit, if so, we are at the end
let isLastPage = model.versions.find((rev) => rev.version === 1) || model.versions.length % model.limit !== 0;
if (config.featureToggles.kubernetesClientDashboardsFolders) {
isLastPage = isLastPage || model.continueToken === '';
}
const viewModeCompare = (
<>
@@ -2,6 +2,7 @@ import { screen, waitFor, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { render } from 'test/test-utils';
import { config } from '@grafana/runtime';
import { historySrv } from 'app/features/dashboard-scene/settings/version-history/HistorySrv';
import { createDashboardModelFixture } from '../../state/__fixtures__/dashboardFixtures';
@@ -56,8 +57,7 @@ describe('VersionSettings', () => {
});
test('renders a header and a loading indicator followed by results in a table', async () => {
// @ts-ignore
historySrv.getHistoryList.mockResolvedValue(versions);
historySrv.getHistoryList = jest.fn().mockResolvedValue(versions);
setup();
expect(screen.getByRole('heading', { name: /versions/i })).toBeInTheDocument();
@@ -75,8 +75,7 @@ describe('VersionSettings', () => {
});
test('does not render buttons if versions === 1', async () => {
// @ts-ignore
historySrv.getHistoryList.mockResolvedValue({
historySrv.getHistoryList = jest.fn().mockResolvedValue({
continueToken: versions.continueToken,
versions: versions.versions.slice(0, 1),
});
@@ -93,8 +92,7 @@ describe('VersionSettings', () => {
});
test('does not render show more button if versions < VERSIONS_FETCH_LIMIT', async () => {
// @ts-ignore
historySrv.getHistoryList.mockResolvedValue({
historySrv.getHistoryList = jest.fn().mockResolvedValue({
continueToken: versions.continueToken,
versions: versions.versions.slice(0, VERSIONS_FETCH_LIMIT - 5),
});
@@ -111,8 +109,7 @@ describe('VersionSettings', () => {
});
test('renders buttons if versions >= VERSIONS_FETCH_LIMIT', async () => {
// @ts-ignore
historySrv.getHistoryList.mockResolvedValue({
historySrv.getHistoryList = jest.fn().mockResolvedValue({
continueToken: versions.continueToken,
versions: versions.versions.slice(0, VERSIONS_FETCH_LIMIT),
});
@@ -134,8 +131,8 @@ describe('VersionSettings', () => {
});
test('clicking show more appends results to the table', async () => {
historySrv.getHistoryList
// @ts-ignore
historySrv.getHistoryList = jest
.fn()
.mockImplementationOnce(() =>
Promise.resolve({
continueToken: versions.continueToken,
@@ -170,14 +167,47 @@ describe('VersionSettings', () => {
});
});
test('does not show more button when receiving partial page without version 1', async () => {
// Mock a partial page response (less than VERSIONS_FETCH_LIMIT)
historySrv.getHistoryList = jest.fn().mockResolvedValueOnce({
continueToken: '',
versions: versions.versions.slice(0, VERSIONS_FETCH_LIMIT - 5),
});
setup();
await waitFor(() => expect(screen.getByRole('table')).toBeInTheDocument());
// Verify that show more button is not present since we got a partial page
expect(screen.queryByRole('button', { name: /show more versions/i })).not.toBeInTheDocument();
// Verify that compare button is still present
expect(screen.getByRole('button', { name: /compare versions/i })).toBeInTheDocument();
});
test('does not show more button when kubernetesClientDashboardsFolders is enabled and continueToken is empty', async () => {
config.featureToggles.kubernetesClientDashboardsFolders = true;
historySrv.getHistoryList = jest.fn().mockResolvedValueOnce({
continueToken: '',
versions: versions.versions.slice(0, VERSIONS_FETCH_LIMIT - 1),
});
setup();
await waitFor(() => expect(screen.getByRole('table')).toBeInTheDocument());
expect(screen.queryByRole('button', { name: /show more versions/i })).not.toBeInTheDocument();
expect(screen.getByRole('button', { name: /compare versions/i })).toBeInTheDocument();
config.featureToggles.kubernetesClientDashboardsFolders = false;
});
test('selecting two versions and clicking compare button should render compare view', async () => {
// @ts-ignore
historySrv.getHistoryList.mockResolvedValue({
historySrv.getHistoryList = jest.fn().mockResolvedValue({
continueToken: versions.continueToken,
versions: versions.versions.slice(0, VERSIONS_FETCH_LIMIT),
});
historySrv.getDashboardVersion
// @ts-ignore
historySrv.getDashboardVersion = jest
.fn()
.mockImplementationOnce(() => Promise.resolve(diffs.lhs))
.mockImplementationOnce(() => Promise.resolve(diffs.rhs));
@@ -125,7 +125,14 @@ export class VersionsSettings extends PureComponent<Props, State> {
}));
isLastPage() {
return this.state.versions.find((rev) => rev.version === 1);
if (config.featureToggles.kubernetesClientDashboardsFolders) {
return (
this.state.versions.find((rev) => rev.version === 1) ||
this.state.versions.length % this.limit !== 0 ||
this.continueToken === ''
);
}
return this.state.versions.find((rev) => rev.version === 1) || this.state.versions.length % this.limit !== 0;
}
onCheck = (ev: React.FormEvent<HTMLInputElement>, versionId: number) => {