From 7d58ec3d4779f4821ecda98fa168b33db165a13f Mon Sep 17 00:00:00 2001 From: Marcus Andersson Date: Tue, 28 Apr 2020 10:06:27 +0200 Subject: [PATCH] AlertTab: some ui updates (#23971) * updated the alerting tab. * changed so we use a confirm button. * removed uncommeneted import. * Change to secondary buttons Co-Authored-By: Dominik Prokop * trying to fix issue with panel of undefined. * Fix prettier * Update public/app/features/alerting/AlertTab.tsx Co-authored-by: Dominik Prokop --- public/app/features/alerting/AlertTab.tsx | 184 +++++++++++------- public/app/features/alerting/StateHistory.tsx | 47 ++--- .../dashboard/panel_editor/EditorTabBody.tsx | 135 ------------- 3 files changed, 135 insertions(+), 231 deletions(-) delete mode 100644 public/app/features/dashboard/panel_editor/EditorTabBody.tsx diff --git a/public/app/features/alerting/AlertTab.tsx b/public/app/features/alerting/AlertTab.tsx index 053d5f9bd70..fdfeb220fb9 100644 --- a/public/app/features/alerting/AlertTab.tsx +++ b/public/app/features/alerting/AlertTab.tsx @@ -1,14 +1,11 @@ import React, { PureComponent } from 'react'; import { connect, MapDispatchToProps, MapStateToProps } from 'react-redux'; import { css } from 'emotion'; -import { Alert, Button, IconName } from '@grafana/ui'; +import { Alert, Button, IconName, CustomScrollbar, Container, HorizontalGroup, ConfirmModal, Modal } from '@grafana/ui'; import { selectors } from '@grafana/e2e-selectors'; import { AngularComponent, getAngularLoader, getDataSourceSrv } from '@grafana/runtime'; - -import appEvents from 'app/core/app_events'; import { getAlertingValidationMessage } from './getAlertingValidationMessage'; -import { EditorTabBody, EditorToolbarView } from '../dashboard/panel_editor/EditorTabBody'; import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA'; import StateHistory from './StateHistory'; import 'app/features/alerting/AlertTabCtrl'; @@ -16,7 +13,7 @@ import 'app/features/alerting/AlertTabCtrl'; import { DashboardModel } from '../dashboard/state/DashboardModel'; import { PanelModel } from '../dashboard/state/PanelModel'; import { TestRuleResult } from './TestRuleResult'; -import { AppNotificationSeverity, CoreEvents, StoreState } from 'app/types'; +import { AppNotificationSeverity, StoreState } from 'app/types'; import { updateLocation } from 'app/core/actions'; import { PanelEditorTabId } from '../dashboard/components/PanelEditor/types'; @@ -37,6 +34,9 @@ export type Props = OwnProps & ConnectedProps & DispatchProps; interface State { validatonMessage: string; + showStateHistory: boolean; + showDeleteConfirmation: boolean; + showTestRule: boolean; } class UnConnectedAlertTab extends PureComponent { @@ -46,6 +46,9 @@ class UnConnectedAlertTab extends PureComponent { state: State = { validatonMessage: '', + showStateHistory: false, + showDeleteConfirmation: false, + showTestRule: false, }; componentDidMount() { @@ -103,57 +106,6 @@ class UnConnectedAlertTab extends PureComponent { } } - stateHistory = (): EditorToolbarView => { - const { panel, dashboard } = this.props; - - return { - title: 'State history', - render: () => { - return ( - - ); - }, - }; - }; - - deleteAlert = (): EditorToolbarView => { - const { panel } = this.props; - return { - title: 'Delete', - btnType: 'danger', - onClick: () => { - appEvents.emit(CoreEvents.showConfirmModal, { - title: 'Delete Alert', - text: 'Are you sure you want to delete this alert rule?', - text2: 'You need to save dashboard for the delete to take effect', - icon: 'trash-alt', - yesText: 'Delete', - onConfirm: () => { - delete panel.alert; - panel.thresholds = []; - this.panelCtrl.alertState = null; - this.panelCtrl.render(); - this.forceUpdate(); - }, - }); - }, - }; - }; - - renderTestRuleResult = () => { - const { dashboard, panel } = this.props; - return ; - }; - - testRule = (): EditorToolbarView => ({ - title: 'Test Rule', - render: () => this.renderTestRuleResult(), - }); - onAddAlert = () => { this.panelCtrl._enableAlert(); this.component.digest(); @@ -165,6 +117,11 @@ class UnConnectedAlertTab extends PureComponent { updateLocation({ query: { tab: PanelEditorTabId.Query }, partial: true }); }; + onToggleModal = (prop: keyof Omit) => { + const value = this.state[prop]; + this.setState({ ...this.state, [prop]: !value }); + }; + renderValidationMessage = () => { const { validatonMessage } = this.state; @@ -186,6 +143,74 @@ class UnConnectedAlertTab extends PureComponent { ); }; + renderTestRule = () => { + if (!this.state.showTestRule) { + return null; + } + + const { panel, dashboard } = this.props; + const onDismiss = () => this.onToggleModal('showTestRule'); + + return ( + + + + ); + }; + + renderDeleteConfirmation = () => { + if (!this.state.showDeleteConfirmation) { + return null; + } + + const { panel } = this.props; + const onDismiss = () => this.onToggleModal('showDeleteConfirmation'); + + return ( + + Are you sure you want to delete this alert rule? +
+ You need to save dashboard for the delete to take effect. + + } + confirmText="Delete Alert" + onDismiss={onDismiss} + onConfirm={() => { + delete panel.alert; + panel.thresholds = []; + this.panelCtrl.alertState = null; + this.panelCtrl.render(); + this.component.digest(); + onDismiss(); + }} + /> + ); + }; + + renderStateHistory = () => { + if (!this.state.showStateHistory) { + return null; + } + + const { panel, dashboard } = this.props; + const onDismiss = () => this.onToggleModal('showStateHistory'); + + return ( + + this.panelCtrl.refresh()} + /> + + ); + }; + render() { const { alert, transformations } = this.props.panel; const { validatonMessage } = this.state; @@ -195,8 +220,6 @@ class UnConnectedAlertTab extends PureComponent { return this.renderValidationMessage(); } - const toolbarItems = alert ? [this.stateHistory(), this.testRule(), this.deleteAlert()] : []; - const model = { title: 'Panel has no alert rule defined', buttonIcon: 'bell' as IconName, @@ -205,19 +228,40 @@ class UnConnectedAlertTab extends PureComponent { }; return ( - -
- {alert && hasTransformations && ( - - )} + <> + + +
+ {alert && hasTransformations && ( + + )} -
(this.element = element)} /> - {!alert && !validatonMessage && } -
- +
(this.element = element)} /> + {alert && ( + + + + + + )} + {!alert && !validatonMessage && } +
+ + + + {this.renderTestRule()} + {this.renderDeleteConfirmation()} + {this.renderStateHistory()} + ); } } diff --git a/public/app/features/alerting/StateHistory.tsx b/public/app/features/alerting/StateHistory.tsx index 2aefa27a47d..ddc02f1deb5 100644 --- a/public/app/features/alerting/StateHistory.tsx +++ b/public/app/features/alerting/StateHistory.tsx @@ -1,11 +1,10 @@ import React, { PureComponent } from 'react'; import { getBackendSrv } from '@grafana/runtime'; -import { Icon } from '@grafana/ui'; +import { Icon, ConfirmButton, Button } from '@grafana/ui'; import alertDef from './state/alertDef'; import { DashboardModel } from '../dashboard/state/DashboardModel'; -import appEvents from '../../core/app_events'; -import { CoreEvents } from 'app/types'; +import { css } from 'emotion'; interface Props { dashboard: DashboardModel; @@ -46,28 +45,16 @@ class StateHistory extends PureComponent { }); } - clearHistory = () => { - const { dashboard, onRefresh, panelId } = this.props; + clearHistory = async () => { + const { dashboard, panelId, onRefresh } = this.props; - appEvents.emit(CoreEvents.showConfirmModal, { - title: 'Delete Alert History', - text: 'Are you sure you want to remove all history & annotations for this alert?', - icon: 'trash-alt', - yesText: 'Yes', - onConfirm: () => { - getBackendSrv() - .post('/api/annotations/mass-delete', { - dashboardId: dashboard.id, - panelId: panelId, - }) - .then(() => { - this.setState({ - stateHistoryItems: [], - }); - onRefresh(); - }); - }, + await getBackendSrv().post('/api/annotations/mass-delete', { + dashboardId: dashboard.id, + panelId: panelId, }); + + this.setState({ stateHistoryItems: [] }); + onRefresh(); }; render() { @@ -78,9 +65,17 @@ class StateHistory extends PureComponent { {stateHistoryItems.length > 0 && (
Last 50 state changes - + + +
)}
    diff --git a/public/app/features/dashboard/panel_editor/EditorTabBody.tsx b/public/app/features/dashboard/panel_editor/EditorTabBody.tsx deleted file mode 100644 index ab9d2e6f94c..00000000000 --- a/public/app/features/dashboard/panel_editor/EditorTabBody.tsx +++ /dev/null @@ -1,135 +0,0 @@ -import React, { PureComponent } from 'react'; -import { CustomScrollbar, Icon, IconName, PanelOptionsGroup } from '@grafana/ui'; -import { selectors } from '@grafana/e2e-selectors'; - -import { FadeIn } from 'app/core/components/Animations/FadeIn'; - -interface Props { - children: JSX.Element; - renderToolbar?: () => JSX.Element; - toolbarItems?: EditorToolbarView[]; - scrollTop?: number; - setScrollTop?: (value: React.MouseEvent) => void; -} - -export interface EditorToolbarView { - title?: string; - heading?: string; - icon?: string; - disabled?: boolean; - onClick?: () => void; - render?: () => JSX.Element; - action?: () => void; - btnType?: 'danger'; -} - -interface State { - openView?: EditorToolbarView; - isOpen: boolean; - fadeIn: boolean; -} - -export class EditorTabBody extends PureComponent { - static defaultProps: Partial = { - toolbarItems: [], - }; - - constructor(props: Props) { - super(props); - - this.state = { - openView: null, - fadeIn: false, - isOpen: false, - }; - } - - componentDidMount() { - this.setState({ fadeIn: true }); - } - - onToggleToolBarView = (item: EditorToolbarView) => { - this.setState({ - openView: item, - isOpen: this.state.openView !== item || !this.state.isOpen, - }); - }; - - onCloseOpenView = () => { - this.setState({ isOpen: false }); - }; - - static getDerivedStateFromProps(props: Props, state: State) { - if (state.openView) { - const activeToolbarItem = props.toolbarItems.find( - (item: any) => item.title === state.openView.title && item.icon === state.openView.icon - ); - if (activeToolbarItem) { - return { - ...state, - openView: activeToolbarItem, - }; - } - } - return state; - } - - renderButton(view: EditorToolbarView) { - const onClick = () => { - if (view.onClick) { - view.onClick(); - } - - if (view.render) { - this.onToggleToolBarView(view); - } - }; - - return ( -
    - -
    - ); - } - - renderOpenView(view: EditorToolbarView) { - return ( - - {view.render()} - - ); - } - - render() { - const { children, renderToolbar, toolbarItems, scrollTop, setScrollTop } = this.props; - const { openView, fadeIn, isOpen } = this.state; - - return ( - <> -
    - {renderToolbar && renderToolbar()} - {toolbarItems.map(item => this.renderButton(item))} -
    -
    - -
    - - {openView && this.renderOpenView(openView)} - - - {children} - -
    -
    -
    - - ); - } -}