mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Dashboard scenes: Unlink library panel inside edit mode (#84355)
Unlink library panel inside edit mode
This commit is contained in:
parent
cf6bed7ae5
commit
0fe5b62fa5
@ -20,6 +20,7 @@ export interface PanelEditorState extends SceneObjectState {
|
||||
dataPane?: PanelDataPane;
|
||||
vizManager: VizPanelManager;
|
||||
showLibraryPanelSaveModal?: boolean;
|
||||
showLibraryPanelUnlinkModal?: boolean;
|
||||
}
|
||||
|
||||
export class PanelEditor extends SceneObjectBase<PanelEditorState> {
|
||||
@ -212,9 +213,22 @@ export class PanelEditor extends SceneObjectBase<PanelEditorState> {
|
||||
locationService.partial({ editPanel: null });
|
||||
};
|
||||
|
||||
public onDismissLibraryPanelModal = () => {
|
||||
public onDismissLibraryPanelSaveModal = () => {
|
||||
this.setState({ showLibraryPanelSaveModal: false });
|
||||
};
|
||||
|
||||
public onUnlinkLibraryPanel = () => {
|
||||
this.setState({ showLibraryPanelUnlinkModal: true });
|
||||
};
|
||||
|
||||
public onDismissUnlinkLibraryPanelModal = () => {
|
||||
this.setState({ showLibraryPanelUnlinkModal: false });
|
||||
};
|
||||
|
||||
public onConfirmUnlinkLibraryPanel = () => {
|
||||
this.state.vizManager.unlinkLibraryPanel();
|
||||
this.setState({ showLibraryPanelUnlinkModal: false });
|
||||
};
|
||||
}
|
||||
|
||||
export function buildPanelEditScene(panel: VizPanel): PanelEditor {
|
||||
|
@ -6,6 +6,7 @@ import { SceneComponentProps } from '@grafana/scenes';
|
||||
import { Button, ToolbarButton, useStyles2 } from '@grafana/ui';
|
||||
|
||||
import { NavToolbarActions } from '../scene/NavToolbarActions';
|
||||
import { UnlinkModal } from '../scene/UnlinkModal';
|
||||
import { getDashboardSceneFor, getLibraryPanel } from '../utils/utils';
|
||||
|
||||
import { PanelEditor } from './PanelEditor';
|
||||
@ -58,7 +59,7 @@ export function PanelEditorRenderer({ model }: SceneComponentProps<PanelEditor>)
|
||||
|
||||
function VizAndDataPane({ model }: SceneComponentProps<PanelEditor>) {
|
||||
const dashboard = getDashboardSceneFor(model);
|
||||
const { vizManager, dataPane, showLibraryPanelSaveModal } = model.useState();
|
||||
const { vizManager, dataPane, showLibraryPanelSaveModal, showLibraryPanelUnlinkModal } = model.useState();
|
||||
const { sourcePanel } = vizManager.useState();
|
||||
const libraryPanel = getLibraryPanel(sourcePanel.resolve());
|
||||
const { controls } = dashboard.useState();
|
||||
@ -88,11 +89,18 @@ function VizAndDataPane({ model }: SceneComponentProps<PanelEditor>) {
|
||||
{showLibraryPanelSaveModal && libraryPanel && (
|
||||
<SaveLibraryVizPanelModal
|
||||
libraryPanel={libraryPanel}
|
||||
onDismiss={model.onDismissLibraryPanelModal}
|
||||
onDismiss={model.onDismissLibraryPanelSaveModal}
|
||||
onConfirm={model.onConfirmSaveLibraryPanel}
|
||||
onDiscard={model.onDiscard}
|
||||
></SaveLibraryVizPanelModal>
|
||||
)}
|
||||
{showLibraryPanelUnlinkModal && libraryPanel && (
|
||||
<UnlinkModal
|
||||
onDismiss={model.onDismissUnlinkLibraryPanelModal}
|
||||
onConfirm={model.onConfirmUnlinkLibraryPanel}
|
||||
isOpen
|
||||
/>
|
||||
)}
|
||||
{dataPane && (
|
||||
<>
|
||||
<div {...splitterProps} />
|
||||
|
@ -250,6 +250,40 @@ describe('VizPanelManager', () => {
|
||||
|
||||
expect(apiCall.mock.calls[0][0].state.panel?.state.title).toBe('new title');
|
||||
});
|
||||
|
||||
it('unlinks library panel', () => {
|
||||
const panel = new VizPanel({
|
||||
key: 'panel-1',
|
||||
pluginId: 'text',
|
||||
});
|
||||
|
||||
const libraryPanelModel = {
|
||||
title: 'title',
|
||||
uid: 'uid',
|
||||
name: 'libraryPanelName',
|
||||
model: vizPanelToPanel(panel),
|
||||
type: 'panel',
|
||||
version: 1,
|
||||
};
|
||||
|
||||
const libraryPanel = new LibraryVizPanel({
|
||||
isLoaded: true,
|
||||
title: libraryPanelModel.title,
|
||||
uid: libraryPanelModel.uid,
|
||||
name: libraryPanelModel.name,
|
||||
panelKey: panel.state.key!,
|
||||
panel: panel,
|
||||
_loadedPanel: libraryPanelModel,
|
||||
});
|
||||
|
||||
const gridItem = new SceneGridItem({ body: libraryPanel });
|
||||
|
||||
const panelManager = VizPanelManager.createFor(panel);
|
||||
panelManager.unlinkLibraryPanel();
|
||||
|
||||
const sourcePanel = panelManager.state.sourcePanel.resolve();
|
||||
expect(sourcePanel.parent?.state.key).toBe(gridItem.state.key);
|
||||
});
|
||||
});
|
||||
|
||||
describe('query options', () => {
|
||||
|
@ -338,6 +338,24 @@ export class VizPanelManager extends SceneObjectBase<VizPanelManagerState> {
|
||||
});
|
||||
}
|
||||
|
||||
public unlinkLibraryPanel() {
|
||||
const sourcePanel = this.state.sourcePanel.resolve();
|
||||
if (!(sourcePanel.parent instanceof LibraryVizPanel)) {
|
||||
throw new Error('VizPanel is not a child of a library panel');
|
||||
}
|
||||
|
||||
const gridItem = sourcePanel.parent.parent;
|
||||
if (!(gridItem instanceof SceneGridItem)) {
|
||||
throw new Error('Library panel not a child of a grid item');
|
||||
}
|
||||
|
||||
const newSourcePanel = this.state.panel.clone({ $data: this.state.$data?.clone() });
|
||||
gridItem.setState({
|
||||
body: newSourcePanel,
|
||||
});
|
||||
this.setState({ sourcePanel: newSourcePanel.getRef() });
|
||||
}
|
||||
|
||||
public commitChanges() {
|
||||
const sourcePanel = this.state.sourcePanel.resolve();
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { css } from '@emotion/css';
|
||||
import React from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
@ -8,10 +8,11 @@ import { Button, ButtonGroup, Dropdown, Icon, Menu, ToolbarButton, ToolbarButton
|
||||
import { AppChromeUpdate } from 'app/core/components/AppChrome/AppChromeUpdate';
|
||||
import { NavToolbarSeparator } from 'app/core/components/AppChrome/NavToolbar/NavToolbarSeparator';
|
||||
import { contextSrv } from 'app/core/core';
|
||||
import { t, Trans } from 'app/core/internationalization';
|
||||
import { Trans, t } from 'app/core/internationalization';
|
||||
import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv';
|
||||
import { playlistSrv } from 'app/features/playlist/PlaylistSrv';
|
||||
|
||||
import { PanelEditor } from '../panel-edit/PanelEditor';
|
||||
import { ShareModal } from '../sharing/ShareModal';
|
||||
import { DashboardInteractions } from '../utils/interactions';
|
||||
import { dynamicDashNavActions } from '../utils/registerDynamicDashNavAction';
|
||||
@ -57,9 +58,9 @@ export function ToolbarActions({ dashboard }: Props) {
|
||||
const buttonWithExtraMargin = useStyles2(getStyles);
|
||||
const isEditingPanel = Boolean(editPanel);
|
||||
const isViewingPanel = Boolean(viewPanelScene);
|
||||
const isEditingLibraryPanel = Boolean(
|
||||
editPanel?.state.vizManager.state.sourcePanel.resolve().parent instanceof LibraryVizPanel
|
||||
);
|
||||
|
||||
const isEditingLibraryPanel = useEditingLibraryPanel(editPanel);
|
||||
|
||||
const hasCopiedPanel = Boolean(copiedPanel);
|
||||
|
||||
toolbarActions.push({
|
||||
@ -383,6 +384,23 @@ export function ToolbarActions({ dashboard }: Props) {
|
||||
),
|
||||
});
|
||||
|
||||
toolbarActions.push({
|
||||
group: 'main-buttons',
|
||||
condition: isEditingPanel && isEditingLibraryPanel && !editview && !isViewingPanel,
|
||||
render: () => (
|
||||
<Button
|
||||
onClick={editPanel?.onUnlinkLibraryPanel}
|
||||
tooltip="Unlink library panel"
|
||||
size="sm"
|
||||
key="unlinkLibraryPanel"
|
||||
fill="outline"
|
||||
variant="secondary"
|
||||
>
|
||||
Unlink library panel
|
||||
</Button>
|
||||
),
|
||||
});
|
||||
|
||||
toolbarActions.push({
|
||||
group: 'main-buttons',
|
||||
condition: isEditingPanel && isEditingLibraryPanel && !editview && !isViewingPanel,
|
||||
@ -504,6 +522,25 @@ export function ToolbarActions({ dashboard }: Props) {
|
||||
return actionElements;
|
||||
}
|
||||
|
||||
function useEditingLibraryPanel(panelEditor?: PanelEditor) {
|
||||
const [isEditingLibraryPanel, setEditingLibraryPanel] = useState<Boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (panelEditor) {
|
||||
const unsub = panelEditor.state.vizManager.subscribeToState((vizManagerState) =>
|
||||
setEditingLibraryPanel(vizManagerState.sourcePanel.resolve().parent instanceof LibraryVizPanel)
|
||||
);
|
||||
return () => {
|
||||
unsub.unsubscribe();
|
||||
};
|
||||
}
|
||||
setEditingLibraryPanel(false);
|
||||
return;
|
||||
}, [panelEditor]);
|
||||
|
||||
return isEditingLibraryPanel;
|
||||
}
|
||||
|
||||
interface ToolbarAction {
|
||||
group: string;
|
||||
condition?: boolean | string;
|
||||
|
Loading…
Reference in New Issue
Block a user