From 2b953660a4ade673527ee38de3f0f9d1e50e53e9 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Wed, 29 Nov 2023 15:01:40 +0100 Subject: [PATCH] DashboardSettings: Provide skeleton for list editing (#78789) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * DashboardSettings: Provide basics for list editing * Update public/app/features/dashboard-scene/scene/DashboardSceneUrlSync.ts Co-authored-by: Torkel Ödegaard * Lint * Review * Fix page nav for items * Move links to dashbaord scene state --------- Co-authored-by: Torkel Ödegaard --- .../scene/DashboardLinksControls.tsx | 16 ++-- .../dashboard-scene/scene/DashboardScene.tsx | 3 + .../scene/DashboardSceneUrlSync.ts | 2 +- .../transformSaveModelToScene.ts | 6 +- .../settings/AnnotationsEditView.tsx | 6 +- .../settings/DashboardLinksEditView.tsx | 81 +++++++++++++++++++ .../settings/EditListViewSceneUrlSync.ts | 31 +++++++ .../settings/GeneralSettings.tsx | 6 +- .../settings/VariablesEditView.tsx | 6 +- .../dashboard-scene/settings/utils.ts | 31 +++++-- 10 files changed, 163 insertions(+), 25 deletions(-) create mode 100644 public/app/features/dashboard-scene/settings/DashboardLinksEditView.tsx create mode 100644 public/app/features/dashboard-scene/settings/EditListViewSceneUrlSync.ts diff --git a/public/app/features/dashboard-scene/scene/DashboardLinksControls.tsx b/public/app/features/dashboard-scene/scene/DashboardLinksControls.tsx index 7b7411cbe01..fcffa81a170 100644 --- a/public/app/features/dashboard-scene/scene/DashboardLinksControls.tsx +++ b/public/app/features/dashboard-scene/scene/DashboardLinksControls.tsx @@ -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 { static Component = DashboardLinksControlsRenderer; } function DashboardLinksControlsRenderer({ model }: SceneComponentProps) { - 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; + return ; } const icon = linkIconMap[link.icon]; diff --git a/public/app/features/dashboard-scene/scene/DashboardScene.tsx b/public/app/features/dashboard-scene/scene/DashboardScene.tsx index 3056105701d..43679d6dfc6 100644 --- a/public/app/features/dashboard-scene/scene/DashboardScene.tsx +++ b/public/app/features/dashboard-scene/scene/DashboardScene.tsx @@ -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 */ diff --git a/public/app/features/dashboard-scene/scene/DashboardSceneUrlSync.ts b/public/app/features/dashboard-scene/scene/DashboardSceneUrlSync.ts index 9954dc9322a..68fb41c3385 100644 --- a/public/app/features/dashboard-scene/scene/DashboardSceneUrlSync.ts +++ b/public/app/features/dashboard-scene/scene/DashboardSceneUrlSync.ts @@ -35,7 +35,7 @@ export class DashboardSceneUrlSync implements SceneObjectUrlSyncHandler { const update: Partial = {}; 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) { diff --git a/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.ts b/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.ts index f1d69e5e0f6..026d98cc367 100644 --- a/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.ts +++ b/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.ts @@ -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({}), }), ], }); diff --git a/public/app/features/dashboard-scene/settings/AnnotationsEditView.tsx b/public/app/features/dashboard-scene/settings/AnnotationsEditView.tsx index 7a9244d52ce..9aefc2ff62a 100644 --- a/public/app/features/dashboard-scene/settings/AnnotationsEditView.tsx +++ b/public/app/features/dashboard-scene/settings/AnnotationsEditView.tsx @@ -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 implements DashboardEditView { public getUrlKey(): string { diff --git a/public/app/features/dashboard-scene/settings/DashboardLinksEditView.tsx b/public/app/features/dashboard-scene/settings/DashboardLinksEditView.tsx new file mode 100644 index 00000000000..cdd14a67f54 --- /dev/null +++ b/public/app/features/dashboard-scene/settings/DashboardLinksEditView.tsx @@ -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 implements DashboardEditView { + static Component = DashboardLinksEditViewRenderer; + + protected _urlSync = new EditListViewSceneUrlSync(this); + + public getUrlKey(): string { + return 'links'; + } +} + +function DashboardLinksEditViewRenderer({ model }: SceneComponentProps) { + 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 ; + } + } + + return ( + + + {links.map((link, i) => ( + { + e.preventDefault(); + locationService.partial({ editIndex: i }); + }} + > + {link.title} + + ))} + + ); +} + +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 ( + + + {JSON.stringify(link)} + + ); +} diff --git a/public/app/features/dashboard-scene/settings/EditListViewSceneUrlSync.ts b/public/app/features/dashboard-scene/settings/EditListViewSceneUrlSync.ts new file mode 100644 index 00000000000..0184e4b645f --- /dev/null +++ b/public/app/features/dashboard-scene/settings/EditListViewSceneUrlSync.ts @@ -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 = {}; + if (typeof values.editIndex === 'string') { + update = { editIndex: Number(values.editIndex) }; + } else { + update = { editIndex: undefined }; + } + + if (Object.keys(update).length > 0) { + this._scene.setState(update); + } + } +} diff --git a/public/app/features/dashboard-scene/settings/GeneralSettings.tsx b/public/app/features/dashboard-scene/settings/GeneralSettings.tsx index f5f2943434e..c0c5817ef9c 100644 --- a/public/app/features/dashboard-scene/settings/GeneralSettings.tsx +++ b/public/app/features/dashboard-scene/settings/GeneralSettings.tsx @@ -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 diff --git a/public/app/features/dashboard-scene/settings/VariablesEditView.tsx b/public/app/features/dashboard-scene/settings/VariablesEditView.tsx index e95482a7a60..7a66b4bf070 100644 --- a/public/app/features/dashboard-scene/settings/VariablesEditView.tsx +++ b/public/app/features/dashboard-scene/settings/VariablesEditView.tsx @@ -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 implements DashboardEditView { public getUrlKey(): string { diff --git a/public/app/features/dashboard-scene/settings/utils.ts b/public/app/features/dashboard-scene/settings/utils.ts index 5853ccde071..b9f587ccaab 100644 --- a/public/app/features/dashboard-scene/settings/utils.ts +++ b/public/app/features/dashboard-scene/settings/utils.ts @@ -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; +} + +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 +): 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 }); } }