From 9ac7263e66968df8b570ba766d735ef3f722c2d6 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Mon, 27 Apr 2020 08:07:56 +0200 Subject: [PATCH] DashboardSave: fix save dashboard when changes are detected (#23909) --- .../components/ConfirmModal/ConfirmModal.tsx | 30 ++++----- .../src/components/Layout/Layout.tsx | 9 ++- .../SaveDashboard/SaveDashboardButton.tsx | 8 +-- .../SaveDashboard/SaveDashboardModal.tsx | 1 + .../SaveDashboard/UnsavedChangesModal.tsx | 50 ++++++++++++++ .../UnsavedChangesModalCtrl.ts | 65 ------------------- .../components/UnsavedChangesModal/index.ts | 1 - public/app/features/dashboard/index.ts | 1 - .../dashboard/services/ChangeTracker.ts | 30 ++++++--- 9 files changed, 94 insertions(+), 101 deletions(-) create mode 100644 public/app/features/dashboard/components/SaveDashboard/UnsavedChangesModal.tsx delete mode 100644 public/app/features/dashboard/components/UnsavedChangesModal/UnsavedChangesModalCtrl.ts delete mode 100644 public/app/features/dashboard/components/UnsavedChangesModal/index.ts diff --git a/packages/grafana-ui/src/components/ConfirmModal/ConfirmModal.tsx b/packages/grafana-ui/src/components/ConfirmModal/ConfirmModal.tsx index 0992d805ed1..be841a841ae 100644 --- a/packages/grafana-ui/src/components/ConfirmModal/ConfirmModal.tsx +++ b/packages/grafana-ui/src/components/ConfirmModal/ConfirmModal.tsx @@ -7,21 +7,6 @@ import { stylesFactory, ThemeContext } from '../../themes'; import { GrafanaTheme } from '@grafana/data'; import { HorizontalGroup } from '..'; -const getStyles = stylesFactory((theme: GrafanaTheme) => ({ - modal: css` - width: 500px; - `, - modalContent: css` - text-align: center; - `, - modalText: css` - font-size: ${theme.typography.heading.h4}; - color: ${theme.colors.link}; - margin-bottom: calc(${theme.spacing.d} * 2); - padding-top: ${theme.spacing.d}; - `, -})); - const defaultIcon: IconName = 'exclamation-triangle'; interface Props { @@ -64,3 +49,18 @@ export const ConfirmModal: FC = ({ ); }; + +const getStyles = stylesFactory((theme: GrafanaTheme) => ({ + modal: css` + width: 500px; + `, + modalContent: css` + text-align: center; + `, + modalText: css` + font-size: ${theme.typography.heading.h4}; + color: ${theme.colors.link}; + margin-bottom: calc(${theme.spacing.d} * 2); + padding-top: ${theme.spacing.d}; + `, +})); diff --git a/packages/grafana-ui/src/components/Layout/Layout.tsx b/packages/grafana-ui/src/components/Layout/Layout.tsx index d0c6448eba4..7f2b2ba97e4 100644 --- a/packages/grafana-ui/src/components/Layout/Layout.tsx +++ b/packages/grafana-ui/src/components/Layout/Layout.tsx @@ -76,9 +76,10 @@ export const VerticalGroup: React.FC> children, spacing, justify, + align, width, }) => ( - + {children} ); @@ -92,7 +93,11 @@ export const Container: React.FC = ({ children, padding, margin const getStyles = stylesFactory( (theme: GrafanaTheme, orientation: Orientation, spacing: Spacing, justify: Justify, align, wrap) => { const finalSpacing = spacing !== 'none' ? theme.spacing[spacing] : 0; - const marginCompensation = orientation === Orientation.Horizontal && !wrap ? 0 : `-${finalSpacing}`; + // compensate for last row margin when wrapped, horizontal layout + const marginCompensation = + (orientation === Orientation.Horizontal && !wrap) || orientation === Orientation.Vertical + ? 0 + : `-${finalSpacing}`; return { layout: css` diff --git a/public/app/features/dashboard/components/SaveDashboard/SaveDashboardButton.tsx b/public/app/features/dashboard/components/SaveDashboard/SaveDashboardButton.tsx index a74c64e1c00..da7bf2bae57 100644 --- a/public/app/features/dashboard/components/SaveDashboard/SaveDashboardButton.tsx +++ b/public/app/features/dashboard/components/SaveDashboard/SaveDashboardButton.tsx @@ -14,15 +14,9 @@ interface SaveDashboardButtonProps { */ getDashboard?: () => DashboardModel; onSaveSuccess?: () => void; - useNewForms?: boolean; } -export const SaveDashboardButton: React.FC = ({ - dashboard, - onSaveSuccess, - getDashboard, - useNewForms, -}) => { +export const SaveDashboardButton: React.FC = ({ dashboard, onSaveSuccess, getDashboard }) => { return ( {({ showModal, hideModal }) => { diff --git a/public/app/features/dashboard/components/SaveDashboard/SaveDashboardModal.tsx b/public/app/features/dashboard/components/SaveDashboard/SaveDashboardModal.tsx index b48f056c532..b1f9fa659de 100644 --- a/public/app/features/dashboard/components/SaveDashboard/SaveDashboardModal.tsx +++ b/public/app/features/dashboard/components/SaveDashboard/SaveDashboardModal.tsx @@ -9,6 +9,7 @@ import { SaveDashboardModalProps } from './types'; export const SaveDashboardModal: React.FC = ({ dashboard, onDismiss, onSaveSuccess }) => { const { state, onDashboardSave } = useDashboardSave(dashboard); const [dashboardSaveModelClone, setDashboardSaveModelClone] = useState(); + return ( <> {state.error && ( diff --git a/public/app/features/dashboard/components/SaveDashboard/UnsavedChangesModal.tsx b/public/app/features/dashboard/components/SaveDashboard/UnsavedChangesModal.tsx new file mode 100644 index 00000000000..e1efcba4a40 --- /dev/null +++ b/public/app/features/dashboard/components/SaveDashboard/UnsavedChangesModal.tsx @@ -0,0 +1,50 @@ +import React from 'react'; +import { Button, HorizontalGroup, Modal, VerticalGroup } from '@grafana/ui'; +import { SaveDashboardButton } from './SaveDashboardButton'; +import { DashboardModel } from '../../state'; +import { css } from 'emotion'; + +interface UnsavedChangesModalProps { + dashboard: DashboardModel; + onDiscard: () => void; + onDismiss: () => void; + onSaveSuccess?: () => void; +} + +export const UnsavedChangesModal: React.FC = ({ + dashboard, + onSaveSuccess, + onDiscard, + onDismiss, +}) => { + return ( + + +

Do you want to save your changes?

+ + + + + +
+
+ ); +}; diff --git a/public/app/features/dashboard/components/UnsavedChangesModal/UnsavedChangesModalCtrl.ts b/public/app/features/dashboard/components/UnsavedChangesModal/UnsavedChangesModalCtrl.ts deleted file mode 100644 index 95ff8544395..00000000000 --- a/public/app/features/dashboard/components/UnsavedChangesModal/UnsavedChangesModalCtrl.ts +++ /dev/null @@ -1,65 +0,0 @@ -import coreModule from 'app/core/core_module'; - -const template = ` - -`; - -export class UnsavedChangesModalCtrl { - clone: any; - dismiss: () => void; - - /** @ngInject */ - constructor(private unsavedChangesSrv: any) {} - - discard() { - this.dismiss(); - this.unsavedChangesSrv.tracker.discardChanges(); - } - - save() { - this.dismiss(); - this.unsavedChangesSrv.tracker.saveChanges(); - } - - onSaveSuccess = () => { - this.dismiss(); - this.unsavedChangesSrv.tracker.onSaveSuccess(); - }; -} - -export function unsavedChangesModalDirective() { - return { - restrict: 'E', - template: template, - controller: UnsavedChangesModalCtrl, - bindToController: true, - controllerAs: 'ctrl', - scope: { dismiss: '&' }, - }; -} - -coreModule.directive('unsavedChangesModal', unsavedChangesModalDirective); diff --git a/public/app/features/dashboard/components/UnsavedChangesModal/index.ts b/public/app/features/dashboard/components/UnsavedChangesModal/index.ts deleted file mode 100644 index 43943f06694..00000000000 --- a/public/app/features/dashboard/components/UnsavedChangesModal/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { UnsavedChangesModalCtrl } from './UnsavedChangesModalCtrl'; diff --git a/public/app/features/dashboard/index.ts b/public/app/features/dashboard/index.ts index 7d043e70b0e..1f77e11b9bc 100644 --- a/public/app/features/dashboard/index.ts +++ b/public/app/features/dashboard/index.ts @@ -10,7 +10,6 @@ import './components/DashNav'; import './components/VersionHistory'; import './components/DashboardSettings'; import './components/SubMenu'; -import './components/UnsavedChangesModal'; import './components/AdHocFilters'; import './components/RowOptions'; diff --git a/public/app/features/dashboard/services/ChangeTracker.ts b/public/app/features/dashboard/services/ChangeTracker.ts index ce68bb0f578..16d1832355b 100644 --- a/public/app/features/dashboard/services/ChangeTracker.ts +++ b/public/app/features/dashboard/services/ChangeTracker.ts @@ -5,6 +5,8 @@ import { ContextSrv } from 'app/core/services/context_srv'; import { GrafanaRootScope } from 'app/routes/GrafanaCtrl'; import { AppEventConsumer, CoreEvents } from 'app/types'; import { appEvents } from 'app/core/app_events'; +import { UnsavedChangesModal } from '../components/SaveDashboard/UnsavedChangesModal'; +import { getLocationSrv } from '@grafana/runtime'; export class ChangeTracker { current: any; @@ -159,17 +161,23 @@ export class ChangeTracker { return currentJson !== originalJson; } - discardChanges() { + discardChanges = () => { this.original = null; this.gotoNext(); - } + }; - open_modal() { - this.$rootScope.appEvent(CoreEvents.showModal, { - templateHtml: '', - modalClass: 'modal--narrow confirm-modal', + open_modal = () => { + this.$rootScope.appEvent(CoreEvents.showModalReact, { + component: UnsavedChangesModal, + props: { + dashboard: this.current, + onSaveSuccess: this.onSaveSuccess, + onDiscard: () => { + this.discardChanges(); + }, + }, }); - } + }; onSaveSuccess = () => { this.$timeout(() => { @@ -177,9 +185,11 @@ export class ChangeTracker { }); }; - gotoNext() { + gotoNext = () => { const baseLen = this.$location.absUrl().length - this.$location.url().length; const nextUrl = this.next.substring(baseLen); - this.$location.url(nextUrl); - } + getLocationSrv().update({ + path: nextUrl, + }); + }; }