mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
DashboardScene: Fixes deleting dirty dashboard (#86479)
* DashboardScene: Fixes deleting dirty dashboard * refactor + unit test
This commit is contained in:
parent
e6799be13c
commit
835b968b08
@ -1,4 +1,5 @@
|
||||
import { CoreApp, LoadingState, getDefaultTimeRange } from '@grafana/data';
|
||||
import { locationService } from '@grafana/runtime';
|
||||
import {
|
||||
sceneGraph,
|
||||
SceneGridLayout,
|
||||
@ -71,6 +72,12 @@ jest.mock('app/features/playlist/PlaylistSrv', () => ({
|
||||
stop: jest.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
jest.mock('app/features/manage-dashboards/state/actions', () => ({
|
||||
...jest.requireActual('app/features/manage-dashboards/state/actions'),
|
||||
deleteDashboard: jest.fn().mockResolvedValue({}),
|
||||
}));
|
||||
|
||||
const worker = createWorker();
|
||||
mockResultsOfDetectChangesWorker({ hasChanges: true, hasTimeChanges: false, hasVariableValueChanges: false });
|
||||
|
||||
@ -884,6 +891,18 @@ describe('DashboardScene', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Deleting dashboard', () => {
|
||||
it('Should mark it non dirty before navigating to root', async () => {
|
||||
const scene = buildTestScene();
|
||||
scene.setState({ isDirty: true });
|
||||
|
||||
locationService.push('/d/adsdas');
|
||||
await scene.deleteDashboard();
|
||||
|
||||
expect(scene.state.isDirty).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Enriching data requests', () => {
|
||||
let scene: DashboardScene;
|
||||
|
||||
|
@ -24,6 +24,7 @@ import store from 'app/core/store';
|
||||
import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv';
|
||||
import { DashboardModel, PanelModel } from 'app/features/dashboard/state';
|
||||
import { dashboardWatcher } from 'app/features/live/dashboard/dashboardWatcher';
|
||||
import { deleteDashboard } from 'app/features/manage-dashboards/state/actions';
|
||||
import { VariablesChanged } from 'app/features/variables/types';
|
||||
import { DashboardDTO, DashboardMeta, SaveDashboardResponseDTO } from 'app/types';
|
||||
import { ShowConfirmModalEvent } from 'app/types/events';
|
||||
@ -832,6 +833,13 @@ export class DashboardScene extends SceneObjectBase<DashboardSceneState> {
|
||||
public setInitialSaveModel(saveModel: Dashboard) {
|
||||
this._initialSaveModel = saveModel;
|
||||
}
|
||||
|
||||
public async deleteDashboard() {
|
||||
await deleteDashboard(this.state.uid!, true);
|
||||
// Need to mark it non dirty to navigate away without unsaved changes warning
|
||||
this.setState({ isDirty: false });
|
||||
locationService.replace('/');
|
||||
}
|
||||
}
|
||||
|
||||
export class DashboardVariableDependency implements SceneVariableDependencyConfigLike {
|
||||
|
@ -0,0 +1,90 @@
|
||||
import React from 'react';
|
||||
import { useAsyncFn, useToggle } from 'react-use';
|
||||
|
||||
import { Button, ConfirmModal, Modal } from '@grafana/ui';
|
||||
import { Trans } from 'app/core/internationalization';
|
||||
|
||||
import { DashboardScene } from '../scene/DashboardScene';
|
||||
|
||||
interface ButtonProps {
|
||||
dashboard: DashboardScene;
|
||||
}
|
||||
|
||||
export function DeleteDashboardButton({ dashboard }: ButtonProps) {
|
||||
const [showModal, toggleModal] = useToggle(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button variant="destructive" onClick={toggleModal} aria-label="Dashboard settings page delete dashboard button">
|
||||
<Trans i18nKey="dashboard-settings.dashboard-delete-button">Delete dashboard</Trans>
|
||||
</Button>
|
||||
|
||||
{showModal && <DeleteDashboardModal dashboard={dashboard} onClose={toggleModal} />}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
interface ModalProps {
|
||||
dashboard: DashboardScene;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
function DeleteDashboardModal({ dashboard, onClose }: ModalProps) {
|
||||
const [, onConfirm] = useAsyncFn(async () => {
|
||||
onClose();
|
||||
await dashboard.deleteDashboard();
|
||||
}, [dashboard, onClose]);
|
||||
|
||||
if (dashboard.state.meta.provisioned) {
|
||||
return <ProvisionedDeleteModal dashboard={dashboard} onClose={onClose} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<ConfirmModal
|
||||
isOpen={true}
|
||||
body={
|
||||
<>
|
||||
<p>Do you want to delete this dashboard?</p>
|
||||
<p>{dashboard.state.title}</p>
|
||||
</>
|
||||
}
|
||||
onConfirm={onConfirm}
|
||||
onDismiss={onClose}
|
||||
title="Delete"
|
||||
icon="trash-alt"
|
||||
confirmText="Delete"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function ProvisionedDeleteModal({ dashboard, onClose }: ModalProps) {
|
||||
return (
|
||||
<Modal isOpen={true} title="Cannot delete provisioned dashboard" icon="trash-alt" onDismiss={onClose}>
|
||||
<p>
|
||||
This dashboard is managed by Grafana provisioning and cannot be deleted. Remove the dashboard from the config
|
||||
file to delete it.
|
||||
</p>
|
||||
<p>
|
||||
<i>
|
||||
See{' '}
|
||||
<a
|
||||
className="external-link"
|
||||
href="https://grafana.com/docs/grafana/latest/administration/provisioning/#dashboards"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
documentation
|
||||
</a>{' '}
|
||||
for more information about provisioning.
|
||||
</i>
|
||||
<br />
|
||||
File path: {dashboard.state.meta.provisionedExternalId}
|
||||
</p>
|
||||
<Modal.ButtonRow>
|
||||
<Button variant="primary" onClick={onClose}>
|
||||
OK
|
||||
</Button>
|
||||
</Modal.ButtonRow>
|
||||
</Modal>
|
||||
);
|
||||
}
|
@ -19,7 +19,6 @@ import { Page } from 'app/core/components/Page/Page';
|
||||
import { FolderPicker } from 'app/core/components/Select/FolderPicker';
|
||||
import { t, Trans } from 'app/core/internationalization';
|
||||
import { TimePickerSettings } from 'app/features/dashboard/components/DashboardSettings/TimePickerSettings';
|
||||
import { DeleteDashboardButton } from 'app/features/dashboard/components/DeleteDashboard/DeleteDashboardButton';
|
||||
import { GenAIDashDescriptionButton } from 'app/features/dashboard/components/GenAI/GenAIDashDescriptionButton';
|
||||
import { GenAIDashTitleButton } from 'app/features/dashboard/components/GenAI/GenAIDashTitleButton';
|
||||
|
||||
@ -29,6 +28,7 @@ import { NavToolbarActions } from '../scene/NavToolbarActions';
|
||||
import { dashboardSceneGraph } from '../utils/dashboardSceneGraph';
|
||||
import { getDashboardSceneFor } from '../utils/utils';
|
||||
|
||||
import { DeleteDashboardButton } from './DeleteDashboardButton';
|
||||
import { DashboardEditView, DashboardEditViewState, useDashboardEditPageNav } from './utils';
|
||||
|
||||
export interface GeneralSettingsEditViewState extends DashboardEditViewState {}
|
||||
@ -161,9 +161,12 @@ export class GeneralSettingsEditView
|
||||
this.getCursorSync()?.setState({ sync: value });
|
||||
};
|
||||
|
||||
public onDeleteDashboard = () => {};
|
||||
|
||||
static Component = ({ model }: SceneComponentProps<GeneralSettingsEditView>) => {
|
||||
const { navModel, pageNav } = useDashboardEditPageNav(model.getDashboard(), model.getUrlKey());
|
||||
const { title, description, tags, meta, editable } = model.getDashboard().useState();
|
||||
const dashboard = model.getDashboard();
|
||||
const { navModel, pageNav } = useDashboardEditPageNav(dashboard, model.getUrlKey());
|
||||
const { title, description, tags, meta, editable } = dashboard.useState();
|
||||
const { sync: graphTooltip } = model.getCursorSync()?.useState() || {};
|
||||
const { timeZone, weekStart, UNSAFE_nowDelay: nowDelay } = model.getTimeRange().useState();
|
||||
const { intervals } = model.getRefreshPicker().useState();
|
||||
@ -172,7 +175,7 @@ export class GeneralSettingsEditView
|
||||
|
||||
return (
|
||||
<Page navModel={navModel} pageNav={pageNav} layout={PageLayoutType.Standard}>
|
||||
<NavToolbarActions dashboard={model.getDashboard()} />
|
||||
<NavToolbarActions dashboard={dashboard} />
|
||||
<div style={{ maxWidth: '600px' }}>
|
||||
<Box marginBottom={5}>
|
||||
<Field
|
||||
@ -270,7 +273,7 @@ export class GeneralSettingsEditView
|
||||
</Field>
|
||||
</CollapsableSection>
|
||||
|
||||
<Box marginTop={3}>{meta.canDelete && <DeleteDashboardButton />}</Box>
|
||||
<Box marginTop={3}>{meta.canDelete && <DeleteDashboardButton dashboard={dashboard} />}</Box>
|
||||
</div>
|
||||
</Page>
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user