DashboardSettings: Provide skeleton for list editing (#78789)

* DashboardSettings: Provide basics for list editing

* Update public/app/features/dashboard-scene/scene/DashboardSceneUrlSync.ts

Co-authored-by: Torkel Ödegaard <torkel@grafana.com>

* Lint

* Review

* Fix page nav for items

* Move links to dashbaord scene state

---------

Co-authored-by: Torkel Ödegaard <torkel@grafana.com>
This commit is contained in:
Dominik Prokop 2023-11-29 15:01:40 +01:00 committed by GitHub
parent e152323a33
commit 2b953660a4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 163 additions and 25 deletions

View File

@ -12,17 +12,21 @@ import {
} from 'app/features/dashboard/components/SubMenu/DashboardLinksDashboard';
import { getLinkSrv } from 'app/features/panel/panellinks/link_srv';
interface DashboardLinksControlsState extends SceneObjectState {
links: DashboardLink[];
dashboardUID: string;
}
import { getDashboardSceneFor } from '../utils/utils';
interface DashboardLinksControlsState extends SceneObjectState {}
export class DashboardLinksControls extends SceneObjectBase<DashboardLinksControlsState> {
static Component = DashboardLinksControlsRenderer;
}
function DashboardLinksControlsRenderer({ model }: SceneComponentProps<DashboardLinksControls>) {
const { links, dashboardUID } = model.useState();
const { links, uid } = getDashboardSceneFor(model).useState();
if (!links || !uid) {
return null;
}
return (
<>
{links.map((link: DashboardLink, index: number) => {
@ -30,7 +34,7 @@ function DashboardLinksControlsRenderer({ model }: SceneComponentProps<Dashboard
const key = `${link.title}-$${index}`;
if (link.type === 'dashboards') {
return <DashboardLinksDashboard key={key} link={link} linkInfo={linkInfo} dashboardUID={dashboardUID} />;
return <DashboardLinksDashboard key={key} link={link} linkInfo={linkInfo} dashboardUID={uid} />;
}
const icon = linkIconMap[link.icon];

View File

@ -16,6 +16,7 @@ import {
SceneVariable,
SceneVariableDependencyConfigLike,
} from '@grafana/scenes';
import { DashboardLink } from '@grafana/schema';
import appEvents from 'app/core/app_events';
import { getNavModel } from 'app/core/selectors/navModel';
import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv';
@ -37,6 +38,8 @@ export interface DashboardSceneState extends SceneObjectState {
title: string;
/** Tags */
tags?: string[];
/** Links */
links?: DashboardLink[];
/** A uid when saved */
uid?: string;
/** @deprecated */

View File

@ -35,7 +35,7 @@ export class DashboardSceneUrlSync implements SceneObjectUrlSyncHandler {
const update: Partial<DashboardSceneState> = {};
if (typeof values.editview === 'string' && meta.canEdit) {
update.editview = createDashboardEditViewFor(values.editview);
update.editview = createDashboardEditViewFor(values.editview, this._scene.getRef());
// If we are not in editing (for example after full page reload)
if (!isEditing) {

View File

@ -226,6 +226,7 @@ export function createDashboardSceneFromDashboardModel(oldModel: DashboardModel)
return new DashboardScene({
title: oldModel.title,
tags: oldModel.tags || [],
links: oldModel.links || [],
uid: oldModel.uid,
id: oldModel.id,
meta: oldModel.meta,
@ -265,10 +266,7 @@ export function createDashboardSceneFromDashboardModel(oldModel: DashboardModel)
intervals: oldModel.timepicker.refresh_intervals,
}),
],
linkControls: new DashboardLinksControls({
links: oldModel.links,
dashboardUID: oldModel.uid,
}),
linkControls: new DashboardLinksControls({}),
}),
],
});

View File

@ -1,16 +1,16 @@
import React from 'react';
import { PageLayoutType } from '@grafana/data';
import { SceneComponentProps, SceneObjectBase, SceneObjectState } from '@grafana/scenes';
import { SceneComponentProps, SceneObjectBase } from '@grafana/scenes';
import { Page } from 'app/core/components/Page/Page';
import { NavToolbarActions } from '../scene/NavToolbarActions';
import { getDashboardSceneFor } from '../utils/utils';
import { GeneralSettingsEditView } from './GeneralSettings';
import { DashboardEditView, useDashboardEditPageNav } from './utils';
import { DashboardEditView, DashboardEditViewState, useDashboardEditPageNav } from './utils';
export interface AnnotationsEditViewState extends SceneObjectState {}
export interface AnnotationsEditViewState extends DashboardEditViewState {}
export class AnnotationsEditView extends SceneObjectBase<AnnotationsEditViewState> implements DashboardEditView {
public getUrlKey(): string {

View File

@ -0,0 +1,81 @@
import React from 'react';
import { NavModel, NavModelItem, PageLayoutType } from '@grafana/data';
import { locationService } from '@grafana/runtime';
import { SceneComponentProps, SceneObjectBase } from '@grafana/scenes';
import { DashboardLink } from '@grafana/schema';
import { Link } from '@grafana/ui';
import { Page } from 'app/core/components/Page/Page';
import { DashboardScene } from '../scene/DashboardScene';
import { NavToolbarActions } from '../scene/NavToolbarActions';
import { EditListViewSceneUrlSync } from './EditListViewSceneUrlSync';
import { DashboardEditView, DashboardEditListViewState, useDashboardEditPageNav } from './utils';
export interface DashboardLinksEditViewState extends DashboardEditListViewState {}
export class DashboardLinksEditView extends SceneObjectBase<DashboardLinksEditViewState> implements DashboardEditView {
static Component = DashboardLinksEditViewRenderer;
protected _urlSync = new EditListViewSceneUrlSync(this);
public getUrlKey(): string {
return 'links';
}
}
function DashboardLinksEditViewRenderer({ model }: SceneComponentProps<DashboardLinksEditView>) {
const { dashboardRef, editIndex } = model.useState();
const dashboard = dashboardRef.resolve();
const links = dashboard.state.links || [];
const { navModel, pageNav } = useDashboardEditPageNav(dashboard, model.getUrlKey());
if (editIndex !== undefined) {
const link = links[editIndex];
if (link) {
return <EditLinkView pageNav={pageNav} navModel={navModel} link={link} dashboard={dashboard} />;
}
}
return (
<Page navModel={navModel} pageNav={pageNav} layout={PageLayoutType.Standard}>
<NavToolbarActions dashboard={dashboard} />
{links.map((link, i) => (
<Link
key={`${link.title}-${i}`}
onClick={(e) => {
e.preventDefault();
locationService.partial({ editIndex: i });
}}
>
{link.title}
</Link>
))}
</Page>
);
}
interface EditLinkViewProps {
link: DashboardLink;
pageNav: NavModelItem;
navModel: NavModel;
dashboard: DashboardScene;
}
function EditLinkView({ pageNav, link, navModel, dashboard }: EditLinkViewProps) {
const parentTab = pageNav.children!.find((p) => p.active)!;
parentTab.parentItem = pageNav;
const editLinkPageNav = {
text: 'Edit link',
parentItem: parentTab,
};
return (
<Page navModel={navModel} pageNav={editLinkPageNav} layout={PageLayoutType.Standard}>
<NavToolbarActions dashboard={dashboard} />
{JSON.stringify(link)}
</Page>
);
}

View File

@ -0,0 +1,31 @@
import { SceneObjectUrlSyncHandler, SceneObjectUrlValues } from '@grafana/scenes';
import { DashboardLinksEditView, DashboardLinksEditViewState } from './DashboardLinksEditView';
export class EditListViewSceneUrlSync implements SceneObjectUrlSyncHandler {
constructor(private _scene: DashboardLinksEditView) {}
getKeys(): string[] {
return ['editIndex'];
}
getUrlState(): SceneObjectUrlValues {
const state = this._scene.state;
return {
editIndex: state.editIndex !== undefined ? String(state.editIndex) : undefined,
};
}
updateFromUrl(values: SceneObjectUrlValues): void {
let update: Partial<DashboardLinksEditViewState> = {};
if (typeof values.editIndex === 'string') {
update = { editIndex: Number(values.editIndex) };
} else {
update = { editIndex: undefined };
}
if (Object.keys(update).length > 0) {
this._scene.setState(update);
}
}
}

View File

@ -1,15 +1,15 @@
import React from 'react';
import { PageLayoutType } from '@grafana/data';
import { SceneComponentProps, SceneObjectBase, SceneObjectState } from '@grafana/scenes';
import { SceneComponentProps, SceneObjectBase } from '@grafana/scenes';
import { Page } from 'app/core/components/Page/Page';
import { NavToolbarActions } from '../scene/NavToolbarActions';
import { getDashboardSceneFor } from '../utils/utils';
import { DashboardEditView, useDashboardEditPageNav } from './utils';
import { DashboardEditView, DashboardEditViewState, useDashboardEditPageNav } from './utils';
export interface GeneralSettingsEditViewState extends SceneObjectState {}
export interface GeneralSettingsEditViewState extends DashboardEditViewState {}
export class GeneralSettingsEditView
extends SceneObjectBase<GeneralSettingsEditViewState>

View File

@ -1,16 +1,16 @@
import React from 'react';
import { PageLayoutType } from '@grafana/data';
import { SceneComponentProps, SceneObjectBase, SceneObjectState } from '@grafana/scenes';
import { SceneComponentProps, SceneObjectBase } from '@grafana/scenes';
import { Page } from 'app/core/components/Page/Page';
import { NavToolbarActions } from '../scene/NavToolbarActions';
import { getDashboardSceneFor } from '../utils/utils';
import { GeneralSettingsEditView } from './GeneralSettings';
import { DashboardEditView, useDashboardEditPageNav } from './utils';
import { DashboardEditView, DashboardEditViewState, useDashboardEditPageNav } from './utils';
export interface VariablesEditViewState extends SceneObjectState {}
export interface VariablesEditViewState extends DashboardEditViewState {}
export class VariablesEditView extends SceneObjectBase<VariablesEditViewState> implements DashboardEditView {
public getUrlKey(): string {

View File

@ -1,7 +1,7 @@
import { useLocation } from 'react-router-dom';
import { locationUtil, NavModelItem } from '@grafana/data';
import { SceneObject } from '@grafana/scenes';
import { SceneObject, SceneObjectRef, SceneObjectState } from '@grafana/scenes';
import { t } from 'app/core/internationalization';
import { getNavModel } from 'app/core/selectors/navModel';
import { useSelector } from 'app/types';
@ -9,9 +9,19 @@ import { useSelector } from 'app/types';
import { DashboardScene } from '../scene/DashboardScene';
import { AnnotationsEditView } from './AnnotationsEditView';
import { DashboardLinksEditView } from './DashboardLinksEditView';
import { GeneralSettingsEditView } from './GeneralSettings';
import { VariablesEditView } from './VariablesEditView';
export interface DashboardEditViewState extends SceneObjectState {
dashboardRef: SceneObjectRef<DashboardScene>;
}
export interface DashboardEditListViewState extends DashboardEditViewState {
/** Index of the list item to edit */
editIndex?: number;
}
export interface DashboardEditView extends SceneObject {
getUrlKey(): string;
}
@ -24,6 +34,7 @@ export function useDashboardEditPageNav(dashboard: DashboardScene, currentEditVi
const pageNav: NavModelItem = {
text: 'Settings',
url: locationUtil.getUrlForPartial(location, { editview: 'settings', editIndex: null }),
children: [
{
text: t('dashboard-settings.general.title', 'General'),
@ -40,6 +51,11 @@ export function useDashboardEditPageNav(dashboard: DashboardScene, currentEditVi
url: locationUtil.getUrlForPartial(location, { editview: 'variables', editIndex: null }),
active: currentEditView === 'variables',
},
{
text: t('dashboard-settings.links.title', 'Links'),
url: locationUtil.getUrlForPartial(location, { editview: 'links', editIndex: null }),
active: currentEditView === 'links',
},
],
parentItem: dashboardPageNav,
};
@ -47,14 +63,19 @@ export function useDashboardEditPageNav(dashboard: DashboardScene, currentEditVi
return { navModel, pageNav };
}
export function createDashboardEditViewFor(editview: string): DashboardEditView {
export function createDashboardEditViewFor(
editview: string,
dashboardRef: SceneObjectRef<DashboardScene>
): DashboardEditView {
switch (editview) {
case 'annotations':
return new AnnotationsEditView({});
return new AnnotationsEditView({ dashboardRef });
case 'variables':
return new VariablesEditView({});
return new VariablesEditView({ dashboardRef });
case 'links':
return new DashboardLinksEditView({ dashboardRef });
case 'settings':
default:
return new GeneralSettingsEditView({});
return new GeneralSettingsEditView({ dashboardRef });
}
}