mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Dashboards: Keep save drawer open for unhandled errors (#70434)
* user essentials mob! 🔱 * user essentials mob! 🔱 lastFile:public/app/features/dashboard/components/SaveDashboard/SaveDashboardDrawer.tsx * user essentials mob! 🔱 lastFile:public/app/features/dashboard/components/SaveDashboard/SaveDashboardDrawer.tsx * user essentials mob! 🔱 lastFile:public/app/features/dashboard/components/SaveDashboard/useDashboardSave.tsx * rename isHandledError fn * fix prettier --------- Co-authored-by: Joao Silva <joao.silva@grafana.com> Co-authored-by: Laura Benz <laura.benz@grafana.com> Co-authored-by: Ashley Harrison <ashley.harrison@grafana.com>
This commit is contained in:
parent
ac78146091
commit
b1e76a7036
@ -2,14 +2,14 @@ import React, { useMemo, useState } from 'react';
|
||||
import { useAsync } from 'react-use';
|
||||
|
||||
import { config, isFetchError } from '@grafana/runtime';
|
||||
import { Drawer, Spinner, Tab, TabsBar } from '@grafana/ui';
|
||||
import { Drawer, Tab, TabsBar } from '@grafana/ui';
|
||||
import { backendSrv } from 'app/core/services/backend_srv';
|
||||
|
||||
import { jsonDiff } from '../VersionHistory/utils';
|
||||
|
||||
import DashboardValidation from './DashboardValidation';
|
||||
import { SaveDashboardDiff } from './SaveDashboardDiff';
|
||||
import { SaveDashboardErrorProxy } from './SaveDashboardErrorProxy';
|
||||
import { proxyHandlesError, SaveDashboardErrorProxy } from './SaveDashboardErrorProxy';
|
||||
import { SaveDashboardAsForm } from './forms/SaveDashboardAsForm';
|
||||
import { SaveDashboardForm } from './forms/SaveDashboardForm';
|
||||
import { SaveProvisionedDashboardForm } from './forms/SaveProvisionedDashboardForm';
|
||||
@ -72,18 +72,11 @@ export const SaveDashboardDrawer = ({ dashboard, onDismiss, onSaveSuccess, isCop
|
||||
return <SaveDashboardDiff diff={data.diff} oldValue={previous.value} newValue={data.clone} />;
|
||||
}
|
||||
|
||||
if (state.loading) {
|
||||
return (
|
||||
<div>
|
||||
<Spinner />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (isNew || isCopy) {
|
||||
return (
|
||||
<SaveDashboardAsForm
|
||||
dashboard={dashboard}
|
||||
isLoading={state.loading}
|
||||
onCancel={onDismiss}
|
||||
onSuccess={onSuccess}
|
||||
onSubmit={onDashboardSave}
|
||||
@ -99,6 +92,7 @@ export const SaveDashboardDrawer = ({ dashboard, onDismiss, onSaveSuccess, isCop
|
||||
return (
|
||||
<SaveDashboardForm
|
||||
dashboard={dashboard}
|
||||
isLoading={state.loading}
|
||||
saveModel={data}
|
||||
onCancel={onDismiss}
|
||||
onSuccess={onSuccess}
|
||||
@ -109,7 +103,12 @@ export const SaveDashboardDrawer = ({ dashboard, onDismiss, onSaveSuccess, isCop
|
||||
);
|
||||
};
|
||||
|
||||
if (state.error && isFetchError(state.error) && !state.error.isHandled) {
|
||||
if (
|
||||
state.error &&
|
||||
isFetchError(state.error) &&
|
||||
!state.error.isHandled &&
|
||||
proxyHandlesError(state.error.data.status)
|
||||
) {
|
||||
return (
|
||||
<SaveDashboardErrorProxy
|
||||
error={state.error}
|
||||
|
@ -28,7 +28,7 @@ export const SaveDashboardErrorProxy = ({
|
||||
const { onDashboardSave } = useDashboardSave(dashboard);
|
||||
|
||||
useEffect(() => {
|
||||
if (error.data && isHandledError(error.data.status)) {
|
||||
if (error.data && proxyHandlesError(error.data.status)) {
|
||||
error.isHandled = true;
|
||||
}
|
||||
}, [error]);
|
||||
@ -109,7 +109,7 @@ const ConfirmPluginDashboardSaveModal = ({ onDismiss, dashboard }: SaveDashboard
|
||||
);
|
||||
};
|
||||
|
||||
const isHandledError = (errorStatus: string) => {
|
||||
export const proxyHandlesError = (errorStatus: string) => {
|
||||
switch (errorStatus) {
|
||||
case 'version-mismatch':
|
||||
case 'name-exists':
|
||||
|
@ -37,6 +37,7 @@ const renderAndSubmitForm = async (
|
||||
) => {
|
||||
render(
|
||||
<SaveDashboardAsForm
|
||||
isLoading={false}
|
||||
dashboard={dashboard as DashboardModel}
|
||||
onCancel={() => {}}
|
||||
onSuccess={() => {}}
|
||||
|
@ -40,7 +40,14 @@ export interface SaveDashboardAsFormProps extends SaveDashboardFormProps {
|
||||
isNew?: boolean;
|
||||
}
|
||||
|
||||
export const SaveDashboardAsForm = ({ dashboard, isNew, onSubmit, onCancel, onSuccess }: SaveDashboardAsFormProps) => {
|
||||
export const SaveDashboardAsForm = ({
|
||||
dashboard,
|
||||
isLoading,
|
||||
isNew,
|
||||
onSubmit,
|
||||
onCancel,
|
||||
onSuccess,
|
||||
}: SaveDashboardAsFormProps) => {
|
||||
const defaultValues: SaveDashboardAsFormDTO = {
|
||||
title: isNew ? dashboard.title : `${dashboard.title} Copy`,
|
||||
$folder: {
|
||||
@ -129,8 +136,8 @@ export const SaveDashboardAsForm = ({ dashboard, isNew, onSubmit, onCancel, onSu
|
||||
<Button type="button" variant="secondary" onClick={onCancel} fill="outline">
|
||||
Cancel
|
||||
</Button>
|
||||
<Button type="submit" aria-label="Save dashboard button">
|
||||
Save
|
||||
<Button disabled={isLoading} type="submit" aria-label="Save dashboard button">
|
||||
{isLoading ? 'Saving...' : 'Save'}
|
||||
</Button>
|
||||
</HorizontalGroup>
|
||||
</>
|
||||
|
@ -34,6 +34,7 @@ const prepareDashboardMock = (
|
||||
const renderAndSubmitForm = async (dashboard: DashboardModel, submitSpy: jest.Mock) => {
|
||||
render(
|
||||
<SaveDashboardForm
|
||||
isLoading={false}
|
||||
dashboard={dashboard}
|
||||
onCancel={() => {}}
|
||||
onSuccess={() => {}}
|
||||
@ -62,6 +63,7 @@ describe('SaveDashboardAsForm', () => {
|
||||
it('renders switches when variables or timerange', () => {
|
||||
render(
|
||||
<SaveDashboardForm
|
||||
isLoading={false}
|
||||
dashboard={prepareDashboardMock(true, true, jest.fn(), jest.fn())}
|
||||
onCancel={() => {}}
|
||||
onSuccess={() => {}}
|
||||
@ -124,6 +126,7 @@ describe('SaveDashboardAsForm', () => {
|
||||
it('renders saved message draft if it was filled before', () => {
|
||||
render(
|
||||
<SaveDashboardForm
|
||||
isLoading={false}
|
||||
dashboard={createDashboardModelFixture()}
|
||||
onCancel={() => {}}
|
||||
onSuccess={() => {}}
|
||||
|
@ -13,6 +13,7 @@ interface FormDTO {
|
||||
|
||||
export type SaveProps = {
|
||||
dashboard: DashboardModel; // original
|
||||
isLoading: boolean;
|
||||
saveModel: SaveDashboardData; // already cloned
|
||||
onCancel: () => void;
|
||||
onSuccess: () => void;
|
||||
@ -23,6 +24,7 @@ export type SaveProps = {
|
||||
|
||||
export const SaveDashboardForm = ({
|
||||
dashboard,
|
||||
isLoading,
|
||||
saveModel,
|
||||
options,
|
||||
onSubmit,
|
||||
@ -108,11 +110,11 @@ export const SaveDashboardForm = ({
|
||||
</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={!saveModel.hasChanges}
|
||||
disabled={!saveModel.hasChanges || isLoading}
|
||||
icon={saving ? 'fa fa-spinner' : undefined}
|
||||
aria-label={selectors.pages.SaveDashboardModal.save}
|
||||
>
|
||||
Save
|
||||
{isLoading ? 'Saving...' : 'Save'}
|
||||
</Button>
|
||||
{!saveModel.hasChanges && <div>No changes to save</div>}
|
||||
</Stack>
|
||||
|
@ -7,7 +7,7 @@ import { Button, ClipboardButton, HorizontalGroup, TextArea } from '@grafana/ui'
|
||||
|
||||
import { SaveDashboardFormProps } from '../types';
|
||||
|
||||
export const SaveProvisionedDashboardForm = ({ dashboard, onCancel }: SaveDashboardFormProps) => {
|
||||
export const SaveProvisionedDashboardForm = ({ dashboard, onCancel }: Omit<SaveDashboardFormProps, 'isLoading'>) => {
|
||||
const [dashboardJSON, setDashboardJson] = useState(() => {
|
||||
const clone = dashboard.getSaveModelClone();
|
||||
delete clone.id;
|
||||
|
@ -26,6 +26,7 @@ export interface SaveDashboardCommand {
|
||||
|
||||
export interface SaveDashboardFormProps {
|
||||
dashboard: DashboardModel;
|
||||
isLoading: boolean;
|
||||
onCancel: () => void;
|
||||
onSuccess: () => void;
|
||||
onSubmit?: (clone: DashboardModel, options: SaveDashboardOptions, dashboard: DashboardModel) => Promise<any>;
|
||||
|
Loading…
Reference in New Issue
Block a user