mirror of
https://github.com/grafana/grafana.git
synced 2025-02-11 16:15:42 -06:00
DashboardScene: Saving updates (provisioned dashboard and fixes) (#81471)
* Save provisioned dashboard * Update * fixes
This commit is contained in:
parent
02acbd795d
commit
715143d4ed
@ -29,7 +29,7 @@ export interface Props {
|
||||
export function SaveDashboardAsForm({ dashboard, drawer, changeInfo }: Props) {
|
||||
const { changedSaveModel } = changeInfo;
|
||||
|
||||
const { register, handleSubmit, setValue, formState, getValues } = useForm<SaveDashboardAsFormDTO>({
|
||||
const { register, handleSubmit, setValue, formState, getValues, watch } = useForm<SaveDashboardAsFormDTO>({
|
||||
mode: 'onBlur',
|
||||
defaultValues: {
|
||||
title: changeInfo.isNew ? changedSaveModel.title! : `${changedSaveModel.title} Copy`,
|
||||
@ -41,8 +41,9 @@ export function SaveDashboardAsForm({ dashboard, drawer, changeInfo }: Props) {
|
||||
copyTags: false,
|
||||
},
|
||||
});
|
||||
|
||||
const { errors, isValid, defaultValues } = formState;
|
||||
const formValues = getValues();
|
||||
const formValues = watch();
|
||||
|
||||
const { state, onSaveDashboard } = useDashboardSave(false);
|
||||
|
||||
|
@ -31,7 +31,7 @@ describe('SaveDashboardDrawer', () => {
|
||||
setup().openAndRender();
|
||||
|
||||
expect(await screen.findByText('Save dashboard')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Save current time range')).not.toBeInTheDocument();
|
||||
expect(screen.queryByLabelText(selectors.pages.SaveDashboardModal.saveTimerange)).not.toBeInTheDocument();
|
||||
expect(screen.getByText('No changes to save')).toBeInTheDocument();
|
||||
expect(screen.queryByLabelText('Tab Changes')).not.toBeInTheDocument();
|
||||
});
|
||||
@ -49,7 +49,7 @@ describe('SaveDashboardDrawer', () => {
|
||||
openAndRender();
|
||||
|
||||
expect(await screen.findByText('Save dashboard')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Save current time range')).toBeInTheDocument();
|
||||
expect(screen.queryByLabelText(selectors.pages.SaveDashboardModal.saveTimerange)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Should update diff when including time range is', async () => {
|
||||
@ -60,7 +60,7 @@ describe('SaveDashboardDrawer', () => {
|
||||
openAndRender();
|
||||
|
||||
expect(await screen.findByText('Save dashboard')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Save current time range')).toBeInTheDocument();
|
||||
expect(screen.queryByLabelText(selectors.pages.SaveDashboardModal.saveTimerange)).toBeInTheDocument();
|
||||
expect(screen.queryByLabelText('Tab Changes')).not.toBeInTheDocument();
|
||||
|
||||
await userEvent.click(screen.getByLabelText(selectors.pages.SaveDashboardModal.saveTimerange));
|
||||
|
@ -8,6 +8,7 @@ import { DashboardScene } from '../scene/DashboardScene';
|
||||
|
||||
import { SaveDashboardAsForm } from './SaveDashboardAsForm';
|
||||
import { SaveDashboardForm } from './SaveDashboardForm';
|
||||
import { SaveProvisionedDashboardForm } from './SaveProvisionedDashboardForm';
|
||||
import { getSaveDashboardChange } from './getSaveDashboardChange';
|
||||
|
||||
interface SaveDashboardDrawerState extends SceneObjectState {
|
||||
@ -36,6 +37,7 @@ export class SaveDashboardDrawer extends SceneObjectBase<SaveDashboardDrawerStat
|
||||
const changeInfo = getSaveDashboardChange(model.state.dashboardRef.resolve(), saveTimeRange, saveVariables);
|
||||
const { changedSaveModel, initialSaveModel, diffs, diffCount } = changeInfo;
|
||||
const dashboard = model.state.dashboardRef.resolve();
|
||||
const isProvisioned = dashboard.state.meta.provisioned;
|
||||
|
||||
const tabs = (
|
||||
<TabsBar>
|
||||
@ -54,12 +56,10 @@ export class SaveDashboardDrawer extends SceneObjectBase<SaveDashboardDrawerStat
|
||||
let title = 'Save dashboard';
|
||||
if (saveAsCopy) {
|
||||
title = 'Save dashboard copy';
|
||||
} else if (isProvisioned) {
|
||||
title = 'Provisioned dashboard';
|
||||
}
|
||||
|
||||
// else if (isProvisioned) {
|
||||
// title = 'Provisioned dashboard';
|
||||
// }
|
||||
|
||||
const renderBody = () => {
|
||||
if (showDiff) {
|
||||
return <SaveDashboardDiff diff={diffs} oldValue={initialSaveModel} newValue={changedSaveModel} />;
|
||||
@ -69,6 +69,10 @@ export class SaveDashboardDrawer extends SceneObjectBase<SaveDashboardDrawerStat
|
||||
return <SaveDashboardAsForm dashboard={dashboard} changeInfo={changeInfo} drawer={model} />;
|
||||
}
|
||||
|
||||
if (isProvisioned) {
|
||||
return <SaveProvisionedDashboardForm dashboard={dashboard} changeInfo={changeInfo} drawer={model} />;
|
||||
}
|
||||
|
||||
return <SaveDashboardForm dashboard={dashboard} changeInfo={changeInfo} drawer={model} />;
|
||||
};
|
||||
|
||||
|
@ -24,8 +24,7 @@ export interface Props {
|
||||
}
|
||||
|
||||
export function SaveDashboardForm({ dashboard, drawer, changeInfo }: Props) {
|
||||
const { saveVariables = false, saveTimeRange = false } = drawer.useState();
|
||||
const { changedSaveModel, hasChanges, hasTimeChanges, hasVariableValueChanges } = changeInfo;
|
||||
const { changedSaveModel, hasChanges } = changeInfo;
|
||||
|
||||
const { state, onSaveDashboard } = useDashboardSave(false);
|
||||
const [options, setOptions] = useState<SaveDashboardOptions>({
|
||||
@ -102,29 +101,9 @@ export function SaveDashboardForm({ dashboard, drawer, changeInfo }: Props) {
|
||||
|
||||
return (
|
||||
<Stack gap={0} direction="column">
|
||||
{hasTimeChanges && (
|
||||
<Field label="Save current time range" description="Will make current time range the new default">
|
||||
<Checkbox
|
||||
id="save-timerange"
|
||||
checked={saveTimeRange}
|
||||
onChange={drawer.onToggleSaveTimeRange}
|
||||
aria-label={selectors.pages.SaveDashboardModal.saveTimerange}
|
||||
/>
|
||||
</Field>
|
||||
)}
|
||||
{hasVariableValueChanges && (
|
||||
<Field label="Save current variable values" description="Will make the current values the new default">
|
||||
<Checkbox
|
||||
id="save-variables"
|
||||
checked={saveVariables}
|
||||
onChange={drawer.onToggleSaveVariables}
|
||||
aria-label={selectors.pages.SaveDashboardModal.saveVariables}
|
||||
/>
|
||||
</Field>
|
||||
)}
|
||||
<SaveDashboardFormCommonOptions drawer={drawer} changeInfo={changeInfo} />
|
||||
<Field label="Message">
|
||||
{/* config.featureToggles.dashgpt * TOOD GenAIDashboardChangesButton */}
|
||||
|
||||
<TextArea
|
||||
aria-label="message"
|
||||
value={options.message ?? ''}
|
||||
@ -143,3 +122,38 @@ export function SaveDashboardForm({ dashboard, drawer, changeInfo }: Props) {
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
export interface SaveDashboardFormCommonOptionsProps {
|
||||
drawer: SaveDashboardDrawer;
|
||||
changeInfo: DashboardChangeInfo;
|
||||
}
|
||||
|
||||
export function SaveDashboardFormCommonOptions({ drawer, changeInfo }: SaveDashboardFormCommonOptionsProps) {
|
||||
const { saveVariables = false, saveTimeRange = false } = drawer.useState();
|
||||
const { hasTimeChanges, hasVariableValueChanges } = changeInfo;
|
||||
|
||||
return (
|
||||
<>
|
||||
{hasTimeChanges && (
|
||||
<Field label="Update default time range" description="Will make current time range the new default">
|
||||
<Checkbox
|
||||
id="save-timerange"
|
||||
checked={saveTimeRange}
|
||||
onChange={drawer.onToggleSaveTimeRange}
|
||||
aria-label={selectors.pages.SaveDashboardModal.saveTimerange}
|
||||
/>
|
||||
</Field>
|
||||
)}
|
||||
{hasVariableValueChanges && (
|
||||
<Field label="Update default variable values" description="Will make the current values the new default">
|
||||
<Checkbox
|
||||
id="save-variables"
|
||||
checked={saveVariables}
|
||||
onChange={drawer.onToggleSaveVariables}
|
||||
aria-label={selectors.pages.SaveDashboardModal.saveVariables}
|
||||
/>
|
||||
</Field>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -0,0 +1,97 @@
|
||||
import { css } from '@emotion/css';
|
||||
import { saveAs } from 'file-saver';
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import AutoSizer from 'react-virtualized-auto-sizer';
|
||||
|
||||
import { Button, ClipboardButton, Stack, CodeEditor, Box } from '@grafana/ui';
|
||||
|
||||
import { DashboardScene } from '../scene/DashboardScene';
|
||||
|
||||
import { SaveDashboardDrawer } from './SaveDashboardDrawer';
|
||||
import { SaveDashboardFormCommonOptions } from './SaveDashboardForm';
|
||||
import { DashboardChangeInfo } from './shared';
|
||||
|
||||
export interface Props {
|
||||
dashboard: DashboardScene;
|
||||
drawer: SaveDashboardDrawer;
|
||||
changeInfo: DashboardChangeInfo;
|
||||
}
|
||||
|
||||
export function SaveProvisionedDashboardForm({ dashboard, drawer, changeInfo }: Props) {
|
||||
const dashboardJSON = useMemo(() => JSON.stringify(changeInfo.changedSaveModel, null, 2), [changeInfo]);
|
||||
|
||||
const saveToFile = useCallback(() => {
|
||||
const blob = new Blob([dashboardJSON], {
|
||||
type: 'application/json;charset=utf-8',
|
||||
});
|
||||
saveAs(blob, changeInfo.changedSaveModel.title + '-' + new Date().getTime() + '.json');
|
||||
}, [changeInfo.changedSaveModel, dashboardJSON]);
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<Stack direction="column" gap={2} grow={1}>
|
||||
<div>
|
||||
This dashboard cannot be saved from the Grafana UI because it has been provisioned from another source. Copy
|
||||
the JSON or save it to a file below, then you can update your dashboard in the provisioning source.
|
||||
<br />
|
||||
<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 /> <br />
|
||||
<strong>File path: </strong> {dashboard.state.meta.provisionedExternalId}
|
||||
</div>
|
||||
<Stack direction="column" gap={0}>
|
||||
<SaveDashboardFormCommonOptions drawer={drawer} changeInfo={changeInfo} />
|
||||
</Stack>
|
||||
<div className={styles.json}>
|
||||
<AutoSizer disableWidth>
|
||||
{({ height }) => (
|
||||
<CodeEditor
|
||||
width="100%"
|
||||
height={height}
|
||||
language="json"
|
||||
showLineNumbers={true}
|
||||
showMiniMap={dashboardJSON.length > 100}
|
||||
value={dashboardJSON}
|
||||
readOnly={true}
|
||||
/>
|
||||
)}
|
||||
</AutoSizer>
|
||||
</div>
|
||||
<Box paddingTop={2}>
|
||||
<Stack gap={2}>
|
||||
<Button variant="secondary" onClick={drawer.onClose} fill="outline">
|
||||
Cancel
|
||||
</Button>
|
||||
<ClipboardButton icon="copy" getText={() => dashboardJSON}>
|
||||
Copy JSON to clipboard
|
||||
</ClipboardButton>
|
||||
<Button type="submit" onClick={saveToFile}>
|
||||
Save JSON to file
|
||||
</Button>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Stack>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = {
|
||||
container: css({
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
}),
|
||||
json: css({
|
||||
flexGrow: 1,
|
||||
maxHeight: '800px',
|
||||
}),
|
||||
};
|
@ -185,7 +185,7 @@ exports[`transformSceneToSaveModel Given a scene with rows Should transform back
|
||||
"type": "row",
|
||||
},
|
||||
],
|
||||
"schemaVersion": 36,
|
||||
"schemaVersion": 39,
|
||||
"tags": [
|
||||
"templating",
|
||||
"gdev",
|
||||
@ -479,7 +479,7 @@ exports[`transformSceneToSaveModel Given a simple scene with custom settings Sho
|
||||
"type": "text",
|
||||
},
|
||||
],
|
||||
"schemaVersion": 36,
|
||||
"schemaVersion": 39,
|
||||
"tags": [
|
||||
"tag1",
|
||||
"tag2",
|
||||
@ -796,7 +796,7 @@ exports[`transformSceneToSaveModel Given a simple scene with variables Should tr
|
||||
"type": "text",
|
||||
},
|
||||
],
|
||||
"schemaVersion": 36,
|
||||
"schemaVersion": 39,
|
||||
"tags": [
|
||||
"gdev",
|
||||
"graph-ng",
|
||||
|
@ -28,6 +28,7 @@ import {
|
||||
} from '@grafana/schema';
|
||||
import { sortedDeepCloneWithoutNulls } from 'app/core/utils/object';
|
||||
import { getPanelDataFrames } from 'app/features/dashboard/components/HelpWizard/utils';
|
||||
import { DASHBOARD_SCHEMA_VERSION } from 'app/features/dashboard/state/DashboardMigrator';
|
||||
import { GrafanaQueryType } from 'app/plugins/datasource/grafana/types';
|
||||
|
||||
import { DashboardControls } from '../scene/DashboardControls';
|
||||
@ -141,6 +142,7 @@ export function transformSceneToSaveModel(scene: DashboardScene, isSnapshot = fa
|
||||
tags: state.tags,
|
||||
links: state.links,
|
||||
graphTooltip,
|
||||
schemaVersion: DASHBOARD_SCHEMA_VERSION,
|
||||
};
|
||||
|
||||
return sortedDeepCloneWithoutNulls(dashboard);
|
||||
|
Loading…
Reference in New Issue
Block a user