mirror of
https://github.com/grafana/grafana.git
synced 2025-07-31 07:22:17 +08:00
Dashlist: Use new nested folder picker (#74011)
* Add new folderUID property * Add nested folder picker + migration to UID * fix folderUID * comment
This commit is contained in:
@ -4496,7 +4496,7 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "Do not use any type assertions.", "4"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "5"]
|
||||
],
|
||||
"public/app/plugins/panel/dashlist/module.tsx:5381": [
|
||||
"public/app/plugins/panel/dashlist/migrations.test.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
|
||||
],
|
||||
"public/app/plugins/panel/debug/CursorView.tsx:5381": [
|
||||
|
@ -25,7 +25,7 @@ title: DashboardListPanelCfg kind
|
||||
### Options
|
||||
|
||||
| Property | Type | Required | Default | Description |
|
||||
|----------------------|----------|----------|---------|-------------|
|
||||
|----------------------|----------|----------|---------|-----------------------------------------------------------------|
|
||||
| `includeVars` | boolean | **Yes** | `false` | |
|
||||
| `keepTime` | boolean | **Yes** | `false` | |
|
||||
| `maxItems` | integer | **Yes** | `10` | |
|
||||
@ -35,6 +35,7 @@ title: DashboardListPanelCfg kind
|
||||
| `showSearch` | boolean | **Yes** | `false` | |
|
||||
| `showStarred` | boolean | **Yes** | `true` | |
|
||||
| `tags` | string[] | **Yes** | | |
|
||||
| `folderId` | integer | No | | |
|
||||
| `folderId` | integer | No | | folderId is deprecated, and migrated to folderUid on panel init |
|
||||
| `folderUID` | string | No | | |
|
||||
|
||||
|
||||
|
@ -12,7 +12,11 @@
|
||||
export const pluginVersion = "10.2.0-pre";
|
||||
|
||||
export interface Options {
|
||||
/**
|
||||
* folderId is deprecated, and migrated to folderUid on panel init
|
||||
*/
|
||||
folderId?: number;
|
||||
folderUID?: string;
|
||||
includeVars: boolean;
|
||||
keepTime: boolean;
|
||||
maxItems: number;
|
||||
|
@ -41,10 +41,11 @@ async function fetchDashboards(options: Options, replaceVars: InterpolateFunctio
|
||||
|
||||
let searchedDashboards: Promise<DashboardSearchItem[]> = Promise.resolve([]);
|
||||
if (options.showSearch) {
|
||||
const uid = options.folderUID === '' ? 'general' : options.folderUID;
|
||||
const params = {
|
||||
limit: options.maxItems,
|
||||
query: replaceVars(options.query, {}, 'text'),
|
||||
folderIds: options.folderId,
|
||||
folderUIDs: uid,
|
||||
tag: options.tags.map((tag: string) => replaceVars(tag, {}, 'text')),
|
||||
type: 'dash-db',
|
||||
};
|
||||
|
77
public/app/plugins/panel/dashlist/migrations.test.ts
Normal file
77
public/app/plugins/panel/dashlist/migrations.test.ts
Normal file
@ -0,0 +1,77 @@
|
||||
import { wellFormedPanelModel } from 'test/fixtures/panelModel.fixture';
|
||||
|
||||
import { PanelModel } from '@grafana/data';
|
||||
import { mockFolderDTO } from 'app/features/browse-dashboards/fixtures/folder.fixture';
|
||||
|
||||
import { dashlistMigrationHandler, AngularModel } from './migrations';
|
||||
|
||||
const getMock = jest.fn();
|
||||
|
||||
jest.mock('@grafana/runtime', () => ({
|
||||
...jest.requireActual('@grafana/runtime'),
|
||||
getBackendSrv: () => ({
|
||||
get: getMock,
|
||||
}),
|
||||
}));
|
||||
|
||||
describe('dashlist migrations', () => {
|
||||
it('migrates angular panel model to react model', async () => {
|
||||
const basePanelModel = wellFormedPanelModel({});
|
||||
basePanelModel.pluginVersion = '5.1';
|
||||
|
||||
const angularPanel: PanelModel<any> & AngularModel = {
|
||||
...basePanelModel,
|
||||
// pluginVersion: '5.1',
|
||||
starred: true,
|
||||
recent: true,
|
||||
search: true,
|
||||
headings: true,
|
||||
limit: 7,
|
||||
query: 'hello, query',
|
||||
};
|
||||
|
||||
const newOptions = await dashlistMigrationHandler(angularPanel);
|
||||
expect(newOptions).toEqual({
|
||||
showStarred: true,
|
||||
showRecentlyViewed: true,
|
||||
showSearch: true,
|
||||
showHeadings: true,
|
||||
maxItems: 7,
|
||||
query: 'hello, query',
|
||||
includeVars: undefined,
|
||||
keepTime: undefined,
|
||||
});
|
||||
expect(angularPanel).toStrictEqual(basePanelModel);
|
||||
});
|
||||
|
||||
it('migrates folder id to folder UID', async () => {
|
||||
const folderDTO = mockFolderDTO(1, {
|
||||
id: 77,
|
||||
uid: 'abc-124',
|
||||
});
|
||||
getMock.mockResolvedValue(folderDTO);
|
||||
|
||||
const basePanelOptions = {
|
||||
showStarred: true,
|
||||
showRecentlyViewed: true,
|
||||
showSearch: true,
|
||||
showHeadings: true,
|
||||
maxItems: 7,
|
||||
query: 'hello, query',
|
||||
includeVars: false,
|
||||
keepTime: false,
|
||||
tags: [],
|
||||
};
|
||||
const panelModel = wellFormedPanelModel({
|
||||
...basePanelOptions,
|
||||
folderId: 77,
|
||||
});
|
||||
|
||||
const newOptions = await dashlistMigrationHandler(panelModel);
|
||||
|
||||
expect(newOptions).toStrictEqual({
|
||||
...basePanelOptions,
|
||||
folderUID: 'abc-124',
|
||||
});
|
||||
});
|
||||
});
|
60
public/app/plugins/panel/dashlist/migrations.ts
Normal file
60
public/app/plugins/panel/dashlist/migrations.ts
Normal file
@ -0,0 +1,60 @@
|
||||
import { PanelModel } from '@grafana/data';
|
||||
import { getBackendSrv } from '@grafana/runtime';
|
||||
import { FolderDTO } from 'app/types';
|
||||
|
||||
import { Options } from './panelcfg.gen';
|
||||
|
||||
function getFolderByID(folderID: number) {
|
||||
return getBackendSrv().get<FolderDTO>(`/api/folders/id/${folderID}`);
|
||||
}
|
||||
|
||||
export interface AngularModel {
|
||||
/** @deprecated */
|
||||
starred?: boolean;
|
||||
/** @deprecated */
|
||||
recent?: boolean;
|
||||
/** @deprecated */
|
||||
search?: boolean;
|
||||
/** @deprecated */
|
||||
headings?: boolean;
|
||||
/** @deprecated */
|
||||
limit?: number;
|
||||
/** @deprecated */
|
||||
query?: string;
|
||||
/** @deprecated */
|
||||
folderId?: number;
|
||||
/** @deprecated */
|
||||
tags?: string[];
|
||||
}
|
||||
|
||||
export async function dashlistMigrationHandler(panel: PanelModel<Options> & AngularModel) {
|
||||
// Convert old angular model to new react model
|
||||
const newOptions: Options = {
|
||||
...panel.options,
|
||||
showStarred: panel.options.showStarred ?? panel.starred,
|
||||
showRecentlyViewed: panel.options.showRecentlyViewed ?? panel.recent,
|
||||
showSearch: panel.options.showSearch ?? panel.search,
|
||||
showHeadings: panel.options.showHeadings ?? panel.headings,
|
||||
maxItems: panel.options.maxItems ?? panel.limit,
|
||||
query: panel.options.query ?? panel.query,
|
||||
folderId: panel.options.folderId ?? panel.folderId,
|
||||
tags: panel.options.tags ?? panel.tags,
|
||||
};
|
||||
|
||||
// Delete old angular properties
|
||||
const previousVersion = parseFloat(panel.pluginVersion || '6.1');
|
||||
if (previousVersion < 6.3) {
|
||||
const oldProps = ['starred', 'recent', 'search', 'headings', 'limit', 'query', 'folderId'] as const;
|
||||
oldProps.forEach((prop) => delete panel[prop]);
|
||||
}
|
||||
|
||||
// Convert the folderId to folderUID. Uses the API to do the conversion.
|
||||
if (newOptions.folderId !== undefined) {
|
||||
const folderId = newOptions.folderId;
|
||||
const folderResp = await getFolderByID(folderId);
|
||||
newOptions.folderUID = folderResp.uid;
|
||||
delete newOptions.folderId;
|
||||
}
|
||||
|
||||
return newOptions;
|
||||
}
|
@ -1,15 +1,11 @@
|
||||
import React from 'react';
|
||||
|
||||
import { PanelModel, PanelPlugin } from '@grafana/data';
|
||||
import { PanelPlugin } from '@grafana/data';
|
||||
import { TagsInput } from '@grafana/ui';
|
||||
|
||||
import {
|
||||
ALL_FOLDER,
|
||||
GENERAL_FOLDER,
|
||||
ReadonlyFolderPicker,
|
||||
} from '../../../core/components/Select/ReadonlyFolderPicker/ReadonlyFolderPicker';
|
||||
import { FolderPicker } from 'app/core/components/Select/FolderPicker';
|
||||
|
||||
import { DashList } from './DashList';
|
||||
import { dashlistMigrationHandler } from './migrations';
|
||||
import { defaultOptions, Options } from './panelcfg.gen';
|
||||
|
||||
export const plugin = new PanelPlugin<Options>(DashList)
|
||||
@ -56,18 +52,12 @@ export const plugin = new PanelPlugin<Options>(DashList)
|
||||
defaultValue: defaultOptions.query,
|
||||
})
|
||||
.addCustomEditor({
|
||||
path: 'folderId',
|
||||
path: 'folderUid',
|
||||
name: 'Folder',
|
||||
id: 'folderId',
|
||||
id: 'folderUid',
|
||||
defaultValue: undefined,
|
||||
editor: function RenderFolderPicker({ value, onChange }) {
|
||||
return (
|
||||
<ReadonlyFolderPicker
|
||||
initialFolderId={value}
|
||||
onChange={(folder) => onChange(folder?.id)}
|
||||
extraFolders={[ALL_FOLDER, GENERAL_FOLDER]}
|
||||
/>
|
||||
);
|
||||
return <FolderPicker value={value} onChange={(folderUID) => onChange(folderUID)} />;
|
||||
},
|
||||
})
|
||||
.addCustomEditor({
|
||||
@ -81,23 +71,4 @@ export const plugin = new PanelPlugin<Options>(DashList)
|
||||
},
|
||||
});
|
||||
})
|
||||
.setMigrationHandler((panel: PanelModel<Options> & Record<string, any>) => {
|
||||
const newOptions = {
|
||||
showStarred: panel.options.showStarred ?? panel.starred,
|
||||
showRecentlyViewed: panel.options.showRecentlyViewed ?? panel.recent,
|
||||
showSearch: panel.options.showSearch ?? panel.search,
|
||||
showHeadings: panel.options.showHeadings ?? panel.headings,
|
||||
maxItems: panel.options.maxItems ?? panel.limit,
|
||||
query: panel.options.query ?? panel.query,
|
||||
folderId: panel.options.folderId ?? panel.folderId,
|
||||
tags: panel.options.tags ?? panel.tags,
|
||||
};
|
||||
|
||||
const previousVersion = parseFloat(panel.pluginVersion || '6.1');
|
||||
if (previousVersion < 6.3) {
|
||||
const oldProps = ['starred', 'recent', 'search', 'headings', 'limit', 'query', 'folderId'];
|
||||
oldProps.forEach((prop) => delete panel[prop]);
|
||||
}
|
||||
|
||||
return newOptions;
|
||||
});
|
||||
.setMigrationHandler(dashlistMigrationHandler);
|
||||
|
@ -30,8 +30,10 @@ composableKinds: PanelCfg: {
|
||||
showHeadings: bool | *true
|
||||
maxItems: int | *10
|
||||
query: string | *""
|
||||
folderId?: int
|
||||
tags: [...string] | *[]
|
||||
// folderId is deprecated, and migrated to folderUid on panel init
|
||||
folderId?: int
|
||||
folderUID?: string
|
||||
} @cuetsy(kind="interface")
|
||||
}
|
||||
}]
|
||||
|
@ -9,7 +9,11 @@
|
||||
// Run 'make gen-cue' from repository root to regenerate.
|
||||
|
||||
export interface Options {
|
||||
/**
|
||||
* folderId is deprecated, and migrated to folderUid on panel init
|
||||
*/
|
||||
folderId?: number;
|
||||
folderUID?: string;
|
||||
includeVars: boolean;
|
||||
keepTime: boolean;
|
||||
maxItems: number;
|
||||
|
21
public/test/fixtures/panelModel.fixture.ts
vendored
Normal file
21
public/test/fixtures/panelModel.fixture.ts
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
import { Chance } from 'chance';
|
||||
|
||||
import { PanelModel } from '@grafana/data';
|
||||
|
||||
export function wellFormedPanelModel<T extends object>(panelOptions: T, seed = 1): PanelModel<T> {
|
||||
const random = Chance(seed);
|
||||
|
||||
return {
|
||||
id: random.integer(),
|
||||
type: random.word(),
|
||||
title: random.sentence({ words: 3 }),
|
||||
description: random.sentence({ words: 10 }),
|
||||
options: panelOptions,
|
||||
fieldConfig: {
|
||||
defaults: {},
|
||||
overrides: [],
|
||||
},
|
||||
pluginVersion: '9.5.0',
|
||||
targets: [],
|
||||
};
|
||||
}
|
Reference in New Issue
Block a user