Skip to content

Commit

Permalink
Dashboard: Migration - Dashboard Settings Variables (List, Duplicate,…
Browse files Browse the repository at this point in the history
… Delete) (#78917)

Co-authored-by: Torkel Ödegaard <torkel@grafana.com>
  • Loading branch information
axelavargas and torkelo committed Dec 14, 2023
1 parent f3cdb44 commit 4c6bbab
Show file tree
Hide file tree
Showing 11 changed files with 601 additions and 42 deletions.
Expand Up @@ -36,7 +36,7 @@ export class DashboardSceneUrlSync implements SceneObjectUrlSyncHandler {
const update: Partial<DashboardSceneState> = {};

if (typeof values.editview === 'string' && meta.canEdit) {
update.editview = createDashboardEditViewFor(values.editview, this._scene.getRef());
update.editview = createDashboardEditViewFor(values.editview);

// If we are not in editing (for example after full page reload)
if (!isEditing) {
Expand Down
Expand Up @@ -122,6 +122,25 @@ describe('transformSaveModelToScene', () => {
expect(scene.state.$behaviors![1]).toBeInstanceOf(behaviors.CursorSync);
expect((scene.state.$behaviors![1] as behaviors.CursorSync).state.sync).toEqual(DashboardCursorSync.Crosshair);
});

it('should initialize the Dashboard Scene with empty template variables', () => {
const dash = {
...defaultDashboard,
title: 'test empty dashboard with no variables',
uid: 'test-uid',
time: { from: 'now-10h', to: 'now' },
weekStart: 'saturday',
fiscalYearStartMonth: 2,
timezone: 'America/New_York',
templating: {
list: [],
},
};
const oldModel = new DashboardModel(dash);

const scene = createDashboardSceneFromDashboardModel(oldModel);
expect(scene.state.$variables?.state.variables).toBeDefined();
});
});

describe('when organizing panels as scene children', () => {
Expand Down
Expand Up @@ -195,6 +195,11 @@ export function createDashboardSceneFromDashboardModel(oldModel: DashboardModel)
variables = new SceneVariableSet({
variables: variableObjects,
});
} else {
// Create empty variable set
variables = new SceneVariableSet({
variables: [],
});
}

if (oldModel.annotations?.list?.length) {
Expand Down
Expand Up @@ -9,6 +9,7 @@ import { Page } from 'app/core/components/Page/Page';

import { DashboardScene } from '../scene/DashboardScene';
import { NavToolbarActions } from '../scene/NavToolbarActions';
import { getDashboardSceneFor } from '../utils/utils';

import { EditListViewSceneUrlSync } from './EditListViewSceneUrlSync';
import { DashboardEditView, DashboardEditListViewState, useDashboardEditPageNav } from './utils';
Expand All @@ -26,8 +27,8 @@ export class DashboardLinksEditView extends SceneObjectBase<DashboardLinksEditVi
}

function DashboardLinksEditViewRenderer({ model }: SceneComponentProps<DashboardLinksEditView>) {
const { dashboardRef, editIndex } = model.useState();
const dashboard = dashboardRef.resolve();
const { editIndex } = model.useState();
const dashboard = getDashboardSceneFor(model);
const links = dashboard.state.links || [];
const { navModel, pageNav } = useDashboardEditPageNav(dashboard, model.getUrlKey());

Expand Down
Expand Up @@ -112,6 +112,7 @@ describe('GeneralSettingsEditView', () => {
});

async function buildTestScene() {
const settings = new GeneralSettingsEditView({});
const dashboard = new DashboardScene({
$timeRange: new SceneTimeRange({}),
$behaviors: [new behaviors.CursorSync({ sync: DashboardCursorSync.Off })],
Expand Down Expand Up @@ -143,10 +144,7 @@ async function buildTestScene() {
}),
],
}),
});

const settings = new GeneralSettingsEditView({
dashboardRef: dashboard.getRef(),
editview: settings,
});

activateFullSceneTree(dashboard);
Expand Down
@@ -1,14 +1,7 @@
import React, { ChangeEvent } from 'react';

import { PageLayoutType } from '@grafana/data';
import {
behaviors,
SceneComponentProps,
SceneObjectBase,
SceneObjectRef,
SceneTimePicker,
sceneGraph,
} from '@grafana/scenes';
import { behaviors, SceneComponentProps, SceneObjectBase, SceneTimePicker, sceneGraph } from '@grafana/scenes';
import { TimeZone } from '@grafana/schema';
import {
Box,
Expand All @@ -31,12 +24,11 @@ import { DashboardControls } from '../scene/DashboardControls';
import { DashboardScene } from '../scene/DashboardScene';
import { NavToolbarActions } from '../scene/NavToolbarActions';
import { dashboardSceneGraph } from '../utils/dashboardSceneGraph';
import { getDashboardSceneFor } from '../utils/utils';

import { DashboardEditView, DashboardEditViewState, useDashboardEditPageNav } from './utils';

export interface GeneralSettingsEditViewState extends DashboardEditViewState {
dashboardRef: SceneObjectRef<DashboardScene>;
}
export interface GeneralSettingsEditViewState extends DashboardEditViewState {}

const EDITABLE_OPTIONS = [
{ label: 'Editable', value: true },
Expand All @@ -54,7 +46,7 @@ export class GeneralSettingsEditView
implements DashboardEditView
{
private get _dashboard(): DashboardScene {
return this.state.dashboardRef.resolve();
return getDashboardSceneFor(this);
}

public getUrlKey(): string {
Expand Down
@@ -0,0 +1,148 @@
import { SceneVariableSet, CustomVariable, SceneGridItem, SceneGridLayout } from '@grafana/scenes';

import { DashboardScene } from '../scene/DashboardScene';
import { activateFullSceneTree } from '../utils/test-utils';

import { VariablesEditView } from './VariablesEditView';

describe('VariablesEditView', () => {
describe('Dashboard Variables state', () => {
let dashboard: DashboardScene;
let variableView: VariablesEditView;

beforeEach(async () => {
const result = await buildTestScene();
dashboard = result.dashboard;
variableView = result.variableView;
});

it('should return the correct urlKey', () => {
expect(variableView.getUrlKey()).toBe('variables');
});

it('should return the dashboard', () => {
expect(variableView.getDashboard()).toBe(dashboard);
});

it('should return the list of variables', () => {
const expectedVariables = [
{
type: 'custom',
name: 'customVar',
query: 'test, test2',
value: 'test',
},
{
type: 'custom',
name: 'customVar2',
query: 'test3, test4',
value: 'test3',
},
];
const variables = variableView.getVariables();
expect(variables).toHaveLength(2);
expect(variables[0].state).toMatchObject(expectedVariables[0]);
expect(variables[1].state).toMatchObject(expectedVariables[1]);
});
});

describe('Dashboard Variables actions', () => {
let variableView: VariablesEditView;

beforeEach(async () => {
const result = await buildTestScene();
variableView = result.variableView;
});

it('should duplicate a variable', () => {
const variables = variableView.getVariables();
const variable = variables[0];
variableView.onDuplicated(variable.state.name);
expect(variableView.getVariables()).toHaveLength(3);
expect(variableView.getVariables()[1].state.name).toBe('copy_of_customVar');
});

it('should handle name when duplicating a variable twice', () => {
const variableIdentifier = 'customVar';
variableView.onDuplicated(variableIdentifier);
variableView.onDuplicated(variableIdentifier);
expect(variableView.getVariables()).toHaveLength(4);
expect(variableView.getVariables()[1].state.name).toBe('copy_of_customVar_1');
expect(variableView.getVariables()[2].state.name).toBe('copy_of_customVar');
});

it('should delete a variable', () => {
const variableIdentifier = 'customVar';
variableView.onDelete(variableIdentifier);
expect(variableView.getVariables()).toHaveLength(1);
expect(variableView.getVariables()[0].state.name).toBe('customVar2');
});

it('should change order of variables', () => {
const fromIndex = 0; // customVar is first
const toIndex = 1;
variableView.onOrderChanged(fromIndex, toIndex);
expect(variableView.getVariables()[0].state.name).toBe('customVar2');
expect(variableView.getVariables()[1].state.name).toBe('customVar');
});

it('should keep the same order of variables with invalid indexes', () => {
const fromIndex = 0;
const toIndex = 2;

const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});

variableView.onOrderChanged(fromIndex, toIndex);
expect(errorSpy).toHaveBeenCalledTimes(1);
expect(variableView.getVariables()[0].state.name).toBe('customVar');
expect(variableView.getVariables()[1].state.name).toBe('customVar2');

errorSpy.mockRestore();
});
});
});

async function buildTestScene() {
const variableView = new VariablesEditView({});
const dashboard = new DashboardScene({
title: 'Dashboard with variables',
uid: 'dash-variables',
meta: {
canEdit: true,
},
$variables: new SceneVariableSet({
variables: [
new CustomVariable({
name: 'customVar',
query: 'test, test2',
}),
new CustomVariable({
name: 'customVar2',
query: 'test3, test4',
}),
],
}),
body: new SceneGridLayout({
children: [
new SceneGridItem({
key: 'griditem-1',
x: 0,
y: 0,
width: 10,
height: 12,
body: undefined,
}),
],
}),
editview: variableView,
});

activateFullSceneTree(dashboard);

await new Promise((r) => setTimeout(r, 1));

dashboard.onEnterEditMode();
variableView.activate();

return { dashboard, variableView };
}

0 comments on commit 4c6bbab

Please sign in to comment.